From 1ef8eabcabe6fce68a2a7dc0811c2d4cf7a60209 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Wed, 1 Nov 2023 21:51:38 -0400 Subject: [PATCH 001/185] fix controller getting disabled (#354) --- fastplotlib/graphics/selectors/_base_selector.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fastplotlib/graphics/selectors/_base_selector.py b/fastplotlib/graphics/selectors/_base_selector.py index e892ca32d..e6796f270 100644 --- a/fastplotlib/graphics/selectors/_base_selector.py +++ b/fastplotlib/graphics/selectors/_base_selector.py @@ -252,7 +252,8 @@ def _move_end(self, ev): # restore the initial controller state # if it was disabled, keep it disabled - self._plot_area.controller.enabled = self._initial_controller_state + if self._initial_controller_state is not None: + self._plot_area.controller.enabled = self._initial_controller_state def _move_to_pointer(self, ev): """ From 430f513e42d587813029bee924cf8320d79415b7 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Wed, 1 Nov 2023 22:31:22 -0400 Subject: [PATCH 002/185] fix normalize_min_max (#353) * fix normalize_min_max * document utils.get_cmap() --- fastplotlib/utils/functions.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/fastplotlib/utils/functions.py b/fastplotlib/utils/functions.py index 3013559d5..8d1e8694f 100644 --- a/fastplotlib/utils/functions.py +++ b/fastplotlib/utils/functions.py @@ -26,6 +26,23 @@ def get_cmap(name: str, alpha: float = 1.0) -> np.ndarray: + """ + Get a colormap as numpy array + + Parameters + ---------- + name: str + name of colormap + alpha: float + alpha, 0.0 - 1.0 + + Returns + ------- + np.ndarray + [n_colors, 4], i.e. [n_colors, RGBA] + + """ + cmap_path = Path(__file__).absolute().parent.joinpath("colormaps", name) if cmap_path.is_file(): cmap = np.loadtxt(cmap_path) @@ -214,6 +231,9 @@ def calculate_gridshape(n_subplots: int) -> Tuple[int, int]: def normalize_min_max(a): """normalize an array between 0 - 1""" + if np.unique(a).size == 1: + return np.zeros(a.size) + return (a - np.min(a)) / (np.max(a - np.min(a))) From c1cf47f152ca67ce6bc142c164733002a1dc8aaf Mon Sep 17 00:00:00 2001 From: Caitlin Lewis <69729525+clewis7@users.noreply.github.com> Date: Fri, 3 Nov 2023 00:52:00 -0400 Subject: [PATCH 003/185] make hlut widget optional (#351) * make hlut widget optional * Apply suggestions from code review --- fastplotlib/widgets/image.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/fastplotlib/widgets/image.py b/fastplotlib/widgets/image.py index a9ebfafb4..1ecbf50fd 100644 --- a/fastplotlib/widgets/image.py +++ b/fastplotlib/widgets/image.py @@ -225,6 +225,7 @@ def __init__( grid_shape: Tuple[int, int] = None, names: List[str] = None, grid_plot_kwargs: dict = None, + histogram_widget: bool = True, **kwargs, ): """ @@ -288,6 +289,9 @@ def __init__( names: Optional[str] gives names to the subplots + histogram_widget: bool, default False + make histogram LUT widget for each subplot + kwargs: Any passed to fastplotlib.graphics.Image @@ -556,16 +560,17 @@ def __init__( subplot.name = name subplot.set_title(name) - hlut = HistogramLUT( - data=d, - image_graphic=ig, - name="histogram_lut" - ) + if histogram_widget: + hlut = HistogramLUT( + data=d, + image_graphic=ig, + name="histogram_lut" + ) - subplot.docks["right"].add_graphic(hlut) - subplot.docks["right"].size = 80 - subplot.docks["right"].auto_scale(maintain_aspect=False) - subplot.docks["right"].controller.enabled = False + subplot.docks["right"].add_graphic(hlut) + subplot.docks["right"].size = 80 + subplot.docks["right"].auto_scale(maintain_aspect=False) + subplot.docks["right"].controller.enabled = False self.block_sliders = False self._image_widget_toolbar = None From 092fe7cfcb680ac541c08edced309eca77454df0 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Fri, 3 Nov 2023 03:12:59 -0400 Subject: [PATCH 004/185] bugfixes and more tests (#358) * fix heatmap cmap and vmin vmax, add heatmap tests * add heatmap to tests glob * remove old commented code * Plot.auto_scale() skipped if scene is empty * add another gridplot test * a comma * smaller heatmap test image, github actions cannot handle 10k x 20k * dims * add heatmap screenshots * add gridplot non square screenshot * update CI to use runner with more RAM * update API docs * bigmem runner for all jobs * use label in ci * Update ci.yml * Update ci.yml * Update ci.yml * use regular CI runners, will submit bug report to github --- docs/source/api/graphics/HeatmapGraphic.rst | 2 - docs/source/api/layouts/gridplot.rst | 3 ++ docs/source/api/layouts/plot.rst | 3 ++ docs/source/api/selectors/Synchronizer.rst | 1 + docs/source/api/widgets/ImageWidget.rst | 2 + .../desktop/gridplot/gridplot_non_square.py | 34 +++++++++++++++ examples/desktop/heatmap/__init__.py | 0 examples/desktop/heatmap/heatmap.py | 37 +++++++++++++++++ examples/desktop/heatmap/heatmap_cmap.py | 39 ++++++++++++++++++ examples/desktop/heatmap/heatmap_data.py | 41 +++++++++++++++++++ examples/desktop/heatmap/heatmap_vmin_vmax.py | 40 ++++++++++++++++++ .../screenshots/gridplot_non_square.png | 3 ++ examples/desktop/screenshots/heatmap.png | 3 ++ examples/desktop/screenshots/heatmap_cmap.png | 3 ++ examples/desktop/screenshots/heatmap_data.png | 3 ++ .../desktop/screenshots/heatmap_vmin_vmax.png | 3 ++ examples/tests/testutils.py | 1 + fastplotlib/graphics/_features/_colors.py | 23 ++++++++++- fastplotlib/graphics/image.py | 20 --------- fastplotlib/layouts/_plot_area.py | 4 +- fastplotlib/widgets/image.py | 3 -- 21 files changed, 241 insertions(+), 27 deletions(-) create mode 100644 examples/desktop/gridplot/gridplot_non_square.py create mode 100644 examples/desktop/heatmap/__init__.py create mode 100644 examples/desktop/heatmap/heatmap.py create mode 100644 examples/desktop/heatmap/heatmap_cmap.py create mode 100644 examples/desktop/heatmap/heatmap_data.py create mode 100644 examples/desktop/heatmap/heatmap_vmin_vmax.py create mode 100644 examples/desktop/screenshots/gridplot_non_square.png create mode 100644 examples/desktop/screenshots/heatmap.png create mode 100644 examples/desktop/screenshots/heatmap_cmap.png create mode 100644 examples/desktop/screenshots/heatmap_data.png create mode 100644 examples/desktop/screenshots/heatmap_vmin_vmax.png diff --git a/docs/source/api/graphics/HeatmapGraphic.rst b/docs/source/api/graphics/HeatmapGraphic.rst index 6da6f6531..3bd2f2baa 100644 --- a/docs/source/api/graphics/HeatmapGraphic.rst +++ b/docs/source/api/graphics/HeatmapGraphic.rst @@ -26,8 +26,6 @@ Properties HeatmapGraphic.position_y HeatmapGraphic.position_z HeatmapGraphic.visible - HeatmapGraphic.vmax - HeatmapGraphic.vmin HeatmapGraphic.world_object Methods diff --git a/docs/source/api/layouts/gridplot.rst b/docs/source/api/layouts/gridplot.rst index 63f1516cf..b5b03bfa4 100644 --- a/docs/source/api/layouts/gridplot.rst +++ b/docs/source/api/layouts/gridplot.rst @@ -22,6 +22,8 @@ Properties GridPlot.canvas GridPlot.renderer + GridPlot.toolbar + GridPlot.widget Methods ~~~~~~~ @@ -36,4 +38,5 @@ Methods GridPlot.remove_animation GridPlot.render GridPlot.show + GridPlot.start_render diff --git a/docs/source/api/layouts/plot.rst b/docs/source/api/layouts/plot.rst index a0be9287b..bd38720b4 100644 --- a/docs/source/api/layouts/plot.rst +++ b/docs/source/api/layouts/plot.rst @@ -31,7 +31,9 @@ Properties Plot.renderer Plot.scene Plot.selectors + Plot.toolbar Plot.viewport + Plot.widget Methods ~~~~~~~ @@ -67,4 +69,5 @@ Methods Plot.set_title Plot.set_viewport_rect Plot.show + Plot.start_render diff --git a/docs/source/api/selectors/Synchronizer.rst b/docs/source/api/selectors/Synchronizer.rst index d0fa0c2a8..2b28fe351 100644 --- a/docs/source/api/selectors/Synchronizer.rst +++ b/docs/source/api/selectors/Synchronizer.rst @@ -28,5 +28,6 @@ Methods :toctree: Synchronizer_api Synchronizer.add + Synchronizer.clear Synchronizer.remove diff --git a/docs/source/api/widgets/ImageWidget.rst b/docs/source/api/widgets/ImageWidget.rst index 4e779f20b..08bce8d7a 100644 --- a/docs/source/api/widgets/ImageWidget.rst +++ b/docs/source/api/widgets/ImageWidget.rst @@ -29,6 +29,7 @@ Properties ImageWidget.ndim ImageWidget.slider_dims ImageWidget.sliders + ImageWidget.widget ImageWidget.window_funcs Methods @@ -38,6 +39,7 @@ Methods ImageWidget.close ImageWidget.reset_vmin_vmax + ImageWidget.reset_vmin_vmax_frame ImageWidget.set_data ImageWidget.show diff --git a/examples/desktop/gridplot/gridplot_non_square.py b/examples/desktop/gridplot/gridplot_non_square.py new file mode 100644 index 000000000..a41bcd9f4 --- /dev/null +++ b/examples/desktop/gridplot/gridplot_non_square.py @@ -0,0 +1,34 @@ +""" +GridPlot Simple +============ +Example showing simple 2x2 GridPlot with Standard images from imageio. +""" + +# test_example = true + +import fastplotlib as fpl +import imageio.v3 as iio + + +plot = fpl.GridPlot(shape=(2, 2), controllers="sync") +# to force a specific framework such as glfw: +# plot = fpl.GridPlot(canvas="glfw") + +im = iio.imread("imageio:clock.png") +im2 = iio.imread("imageio:astronaut.png") +im3 = iio.imread("imageio:coffee.png") + +plot[0, 0].add_image(data=im) +plot[0, 1].add_image(data=im2) +plot[1, 0].add_image(data=im3) + +plot.show() + +plot.canvas.set_logical_size(800, 800) + +for subplot in plot: + subplot.auto_scale() + +if __name__ == "__main__": + print(__doc__) + fpl.run() diff --git a/examples/desktop/heatmap/__init__.py b/examples/desktop/heatmap/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/examples/desktop/heatmap/heatmap.py b/examples/desktop/heatmap/heatmap.py new file mode 100644 index 000000000..45c340cbd --- /dev/null +++ b/examples/desktop/heatmap/heatmap.py @@ -0,0 +1,37 @@ +""" +Simple Heatmap +============== +Example showing how to plot a heatmap +""" + +# test_example = true + +import fastplotlib as fpl +import numpy as np + +plot = fpl.Plot() +# to force a specific framework such as glfw: +# plot = fpl.Plot(canvas="glfw") + +xs = np.linspace(0, 1_000, 10_000) + +sine = np.sin(xs) +cosine = np.cos(xs) + +# alternating sines and cosines +data = np.zeros((10_000, 10_000), dtype=np.float32) +data[::2] = sine +data[1::2] = cosine + +# plot the image data +heatmap_graphic = plot.add_heatmap(data=data, name="heatmap") + +plot.show() + +plot.canvas.set_logical_size(1500, 1500) + +plot.auto_scale() + +if __name__ == "__main__": + print(__doc__) + fpl.run() diff --git a/examples/desktop/heatmap/heatmap_cmap.py b/examples/desktop/heatmap/heatmap_cmap.py new file mode 100644 index 000000000..afc67f5b8 --- /dev/null +++ b/examples/desktop/heatmap/heatmap_cmap.py @@ -0,0 +1,39 @@ +""" +Heatmap change cmap +=================== +Change the cmap of a heatmap +""" + +# test_example = true + +import fastplotlib as fpl +import numpy as np + +plot = fpl.Plot() +# to force a specific framework such as glfw: +# plot = fpl.Plot(canvas="glfw") + +xs = np.linspace(0, 1_000, 10_000) + +sine = np.sin(xs) +cosine = np.cos(xs) + +# alternating sines and cosines +data = np.zeros((10_000, 10_000), dtype=np.float32) +data[::2] = sine +data[1::2] = cosine + +# plot the image data +heatmap_graphic = plot.add_heatmap(data=data, name="heatmap") + +plot.show() + +plot.canvas.set_logical_size(1500, 1500) + +plot.auto_scale() + +heatmap_graphic.cmap = "viridis" + +if __name__ == "__main__": + print(__doc__) + fpl.run() diff --git a/examples/desktop/heatmap/heatmap_data.py b/examples/desktop/heatmap/heatmap_data.py new file mode 100644 index 000000000..78e819ab8 --- /dev/null +++ b/examples/desktop/heatmap/heatmap_data.py @@ -0,0 +1,41 @@ +""" +Heatmap change data +=================== +Change the data of a heatmap +""" + +# test_example = true + +import fastplotlib as fpl +import numpy as np + +plot = fpl.Plot() +# to force a specific framework such as glfw: +# plot = fpl.Plot(canvas="glfw") + +xs = np.linspace(0, 1_000, 10_000) + +sine = np.sin(xs) +cosine = np.cos(xs) + +# alternating sines and cosines +data = np.zeros((10_000, 10_000), dtype=np.float32) +data[::2] = sine +data[1::2] = cosine + +# plot the image data +heatmap_graphic = plot.add_heatmap(data=data, name="heatmap") + +plot.show() + +plot.canvas.set_logical_size(1500, 1500) + +plot.auto_scale() + +heatmap_graphic.data[:5_000] = sine +heatmap_graphic.data[5_000:] = cosine + + +if __name__ == "__main__": + print(__doc__) + fpl.run() diff --git a/examples/desktop/heatmap/heatmap_vmin_vmax.py b/examples/desktop/heatmap/heatmap_vmin_vmax.py new file mode 100644 index 000000000..7aae1d6d3 --- /dev/null +++ b/examples/desktop/heatmap/heatmap_vmin_vmax.py @@ -0,0 +1,40 @@ +""" +Heatmap change vmin vmax +======================== +Change the vmin vmax of a heatmap +""" + +# test_example = true + +import fastplotlib as fpl +import numpy as np + +plot = fpl.Plot() +# to force a specific framework such as glfw: +# plot = fpl.Plot(canvas="glfw") + +xs = np.linspace(0, 1_000, 10_000) + +sine = np.sin(xs) +cosine = np.cos(xs) + +# alternating sines and cosines +data = np.zeros((10_000, 10_000), dtype=np.float32) +data[::2] = sine +data[1::2] = cosine + +# plot the image data +heatmap_graphic = plot.add_heatmap(data=data, name="heatmap") + +plot.show() + +plot.canvas.set_logical_size(1500, 1500) + +plot.auto_scale() + +heatmap_graphic.cmap.vmin = -0.5 +heatmap_graphic.cmap.vmax = 0.5 + +if __name__ == "__main__": + print(__doc__) + fpl.run() diff --git a/examples/desktop/screenshots/gridplot_non_square.png b/examples/desktop/screenshots/gridplot_non_square.png new file mode 100644 index 000000000..7b534aef9 --- /dev/null +++ b/examples/desktop/screenshots/gridplot_non_square.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:abf936904d1b5e2018a72311c510108925f2972dfdf59166580ad27876f9e2be +size 220140 diff --git a/examples/desktop/screenshots/heatmap.png b/examples/desktop/screenshots/heatmap.png new file mode 100644 index 000000000..d0df1510a --- /dev/null +++ b/examples/desktop/screenshots/heatmap.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6c19d6454e79d92074bac01175dfbb8506e882ea55b626c0b2357960ed6e294f +size 163655 diff --git a/examples/desktop/screenshots/heatmap_cmap.png b/examples/desktop/screenshots/heatmap_cmap.png new file mode 100644 index 000000000..db3038dee --- /dev/null +++ b/examples/desktop/screenshots/heatmap_cmap.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0328eec32f13042e3e2f243793317e180bba1353fe961604ecad3f38463b8809 +size 156419 diff --git a/examples/desktop/screenshots/heatmap_data.png b/examples/desktop/screenshots/heatmap_data.png new file mode 100644 index 000000000..96169ec77 --- /dev/null +++ b/examples/desktop/screenshots/heatmap_data.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4a376c24fa123088be69f807ec5212cb5ed5680b146ce9d62df584790c632845 +size 20838 diff --git a/examples/desktop/screenshots/heatmap_vmin_vmax.png b/examples/desktop/screenshots/heatmap_vmin_vmax.png new file mode 100644 index 000000000..2a809d545 --- /dev/null +++ b/examples/desktop/screenshots/heatmap_vmin_vmax.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3eb12ad590aa8260f2cf722abadf5b51bcb7d5a8d8d1cb05b7711b50331da07a +size 165937 diff --git a/examples/tests/testutils.py b/examples/tests/testutils.py index 6155f8763..5f6772fb7 100644 --- a/examples/tests/testutils.py +++ b/examples/tests/testutils.py @@ -16,6 +16,7 @@ # examples live in themed sub-folders example_globs = [ "image/*.py", + "heatmap/*.py", "scatter/*.py", "line/*.py", "line_collection/*.py", diff --git a/fastplotlib/graphics/_features/_colors.py b/fastplotlib/graphics/_features/_colors.py index 85014155d..b6723b34b 100644 --- a/fastplotlib/graphics/_features/_colors.py +++ b/fastplotlib/graphics/_features/_colors.py @@ -401,8 +401,29 @@ class HeatmapCmapFeature(ImageCmapFeature): """ def _set(self, cmap_name: str): + # in heatmap we use one material for all ImageTiles self._parent._material.map.data[:] = make_colors(256, cmap_name) self._parent._material.map.update_range((0, 0, 0), size=(256, 1, 1)) - self.name = cmap_name + self._name = cmap_name self._feature_changed(key=None, new_data=self.name) + + @property + def vmin(self) -> float: + """Minimum contrast limit.""" + return self._parent._material.clim[0] + + @vmin.setter + def vmin(self, value: float): + """Minimum contrast limit.""" + self._parent._material.clim = (value, self._parent._material.clim[1]) + + @property + def vmax(self) -> float: + """Maximum contrast limit.""" + return self._parent._material.clim[1] + + @vmax.setter + def vmax(self, value: float): + """Maximum contrast limit.""" + self._parent._material.clim = (self._parent._material.clim[0], value) \ No newline at end of file diff --git a/fastplotlib/graphics/image.py b/fastplotlib/graphics/image.py index 12ac9e41d..10f09eefb 100644 --- a/fastplotlib/graphics/image.py +++ b/fastplotlib/graphics/image.py @@ -480,26 +480,6 @@ def __init__( # set it with the actual data self.data = data - @property - def vmin(self) -> float: - """Minimum contrast limit.""" - return self._material.clim[0] - - @vmin.setter - def vmin(self, value: float): - """Minimum contrast limit.""" - self._material.clim = (value, self._material.clim[1]) - - @property - def vmax(self) -> float: - """Maximum contrast limit.""" - return self._material.clim[1] - - @vmax.setter - def vmax(self, value: float): - """Maximum contrast limit.""" - self._material.clim = (self._material.clim[0], value) - def set_feature(self, feature: str, new_data: Any, indices: Any): pass diff --git a/fastplotlib/layouts/_plot_area.py b/fastplotlib/layouts/_plot_area.py index 2060850c2..7590bad10 100644 --- a/fastplotlib/layouts/_plot_area.py +++ b/fastplotlib/layouts/_plot_area.py @@ -495,7 +495,9 @@ def auto_scale(self, maintain_aspect: bool = False, zoom: float = 0.8): zoom value for the camera after auto-scaling, if zoom = 1.0 then the graphics in the scene will fill the entire canvas. """ - # hacky workaround for now until we decided if we want to put selectors in their own scene + if not len(self.scene.children) > 0: + return + # hacky workaround for now until we decide if we want to put selectors in their own scene # remove all selectors from a scene to calculate scene bbox for selector in self.selectors: self.scene.remove(selector.world_object) diff --git a/fastplotlib/widgets/image.py b/fastplotlib/widgets/image.py index 1ecbf50fd..da256207f 100644 --- a/fastplotlib/widgets/image.py +++ b/fastplotlib/widgets/image.py @@ -874,9 +874,6 @@ def set_data( # force graphics to update self.current_index = self.current_index - # if reset_vmin_vmax: - # self.reset_vmin_vmax() - def show(self, toolbar: bool = True, sidecar: bool = False, sidecar_kwargs: dict = None): """ Show the widget From 9a81e7dfd86b17cc0790a7acc2dcd553e2415be4 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Fri, 3 Nov 2023 03:18:46 -0400 Subject: [PATCH 005/185] bump version --- fastplotlib/VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastplotlib/VERSION b/fastplotlib/VERSION index 2952df5a6..a0ad4fd5d 100644 --- a/fastplotlib/VERSION +++ b/fastplotlib/VERSION @@ -1 +1 @@ -0.1.0.a14 +0.1.0.a15 From 363df5aef2a0b7205f52c68ec122d6597f6579df Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Mon, 13 Nov 2023 18:27:31 -0500 Subject: [PATCH 006/185] try CI with bigmem runner again (#363) bigmem runner works --- .github/workflows/ci.yml | 6 +++--- .github/workflows/screenshots.yml | 2 +- examples/desktop/screenshots/gridplot.png | 4 ++-- examples/desktop/screenshots/image_cmap.png | 4 ++-- examples/desktop/screenshots/image_rgb.png | 4 ++-- examples/desktop/screenshots/image_rgbvminvmax.png | 4 ++-- examples/desktop/screenshots/image_simple.png | 4 ++-- examples/desktop/screenshots/image_vminvmax.png | 4 ++-- examples/desktop/screenshots/line.png | 4 ++-- examples/desktop/screenshots/line_cmap.png | 4 ++-- examples/desktop/screenshots/line_collection.png | 4 ++-- .../desktop/screenshots/line_collection_cmap_values.png | 4 ++-- .../screenshots/line_collection_cmap_values_qualitative.png | 4 ++-- examples/desktop/screenshots/line_collection_colors.png | 4 ++-- examples/desktop/screenshots/line_colorslice.png | 4 ++-- examples/desktop/screenshots/line_dataslice.png | 4 ++-- examples/desktop/screenshots/line_present_scaling.png | 4 ++-- examples/desktop/screenshots/line_stack.png | 4 ++-- examples/desktop/screenshots/scatter.png | 4 ++-- examples/desktop/screenshots/scatter_cmap.png | 4 ++-- examples/desktop/screenshots/scatter_colorslice.png | 4 ++-- examples/desktop/screenshots/scatter_dataslice.png | 4 ++-- examples/desktop/screenshots/scatter_present.png | 4 ++-- examples/desktop/screenshots/scatter_size.png | 4 ++-- examples/notebooks/screenshots/nb-astronaut.png | 4 ++-- examples/notebooks/screenshots/nb-astronaut_RGB.png | 4 ++-- examples/notebooks/screenshots/nb-camera.png | 4 ++-- examples/notebooks/screenshots/nb-lines-3d.png | 4 ++-- .../screenshots/nb-lines-cmap-jet-values-cosine.png | 4 ++-- examples/notebooks/screenshots/nb-lines-cmap-jet-values.png | 4 ++-- examples/notebooks/screenshots/nb-lines-cmap-jet.png | 4 ++-- examples/notebooks/screenshots/nb-lines-cmap-tab-10.png | 4 ++-- .../notebooks/screenshots/nb-lines-cmap-viridis-values.png | 4 ++-- examples/notebooks/screenshots/nb-lines-cmap-viridis.png | 4 ++-- examples/notebooks/screenshots/nb-lines-cmap-white.png | 4 ++-- examples/notebooks/screenshots/nb-lines-colors.png | 4 ++-- examples/notebooks/screenshots/nb-lines-data.png | 4 ++-- examples/notebooks/screenshots/nb-lines-underlay.png | 4 ++-- examples/notebooks/screenshots/nb-lines.png | 4 ++-- 39 files changed, 78 insertions(+), 78 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7c15e3865..08a36a971 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: docs-build: name: Docs - runs-on: ubuntu-latest + runs-on: bigmem strategy: fail-fast: false steps: @@ -48,7 +48,7 @@ jobs: test-build-full: name: Test examples, env with notebook and glfw - runs-on: ubuntu-latest + runs-on: bigmem if: ${{ !github.event.pull_request.draft }} strategy: fail-fast: false @@ -105,7 +105,7 @@ jobs: test-build-desktop: name: Test examples, env with only glfw - runs-on: ubuntu-latest + runs-on: bigmem if: ${{ !github.event.pull_request.draft }} strategy: fail-fast: false diff --git a/.github/workflows/screenshots.yml b/.github/workflows/screenshots.yml index 40f55d234..39797b95b 100644 --- a/.github/workflows/screenshots.yml +++ b/.github/workflows/screenshots.yml @@ -13,7 +13,7 @@ on: jobs: screenshots: name: Regenerate - runs-on: 'ubuntu-latest' + runs-on: bigmem if: ${{ !github.event.pull_request.draft }} steps: - name: Install git-lfs diff --git a/examples/desktop/screenshots/gridplot.png b/examples/desktop/screenshots/gridplot.png index f2cbb1e7a..eafded3b1 100644 --- a/examples/desktop/screenshots/gridplot.png +++ b/examples/desktop/screenshots/gridplot.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2705c69adab84f7740322b4a66ce33df00001dc7d51624becb8e88204113b028 -size 350236 +oid sha256:a83704df89be660a6797e27ae4031ab48eb21f061a94cfdeb007a022b745de06 +size 350327 diff --git a/examples/desktop/screenshots/image_cmap.png b/examples/desktop/screenshots/image_cmap.png index cf3ae8ac0..f2a628a8b 100644 --- a/examples/desktop/screenshots/image_cmap.png +++ b/examples/desktop/screenshots/image_cmap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d9dcf05ca2953103b9960d9159ccb89dc257bf5e5c6d3906eeaaac9f71686439 -size 274882 +oid sha256:8cc6aa770845f05bdc8914d3c983504f7a75b316a24e8e2b182c05145187b3e2 +size 274798 diff --git a/examples/desktop/screenshots/image_rgb.png b/examples/desktop/screenshots/image_rgb.png index 5681017c8..90f1da3c2 100644 --- a/examples/desktop/screenshots/image_rgb.png +++ b/examples/desktop/screenshots/image_rgb.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:408e31db97278c584f4aaa0039099366fc8feb5693d15ab335205927d067c42a -size 319585 +oid sha256:3372487a5ae0fd47095ffbfc90915b94ff1a54c463ae887474f6eadd04801d6b +size 319514 diff --git a/examples/desktop/screenshots/image_rgbvminvmax.png b/examples/desktop/screenshots/image_rgbvminvmax.png index aea5fdf85..a89c4de3e 100644 --- a/examples/desktop/screenshots/image_rgbvminvmax.png +++ b/examples/desktop/screenshots/image_rgbvminvmax.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d5dbe9a837b3503ca45eb83edbec7b1d7b6463093699af6b01b5303978af4b85 -size 44781 +oid sha256:026ea433786e50aa3d9e0fa8eb1d921617e52aca97eb6ffd5c8b297f1cd69c79 +size 44787 diff --git a/examples/desktop/screenshots/image_simple.png b/examples/desktop/screenshots/image_simple.png index 5ab073433..010101d59 100644 --- a/examples/desktop/screenshots/image_simple.png +++ b/examples/desktop/screenshots/image_simple.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4aa397a120ed1b232c4d56ffd3547ea42c2874aa54bfbdbffebfd34129059ccd -size 272355 +oid sha256:64803f58bd07e87d171e66eb9437d4fcc2ec1781e4da150a7229f6deef0835ad +size 272599 diff --git a/examples/desktop/screenshots/image_vminvmax.png b/examples/desktop/screenshots/image_vminvmax.png index aea5fdf85..a89c4de3e 100644 --- a/examples/desktop/screenshots/image_vminvmax.png +++ b/examples/desktop/screenshots/image_vminvmax.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d5dbe9a837b3503ca45eb83edbec7b1d7b6463093699af6b01b5303978af4b85 -size 44781 +oid sha256:026ea433786e50aa3d9e0fa8eb1d921617e52aca97eb6ffd5c8b297f1cd69c79 +size 44787 diff --git a/examples/desktop/screenshots/line.png b/examples/desktop/screenshots/line.png index a38008ab9..0160a3213 100644 --- a/examples/desktop/screenshots/line.png +++ b/examples/desktop/screenshots/line.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1fe996cd43013ff2e616e8a549933137529c13ad8e320331420e9c64f6ed1690 -size 49738 +oid sha256:164e5c36d57707da3ab928fe79312b4362de083c8422fc126ebc6cbc895eb022 +size 49783 diff --git a/examples/desktop/screenshots/line_cmap.png b/examples/desktop/screenshots/line_cmap.png index 0ece6fbde..a07b15d36 100644 --- a/examples/desktop/screenshots/line_cmap.png +++ b/examples/desktop/screenshots/line_cmap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c6f511ffd3a10e2c653afd3b9eca8f6bb10af26759a7efc73fe16c825cc1bf15 -size 43718 +oid sha256:c6df49f5f900caa217a5a0b01f601d6e83e1ee5c3a60c8968b9be837df785905 +size 43673 diff --git a/examples/desktop/screenshots/line_collection.png b/examples/desktop/screenshots/line_collection.png index f7be75201..60ec82bc8 100644 --- a/examples/desktop/screenshots/line_collection.png +++ b/examples/desktop/screenshots/line_collection.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4aa71b9b8d2c049dad951493a5f51c32da33a3e536761254cd18368d6b8cd8e8 -size 146755 +oid sha256:e0ddd75bc2fd8e4844ccb46c53fe463dc604927737ed00c8fb6b1c29fd2b1ab2 +size 146797 diff --git a/examples/desktop/screenshots/line_collection_cmap_values.png b/examples/desktop/screenshots/line_collection_cmap_values.png index a91e4ce69..7223db9ee 100644 --- a/examples/desktop/screenshots/line_collection_cmap_values.png +++ b/examples/desktop/screenshots/line_collection_cmap_values.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a32210432cd8e88bec20a84b1e6839d0a2d5bb2edb1aea8ebe09569872cb16d8 -size 93580 +oid sha256:311c7ef6f6f46e32983a5a531adc179b0179f382300f3cafbb6d8a7d4aeab565 +size 93676 diff --git a/examples/desktop/screenshots/line_collection_cmap_values_qualitative.png b/examples/desktop/screenshots/line_collection_cmap_values_qualitative.png index c38e5fb96..500de82bb 100644 --- a/examples/desktop/screenshots/line_collection_cmap_values_qualitative.png +++ b/examples/desktop/screenshots/line_collection_cmap_values_qualitative.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5be6f9343b47848d3e1be4b82315f0b71bdb1b919503f943c618ef8ba7f6272a -size 95656 +oid sha256:8714dd43aa3d5c791fb9359e745895447d3a3234e3e8598d171ea3666a3fd7a3 +size 95660 diff --git a/examples/desktop/screenshots/line_collection_colors.png b/examples/desktop/screenshots/line_collection_colors.png index 1ae597033..f60faae32 100644 --- a/examples/desktop/screenshots/line_collection_colors.png +++ b/examples/desktop/screenshots/line_collection_colors.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b1e5b913ca91293a8edb8f6898249dd62019cb827223dacf377e3fc6cda89a77 -size 82686 +oid sha256:e2fad15985608a4c6b1fda3005dc89950b4cad5fed956f5d26672257385985d0 +size 82753 diff --git a/examples/desktop/screenshots/line_colorslice.png b/examples/desktop/screenshots/line_colorslice.png index 003f86e44..ebacf9170 100644 --- a/examples/desktop/screenshots/line_colorslice.png +++ b/examples/desktop/screenshots/line_colorslice.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0033fc23cda3f07cdd7db642d4d1af710319d56a1690339354a9df27bf51c381 -size 57146 +oid sha256:3f708da0a80ec8776da28052835e19132bee8e1a1e301021bf4bc5f44e6a8825 +size 57105 diff --git a/examples/desktop/screenshots/line_dataslice.png b/examples/desktop/screenshots/line_dataslice.png index 20c777212..55fd20a91 100644 --- a/examples/desktop/screenshots/line_dataslice.png +++ b/examples/desktop/screenshots/line_dataslice.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2a169b359a0e56bb48a625069e73e53b26e061e6bcb83d6eb613fbdd1a43cdac -size 75385 +oid sha256:91a3995675530ed2132149e2c2f1dad6731b5d9ec7b503d78da133db1e06cae0 +size 75481 diff --git a/examples/desktop/screenshots/line_present_scaling.png b/examples/desktop/screenshots/line_present_scaling.png index c4a41ac2e..bcb84b62b 100644 --- a/examples/desktop/screenshots/line_present_scaling.png +++ b/examples/desktop/screenshots/line_present_scaling.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c67d65b0a5120c014b34dcfc326079113cee22b849c14a0284fc7881dac5d43c -size 43446 +oid sha256:b7b8d50cbbd9b1dafbf9a6559ef974c53e14e2a8106fa0c5c3c065fc532c76e5 +size 43413 diff --git a/examples/desktop/screenshots/line_stack.png b/examples/desktop/screenshots/line_stack.png index 23343df32..443184247 100644 --- a/examples/desktop/screenshots/line_stack.png +++ b/examples/desktop/screenshots/line_stack.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bc2496c203b2994ef5b8e714e1c7619e726d0b605e0c25498f11e1154d4905ec -size 360981 +oid sha256:29580e5ebb0597d54adb2b7f2f91de44e4480cf23c0c271ee44426203b4c1c44 +size 360892 diff --git a/examples/desktop/screenshots/scatter.png b/examples/desktop/screenshots/scatter.png index e35fd9e3c..48e22fe53 100644 --- a/examples/desktop/screenshots/scatter.png +++ b/examples/desktop/screenshots/scatter.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e2efb81fb8b6b11bb1fc3136394c0f6bf1c38972d03dabd07949928f4e53cf71 -size 25240 +oid sha256:8d806d252ee03ade60cffcc651ebe041560bcc095f5aaeacf43f3ffb4d29f660 +size 25231 diff --git a/examples/desktop/screenshots/scatter_cmap.png b/examples/desktop/screenshots/scatter_cmap.png index 10477e81b..06b94d9ed 100644 --- a/examples/desktop/screenshots/scatter_cmap.png +++ b/examples/desktop/screenshots/scatter_cmap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8b4b364d1cd3ab328f025030db87f8ff4fb2544c3bfb67176ea8f0acdc24f04b -size 59407 +oid sha256:e1c0c2b899fdb0e5456ec7305ba20a5fc04b715f88c834715fcb867d456d58b3 +size 59401 diff --git a/examples/desktop/screenshots/scatter_colorslice.png b/examples/desktop/screenshots/scatter_colorslice.png index cd5a1f00d..f9edc4d6a 100644 --- a/examples/desktop/screenshots/scatter_colorslice.png +++ b/examples/desktop/screenshots/scatter_colorslice.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0d3afcf293e425c2369d93745cb933afb15d971866620f160629e394f50cd9b6 -size 23747 +oid sha256:3d271bf913c196559ae0f5e323326dc9f68b015ce0d2078d775371ff57bef309 +size 23746 diff --git a/examples/desktop/screenshots/scatter_dataslice.png b/examples/desktop/screenshots/scatter_dataslice.png index 8ed7ad590..c444b2604 100644 --- a/examples/desktop/screenshots/scatter_dataslice.png +++ b/examples/desktop/screenshots/scatter_dataslice.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:974111967bc5e4197b6b38c7a00950ee013ce4e689162c9d91e902d37240221a -size 26001 +oid sha256:ef00ef6e6babf9fbfd18b7c1b91cc80c6c1dd0253d7f5fbb2a7b9a900dad4e41 +size 25996 diff --git a/examples/desktop/screenshots/scatter_present.png b/examples/desktop/screenshots/scatter_present.png index 335191d91..3c19441b6 100644 --- a/examples/desktop/screenshots/scatter_present.png +++ b/examples/desktop/screenshots/scatter_present.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fb5b0bf6fb7b7dbfafc1b2553dfff87f329ec5070fb69061313acce46364df52 -size 24627 +oid sha256:cf630459458da82853919717908d433ab1fe846f2158a30b7ef7ddbd53ae418d +size 24617 diff --git a/examples/desktop/screenshots/scatter_size.png b/examples/desktop/screenshots/scatter_size.png index 1d0f91f9c..da211cde1 100644 --- a/examples/desktop/screenshots/scatter_size.png +++ b/examples/desktop/screenshots/scatter_size.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3eb05d8a18aada52a6ab02a0d3d030aab97510aace226cf3967e5c5c1cd3274d -size 66044 +oid sha256:10533aa5831a50a0f9b38c0a60b89a9b6c33311ecb3a569c5e0b4c82379dc20a +size 66037 diff --git a/examples/notebooks/screenshots/nb-astronaut.png b/examples/notebooks/screenshots/nb-astronaut.png index e8345f7b2..2faf79def 100644 --- a/examples/notebooks/screenshots/nb-astronaut.png +++ b/examples/notebooks/screenshots/nb-astronaut.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:36a11f5c0a80e1cfbdeb318b314886f4d8e02ba8a763bed0db9994ef451bfd42 -size 128068 +oid sha256:4845a61b99f7a489ac82a8688cc3350ce66e4771bb1399354591fd39688a58a2 +size 127977 diff --git a/examples/notebooks/screenshots/nb-astronaut_RGB.png b/examples/notebooks/screenshots/nb-astronaut_RGB.png index 0ff257ccf..22b2627cb 100644 --- a/examples/notebooks/screenshots/nb-astronaut_RGB.png +++ b/examples/notebooks/screenshots/nb-astronaut_RGB.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dc27fc081b464bb53afd98d3748b8bc75764537d76a8012b9f1b2c1d4c10613d -size 125492 +oid sha256:2c34bd21fd7bf98bab25431019e8fee30b0f4912b6b4495ad963fb9e107b1f21 +size 125479 diff --git a/examples/notebooks/screenshots/nb-camera.png b/examples/notebooks/screenshots/nb-camera.png index cbf936192..32e83f3ba 100644 --- a/examples/notebooks/screenshots/nb-camera.png +++ b/examples/notebooks/screenshots/nb-camera.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cbf213d944a16cf9f72542e7a2172330fefa97c8577905f07df12559eb4485c3 -size 89303 +oid sha256:8ce695e954332a9b9122d418645d785900506fc30a897844bdf7fdce0bffb316 +size 89342 diff --git a/examples/notebooks/screenshots/nb-lines-3d.png b/examples/notebooks/screenshots/nb-lines-3d.png index 6bb05537a..727450428 100644 --- a/examples/notebooks/screenshots/nb-lines-3d.png +++ b/examples/notebooks/screenshots/nb-lines-3d.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b7e61fb22db10e515a7d249649c5e220731c6ea5a83bb626f06dcf41167f117e -size 23052 +oid sha256:1da691b87d324d1a5b2c4f9231be89c0c3dddb0584615a07f28a4d027dc59d5c +size 23057 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-jet-values-cosine.png b/examples/notebooks/screenshots/nb-lines-cmap-jet-values-cosine.png index b1045cde6..2f149e7f4 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-jet-values-cosine.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-jet-values-cosine.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1f55806e64a8ffde2f11eed1dc75a874371800046c062da21e71554abedda251 -size 17136 +oid sha256:a755f36126b805b8d63d6ea679ffa270dc20d976a9a3a2cd1420ccdf0e981474 +size 17158 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-jet-values.png b/examples/notebooks/screenshots/nb-lines-cmap-jet-values.png index 53b3d4cbd..45a34c5f0 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-jet-values.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-jet-values.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e5e9bcb785fe5efee324bdde451d62158668dafa0c026179bd11d38298fb0002 -size 18526 +oid sha256:76e55108ce7ace466fb9b90b852a1102ff8b4c931e16f05af231a854519c8467 +size 18505 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-jet.png b/examples/notebooks/screenshots/nb-lines-cmap-jet.png index 8bfd0d577..ed8138ab1 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-jet.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-jet.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2d6fd17a9a704b2d9c5341e85763f1ba9c5e3026da88858f004e66a781e02eaa -size 16310 +oid sha256:7e463ac93808329ffa4fe421838ff8d39ce49081b888d09954405cd170af8a85 +size 16267 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-tab-10.png b/examples/notebooks/screenshots/nb-lines-cmap-tab-10.png index 3e76883bf..e10d6f5e9 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-tab-10.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-tab-10.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:672da2cc5e500ce3bbdabb01eaf5a7d2b9fb6ea4e6e95cb3392b2a0573a970d9 -size 14882 +oid sha256:99c10da11e298d69cd85d587b133fe31a5528657ba456e5f0050ca0e48ed0f31 +size 14865 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-viridis-values.png b/examples/notebooks/screenshots/nb-lines-cmap-viridis-values.png index 4b6212a6a..da5693226 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-viridis-values.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-viridis-values.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1e1224f75ce0286c4721b5f65af339fc922dcb2308f8d2fa3def10ead48cdce8 -size 15096 +oid sha256:d356938c29834263d9879c3217782414d631e998ea02f11f3c00c0f64d8a63a6 +size 15084 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-viridis.png b/examples/notebooks/screenshots/nb-lines-cmap-viridis.png index 35c38c881..ddcb6d54a 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-viridis.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-viridis.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f6201cd8dc9273adca73329b0eae81faf6aed42c3bf8f7ee503b9251af499dcd -size 19203 +oid sha256:dfd17b5b191edf58377451b0c34636d047f69de1a89087d9d2d561d967c4d236 +size 19118 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-white.png b/examples/notebooks/screenshots/nb-lines-cmap-white.png index 67c2fc116..93efe221c 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-white.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-white.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ecb2d4d591b852bda8758efcf91d389442f916bbb4a06c5216d52dcf72172370 -size 12955 +oid sha256:d3b512a195ab30075a028d19884716c5f776acd6f118e682046d6659d1e1095e +size 12962 diff --git a/examples/notebooks/screenshots/nb-lines-colors.png b/examples/notebooks/screenshots/nb-lines-colors.png index b9972c8f4..a8af1a4be 100644 --- a/examples/notebooks/screenshots/nb-lines-colors.png +++ b/examples/notebooks/screenshots/nb-lines-colors.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a8eefa5106414bfb540b282d74372831ef3c4a9d941aaf50026ea64a3d3009f7 -size 40544 +oid sha256:c8db3d4ccb1e56919866a6ba510033dc903d23a5af06a961f90ddb7382d16ec7 +size 40477 diff --git a/examples/notebooks/screenshots/nb-lines-data.png b/examples/notebooks/screenshots/nb-lines-data.png index 14d6f89f0..369a999b2 100644 --- a/examples/notebooks/screenshots/nb-lines-data.png +++ b/examples/notebooks/screenshots/nb-lines-data.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8e89906d0d749f443e751eeb43b017622a46dfaa91545e9135d0a519e0aad0eb -size 54446 +oid sha256:2907942f9307eb21764ea02d363a8c6c8b7e4fbf375257b7c19225e1f7b66279 +size 54404 diff --git a/examples/notebooks/screenshots/nb-lines-underlay.png b/examples/notebooks/screenshots/nb-lines-underlay.png index d8809f301..d9164c635 100644 --- a/examples/notebooks/screenshots/nb-lines-underlay.png +++ b/examples/notebooks/screenshots/nb-lines-underlay.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:61ed6bde5639d57694cb8752052dda08a5f2f7dcc32966ab62385bc866c299e3 -size 55936 +oid sha256:fd21151b5af0065b25e20b7e382d310afebb3ee36ab12acadc6188b6adf4b128 +size 55961 diff --git a/examples/notebooks/screenshots/nb-lines.png b/examples/notebooks/screenshots/nb-lines.png index 3dcc1767e..2fcd0637f 100644 --- a/examples/notebooks/screenshots/nb-lines.png +++ b/examples/notebooks/screenshots/nb-lines.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:39478dbf9af2f74ae0e0240616d94480569d53dcbd5f046315eeff3855d4cb2e -size 37711 +oid sha256:4f70a9dd8cc631337ba1ccc7931ef2d412d0d6713d5403011994d59258f61e34 +size 37714 From 37687592fe3788caa387f052f49c793372e2be45 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Wed, 15 Nov 2023 23:05:07 -0500 Subject: [PATCH 007/185] set canvas default 60fps (#370) --- fastplotlib/layouts/_utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fastplotlib/layouts/_utils.py b/fastplotlib/layouts/_utils.py index dd6fbeb50..e8201d073 100644 --- a/fastplotlib/layouts/_utils.py +++ b/fastplotlib/layouts/_utils.py @@ -66,7 +66,7 @@ def make_canvas_and_renderer( if canvas is None: Canvas = auto_determine_canvas() - canvas = Canvas() + canvas = Canvas(max_fps=60) elif isinstance(canvas, str): if canvas not in CANVAS_OPTIONS: @@ -76,7 +76,7 @@ def make_canvas_and_renderer( f"The {canvas} framework is not installed for using this canvas" ) else: - canvas = CANVAS_OPTIONS_AVAILABLE[canvas]() + canvas = CANVAS_OPTIONS_AVAILABLE[canvas](max_fps=60) elif not isinstance(canvas, (WgpuCanvasBase, Texture)): raise ValueError( From 7350b81e66c0cacd4246d12812d0d6943d31c498 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Wed, 15 Nov 2023 23:19:00 -0500 Subject: [PATCH 008/185] `Plot.auto_scale()` shouldn't change the camera aspect unless desired (#364) * now auto_scale() by default does not change camera aspect * center_scene() and maintain_aspect() take only kwargs * update screenshot images after auto_scale() fix --- examples/desktop/screenshots/gridplot.png | 4 ++-- .../screenshots/gridplot_non_square.png | 4 ++-- examples/desktop/screenshots/heatmap.png | 4 ++-- examples/desktop/screenshots/heatmap_cmap.png | 4 ++-- examples/desktop/screenshots/heatmap_data.png | 4 ++-- .../desktop/screenshots/heatmap_vmin_vmax.png | 4 ++-- examples/desktop/screenshots/image_cmap.png | 4 ++-- examples/desktop/screenshots/image_rgb.png | 4 ++-- .../desktop/screenshots/image_rgbvminvmax.png | 4 ++-- examples/desktop/screenshots/image_simple.png | 4 ++-- .../desktop/screenshots/image_vminvmax.png | 4 ++-- examples/desktop/screenshots/line.png | 4 ++-- .../desktop/screenshots/line_colorslice.png | 4 ++-- .../desktop/screenshots/line_dataslice.png | 4 ++-- .../screenshots/line_present_scaling.png | 4 ++-- examples/desktop/screenshots/scatter.png | 2 +- examples/desktop/screenshots/scatter_cmap.png | 4 ++-- .../screenshots/scatter_colorslice.png | 4 ++-- .../desktop/screenshots/scatter_dataslice.png | 4 ++-- .../desktop/screenshots/scatter_present.png | 4 ++-- .../screenshots/nb-lines-underlay.png | 4 ++-- fastplotlib/layouts/_plot_area.py | 20 ++++++++++++------- 22 files changed, 54 insertions(+), 48 deletions(-) diff --git a/examples/desktop/screenshots/gridplot.png b/examples/desktop/screenshots/gridplot.png index eafded3b1..bc35ccf8c 100644 --- a/examples/desktop/screenshots/gridplot.png +++ b/examples/desktop/screenshots/gridplot.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a83704df89be660a6797e27ae4031ab48eb21f061a94cfdeb007a022b745de06 -size 350327 +oid sha256:e416fc968edd3788513e369f4d265b0abd7216a7ef19ec8b84659c30ca7c8ca1 +size 307384 diff --git a/examples/desktop/screenshots/gridplot_non_square.png b/examples/desktop/screenshots/gridplot_non_square.png index 7b534aef9..82b2b0eb4 100644 --- a/examples/desktop/screenshots/gridplot_non_square.png +++ b/examples/desktop/screenshots/gridplot_non_square.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:abf936904d1b5e2018a72311c510108925f2972dfdf59166580ad27876f9e2be -size 220140 +oid sha256:ab5598b67b80efce0d2559e400e77098e734c91608a3f49b691ddaa030d47edb +size 203434 diff --git a/examples/desktop/screenshots/heatmap.png b/examples/desktop/screenshots/heatmap.png index d0df1510a..a0655cf3a 100644 --- a/examples/desktop/screenshots/heatmap.png +++ b/examples/desktop/screenshots/heatmap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6c19d6454e79d92074bac01175dfbb8506e882ea55b626c0b2357960ed6e294f -size 163655 +oid sha256:6872c3cc3e35ec918b054fb2d76525bbd3d82d8b49916aca1046aa1be65ff923 +size 111825 diff --git a/examples/desktop/screenshots/heatmap_cmap.png b/examples/desktop/screenshots/heatmap_cmap.png index db3038dee..2eb769c14 100644 --- a/examples/desktop/screenshots/heatmap_cmap.png +++ b/examples/desktop/screenshots/heatmap_cmap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0328eec32f13042e3e2f243793317e180bba1353fe961604ecad3f38463b8809 -size 156419 +oid sha256:f2eba96c2bfb1d07365810a69e99c79b068741f5dcf74fc745c13d5ff21f16f2 +size 106671 diff --git a/examples/desktop/screenshots/heatmap_data.png b/examples/desktop/screenshots/heatmap_data.png index 96169ec77..50a8ae79e 100644 --- a/examples/desktop/screenshots/heatmap_data.png +++ b/examples/desktop/screenshots/heatmap_data.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4a376c24fa123088be69f807ec5212cb5ed5680b146ce9d62df584790c632845 -size 20838 +oid sha256:f0576063658e05e19b7723b4c88dc4d55a8178b090b4a88e33251fc92408b4a1 +size 18051 diff --git a/examples/desktop/screenshots/heatmap_vmin_vmax.png b/examples/desktop/screenshots/heatmap_vmin_vmax.png index 2a809d545..f10382e87 100644 --- a/examples/desktop/screenshots/heatmap_vmin_vmax.png +++ b/examples/desktop/screenshots/heatmap_vmin_vmax.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3eb12ad590aa8260f2cf722abadf5b51bcb7d5a8d8d1cb05b7711b50331da07a -size 165937 +oid sha256:639d50f2f5fb07dba461e7a38de3886092f8754277eadbb5e305e32023289abd +size 124403 diff --git a/examples/desktop/screenshots/image_cmap.png b/examples/desktop/screenshots/image_cmap.png index f2a628a8b..bed07a41a 100644 --- a/examples/desktop/screenshots/image_cmap.png +++ b/examples/desktop/screenshots/image_cmap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8cc6aa770845f05bdc8914d3c983504f7a75b316a24e8e2b182c05145187b3e2 -size 274798 +oid sha256:e1d78cc0681079a5c43d9fdb4142f5fee75d477d9f9a1469fca8bc8933c244fc +size 216210 diff --git a/examples/desktop/screenshots/image_rgb.png b/examples/desktop/screenshots/image_rgb.png index 90f1da3c2..a21c0658b 100644 --- a/examples/desktop/screenshots/image_rgb.png +++ b/examples/desktop/screenshots/image_rgb.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3372487a5ae0fd47095ffbfc90915b94ff1a54c463ae887474f6eadd04801d6b -size 319514 +oid sha256:820a73b9b2e5bbaed84fb11438e2c5672b76c8b57a33823f4840a71be03d7dd1 +size 251438 diff --git a/examples/desktop/screenshots/image_rgbvminvmax.png b/examples/desktop/screenshots/image_rgbvminvmax.png index a89c4de3e..88acfadc5 100644 --- a/examples/desktop/screenshots/image_rgbvminvmax.png +++ b/examples/desktop/screenshots/image_rgbvminvmax.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:026ea433786e50aa3d9e0fa8eb1d921617e52aca97eb6ffd5c8b297f1cd69c79 -size 44787 +oid sha256:5f2f562573f8104342ae01b5852f71c960341bdd70ae0bc7967df663166edbd3 +size 39604 diff --git a/examples/desktop/screenshots/image_simple.png b/examples/desktop/screenshots/image_simple.png index 010101d59..098d5a055 100644 --- a/examples/desktop/screenshots/image_simple.png +++ b/examples/desktop/screenshots/image_simple.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:64803f58bd07e87d171e66eb9437d4fcc2ec1781e4da150a7229f6deef0835ad -size 272599 +oid sha256:a4ed42d042d8bb7e35f31b5ad0a3e3a495cf9c3164516eb457d8b41d7fae6bab +size 213075 diff --git a/examples/desktop/screenshots/image_vminvmax.png b/examples/desktop/screenshots/image_vminvmax.png index a89c4de3e..88acfadc5 100644 --- a/examples/desktop/screenshots/image_vminvmax.png +++ b/examples/desktop/screenshots/image_vminvmax.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:026ea433786e50aa3d9e0fa8eb1d921617e52aca97eb6ffd5c8b297f1cd69c79 -size 44787 +oid sha256:5f2f562573f8104342ae01b5852f71c960341bdd70ae0bc7967df663166edbd3 +size 39604 diff --git a/examples/desktop/screenshots/line.png b/examples/desktop/screenshots/line.png index 0160a3213..cbc0a7b21 100644 --- a/examples/desktop/screenshots/line.png +++ b/examples/desktop/screenshots/line.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:164e5c36d57707da3ab928fe79312b4362de083c8422fc126ebc6cbc895eb022 -size 49783 +oid sha256:018f4a36c60b3de2f1406aa2823b751de5fae2c2340f9d49368d007ba7379637 +size 44422 diff --git a/examples/desktop/screenshots/line_colorslice.png b/examples/desktop/screenshots/line_colorslice.png index ebacf9170..7b652f165 100644 --- a/examples/desktop/screenshots/line_colorslice.png +++ b/examples/desktop/screenshots/line_colorslice.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3f708da0a80ec8776da28052835e19132bee8e1a1e301021bf4bc5f44e6a8825 -size 57105 +oid sha256:5bd80cee80f491da6ab64c868a3c70254a68072e0bc0caad80c7999cadcb2df9 +size 50497 diff --git a/examples/desktop/screenshots/line_dataslice.png b/examples/desktop/screenshots/line_dataslice.png index 55fd20a91..d68a554bd 100644 --- a/examples/desktop/screenshots/line_dataslice.png +++ b/examples/desktop/screenshots/line_dataslice.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:91a3995675530ed2132149e2c2f1dad6731b5d9ec7b503d78da133db1e06cae0 -size 75481 +oid sha256:823651905a775b5cbcc2ce0f79d25d69b29f17b4c060c244d80ae87019f05d5b +size 69332 diff --git a/examples/desktop/screenshots/line_present_scaling.png b/examples/desktop/screenshots/line_present_scaling.png index bcb84b62b..02cd2b1f8 100644 --- a/examples/desktop/screenshots/line_present_scaling.png +++ b/examples/desktop/screenshots/line_present_scaling.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b7b8d50cbbd9b1dafbf9a6559ef974c53e14e2a8106fa0c5c3c065fc532c76e5 -size 43413 +oid sha256:3d18669d5e75cee3326d0380ae5dd26cab71ea97725ff99bc5228d2555d51454 +size 30373 diff --git a/examples/desktop/screenshots/scatter.png b/examples/desktop/screenshots/scatter.png index 48e22fe53..bf5e8c92a 100644 --- a/examples/desktop/screenshots/scatter.png +++ b/examples/desktop/screenshots/scatter.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8d806d252ee03ade60cffcc651ebe041560bcc095f5aaeacf43f3ffb4d29f660 +oid sha256:bd38399b77e09d915c5bb1e7ee022f936ae90682f598357bc774a95c372dc78f size 25231 diff --git a/examples/desktop/screenshots/scatter_cmap.png b/examples/desktop/screenshots/scatter_cmap.png index 06b94d9ed..eec22566a 100644 --- a/examples/desktop/screenshots/scatter_cmap.png +++ b/examples/desktop/screenshots/scatter_cmap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e1c0c2b899fdb0e5456ec7305ba20a5fc04b715f88c834715fcb867d456d58b3 -size 59401 +oid sha256:e712693b403166909dcaa65256131eacba0a15892cd144ad97fdecb6b9835e93 +size 57273 diff --git a/examples/desktop/screenshots/scatter_colorslice.png b/examples/desktop/screenshots/scatter_colorslice.png index f9edc4d6a..0da0fcd9f 100644 --- a/examples/desktop/screenshots/scatter_colorslice.png +++ b/examples/desktop/screenshots/scatter_colorslice.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3d271bf913c196559ae0f5e323326dc9f68b015ce0d2078d775371ff57bef309 -size 23746 +oid sha256:c620cad9976f390e44a5b037f3ff61fb80e6487e17f4be8118be5df55f276a35 +size 23664 diff --git a/examples/desktop/screenshots/scatter_dataslice.png b/examples/desktop/screenshots/scatter_dataslice.png index c444b2604..32f56ad11 100644 --- a/examples/desktop/screenshots/scatter_dataslice.png +++ b/examples/desktop/screenshots/scatter_dataslice.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ef00ef6e6babf9fbfd18b7c1b91cc80c6c1dd0253d7f5fbb2a7b9a900dad4e41 -size 25996 +oid sha256:69d2f0999b0bb334e48320702095fc76444f4d89d43a51ac6c5c8f49e1df96ac +size 25999 diff --git a/examples/desktop/screenshots/scatter_present.png b/examples/desktop/screenshots/scatter_present.png index 3c19441b6..8c1e5eed4 100644 --- a/examples/desktop/screenshots/scatter_present.png +++ b/examples/desktop/screenshots/scatter_present.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cf630459458da82853919717908d433ab1fe846f2158a30b7ef7ddbd53ae418d -size 24617 +oid sha256:e45c1a936771e569e562ed3496421e498e725325093e84243ab494c0718ead3a +size 23639 diff --git a/examples/notebooks/screenshots/nb-lines-underlay.png b/examples/notebooks/screenshots/nb-lines-underlay.png index d9164c635..d6b630362 100644 --- a/examples/notebooks/screenshots/nb-lines-underlay.png +++ b/examples/notebooks/screenshots/nb-lines-underlay.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fd21151b5af0065b25e20b7e382d310afebb3ee36ab12acadc6188b6adf4b128 -size 55961 +oid sha256:70800d1739bb5ba7d5d9d2399335ebc1ce8a0874042ed4b6256b6d041014eb18 +size 55884 diff --git a/fastplotlib/layouts/_plot_area.py b/fastplotlib/layouts/_plot_area.py index 7590bad10..dec5d891e 100644 --- a/fastplotlib/layouts/_plot_area.py +++ b/fastplotlib/layouts/_plot_area.py @@ -459,7 +459,7 @@ def center_graphic(self, graphic: Graphic, zoom: float = 1.35): # probably because camera.show_object uses bounding sphere self.camera.zoom = zoom - def center_scene(self, zoom: float = 1.35): + def center_scene(self, *, zoom: float = 1.35): """ Auto-center the scene, does not scale. @@ -481,15 +481,20 @@ def center_scene(self, zoom: float = 1.35): # probably because camera.show_object uses bounding sphere camera.zoom = zoom - def auto_scale(self, maintain_aspect: bool = False, zoom: float = 0.8): + def auto_scale( + self, + *, # since this is often used as an event handler, don't want to coerce maintain_aspect = True + maintain_aspect: Union[None, bool] = None, + zoom: float = 0.8 + ): """ Auto-scale the camera w.r.t to the scene Parameters ---------- - maintain_aspect: bool, default ``False`` - maintain the camera aspect ratio for all dimensions, if ``False`` the camera - is scaled according to the bounds in each dimension. + maintain_aspect: ``None`` or bool, default ``None`` + Maintain the camera aspect ratio for all dimensions. If ``None``, the aspect is left unchanged. + if ``False`` the camera is scaled to the bounding box of the current scene. zoom: float, default 0.8 zoom value for the camera after auto-scaling, if zoom = 1.0 then the graphics @@ -503,8 +508,9 @@ def auto_scale(self, maintain_aspect: bool = False, zoom: float = 0.8): self.scene.remove(selector.world_object) self.center_scene() - if not isinstance(maintain_aspect, bool): - maintain_aspect = False # assume False + + if maintain_aspect is None: # if not provided keep current setting + maintain_aspect = self.camera.maintain_aspect # scale all cameras associated with this controller else it looks wonky for camera in self.controller.cameras: From b1242d4d31731560936a6d0adbac39437187d1dd Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Thu, 16 Nov 2023 01:54:52 -0500 Subject: [PATCH 009/185] `ImageWidget` notebook tests (#371) * add .npy to gitattributes * add zfish data file * update imagewidget user demo, add imagewidget test nb, imagewidget auto-updates w.r.t. window_funcs and frame_apply * use scipy instead of skimage for gaussian filter example * add ffmpeg to ci * add imageio[pyav] * allow_pickle=True for zfish npy load * add git lfs fetch to screenshots ci, remove allow_pickle * add imagewidget ground-truth screenshots --- .gitattributes | 2 + .github/workflows/ci.yml | 2 +- .github/workflows/screenshots.yml | 6 +- examples/notebooks/image_widget.ipynb | 199 +++++--- examples/notebooks/image_widget_test.ipynb | 468 ++++++++++++++++++ .../nb-image-widget-movie-set_data.png | 3 + .../nb-image-widget-movie-single-0-reset.png | 3 + .../nb-image-widget-movie-single-0.png | 3 + .../nb-image-widget-movie-single-279.png | 3 + ...e-widget-movie-single-50-window-max-33.png | 3 + ...-widget-movie-single-50-window-mean-13.png | 3 + ...-widget-movie-single-50-window-mean-33.png | 3 + ...ge-widget-movie-single-50-window-reset.png | 3 + .../nb-image-widget-movie-single-50.png | 3 + .../nb-image-widget-single-gnuplot2.png | 3 + .../screenshots/nb-image-widget-single.png | 3 + ...et-zfish-frame-50-frame-apply-gaussian.png | 3 + ...idget-zfish-frame-50-frame-apply-reset.png | 3 + ...ge-widget-zfish-frame-50-max-window-13.png | 3 + ...e-widget-zfish-frame-50-mean-window-13.png | 3 + ...ge-widget-zfish-frame-50-mean-window-5.png | 3 + .../nb-image-widget-zfish-frame-50.png | 3 + .../nb-image-widget-zfish-frame-99.png | 3 + ...ish-grid-frame-50-frame-apply-gaussian.png | 3 + ...-zfish-grid-frame-50-frame-apply-reset.png | 3 + ...dget-zfish-grid-frame-50-max-window-13.png | 3 + ...get-zfish-grid-frame-50-mean-window-13.png | 3 + ...dget-zfish-grid-frame-50-mean-window-5.png | 3 + .../nb-image-widget-zfish-grid-frame-50.png | 3 + .../nb-image-widget-zfish-grid-frame-99.png | 3 + ...e-widget-zfish-grid-init-mean-window-5.png | 3 + ...fish-grid-set_data-reset-indices-false.png | 3 + ...zfish-grid-set_data-reset-indices-true.png | 3 + ...-image-widget-zfish-init-mean-window-5.png | 3 + examples/notebooks/zfish_test.npy | 3 + fastplotlib/widgets/image.py | 70 ++- setup.py | 2 +- 37 files changed, 761 insertions(+), 78 deletions(-) create mode 100644 examples/notebooks/image_widget_test.ipynb create mode 100644 examples/notebooks/screenshots/nb-image-widget-movie-set_data.png create mode 100644 examples/notebooks/screenshots/nb-image-widget-movie-single-0-reset.png create mode 100644 examples/notebooks/screenshots/nb-image-widget-movie-single-0.png create mode 100644 examples/notebooks/screenshots/nb-image-widget-movie-single-279.png create mode 100644 examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-max-33.png create mode 100644 examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-13.png create mode 100644 examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-33.png create mode 100644 examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-reset.png create mode 100644 examples/notebooks/screenshots/nb-image-widget-movie-single-50.png create mode 100644 examples/notebooks/screenshots/nb-image-widget-single-gnuplot2.png create mode 100644 examples/notebooks/screenshots/nb-image-widget-single.png create mode 100644 examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-gaussian.png create mode 100644 examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-reset.png create mode 100644 examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-max-window-13.png create mode 100644 examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-13.png create mode 100644 examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-5.png create mode 100644 examples/notebooks/screenshots/nb-image-widget-zfish-frame-50.png create mode 100644 examples/notebooks/screenshots/nb-image-widget-zfish-frame-99.png create mode 100644 examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-gaussian.png create mode 100644 examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-reset.png create mode 100644 examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-max-window-13.png create mode 100644 examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-13.png create mode 100644 examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-5.png create mode 100644 examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50.png create mode 100644 examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-99.png create mode 100644 examples/notebooks/screenshots/nb-image-widget-zfish-grid-init-mean-window-5.png create mode 100644 examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-false.png create mode 100644 examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-true.png create mode 100644 examples/notebooks/screenshots/nb-image-widget-zfish-init-mean-window-5.png create mode 100644 examples/notebooks/zfish_test.npy diff --git a/.gitattributes b/.gitattributes index 24a8e8793..e4a509285 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,3 @@ *.png filter=lfs diff=lfs merge=lfs -text +*.npy filter=lfs diff=lfs merge=lfs -text + diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 08a36a971..fddfae5f4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -74,7 +74,7 @@ jobs: - name: Install llvmpipe and lavapipe for offscreen canvas run: | sudo apt-get update -y -qq - sudo apt-get install --no-install-recommends -y libegl1-mesa libgl1-mesa-dri libxcb-xfixes0-dev mesa-vulkan-drivers git-lfs + sudo apt-get install --no-install-recommends -y ffmpeg libegl1-mesa libgl1-mesa-dri libxcb-xfixes0-dev mesa-vulkan-drivers git-lfs - name: Install dev dependencies run: | python -m pip install --upgrade pip setuptools diff --git a/.github/workflows/screenshots.yml b/.github/workflows/screenshots.yml index 39797b95b..d3cdb919b 100644 --- a/.github/workflows/screenshots.yml +++ b/.github/workflows/screenshots.yml @@ -27,7 +27,7 @@ jobs: - name: Install llvmpipe and lavapipe for offscreen canvas run: | sudo apt-get update -y -qq - sudo apt-get install --no-install-recommends -y libegl1-mesa libgl1-mesa-dri libxcb-xfixes0-dev mesa-vulkan-drivers + sudo apt-get install --no-install-recommends -y ffmpeg libegl1-mesa libgl1-mesa-dri libxcb-xfixes0-dev mesa-vulkan-drivers - name: Install dev dependencies run: | python -m pip install --upgrade pip setuptools @@ -38,6 +38,10 @@ jobs: - name: Show wgpu backend run: python -c "from examples.tests.testutils import wgpu_backend; print(wgpu_backend)" + - name: fetch git lfs files + run: | + git lfs fetch --all + git lfs pull - name: Test examples env: PYGFX_EXPECT_LAVAPIPE: true diff --git a/examples/notebooks/image_widget.ipynb b/examples/notebooks/image_widget.ipynb index d8f91c1be..296126f5e 100644 --- a/examples/notebooks/image_widget.ipynb +++ b/examples/notebooks/image_widget.ipynb @@ -10,7 +10,9 @@ "outputs": [], "source": [ "from fastplotlib.widgets import ImageWidget\n", - "import numpy as np" + "import numpy as np\n", + "\n", + "import imageio.v3 as iio # not a fastplotlib dependency, only used for examples" ] }, { @@ -30,7 +32,8 @@ }, "outputs": [], "source": [ - "a = np.random.rand(512, 512)" + "# image widget only supports grayscale images for now\n", + "a = iio.imread(\"imageio:camera.png\")" ] }, { @@ -63,7 +66,7 @@ "id": "b718162f-9aa6-4091-a7a4-c620676b48bd", "metadata": {}, "source": [ - "### can dynamically change features" + "### Access graphics managed by the image widget" ] }, { @@ -75,7 +78,7 @@ }, "outputs": [], "source": [ - "iw.gridplot[0, 0].graphics[0].cmap = \"gnuplot2\"" + "iw.gridplot[0, 0][\"image_widget_managed\"].cmap = \"gnuplot2\"" ] }, { @@ -95,7 +98,10 @@ }, "outputs": [], "source": [ - "a = np.random.rand(500, 512, 512)" + "movie = iio.imread(\"imageio:cockatoo.mp4\")\n", + "\n", + "# convert RGB movie to grayscale, this could take a minute\n", + "gray_movie = np.dot(movie[..., :3], [0.299, 0.587, 0.114])" ] }, { @@ -107,10 +113,10 @@ }, "outputs": [], "source": [ - "iw2 = ImageWidget(\n", - " data=a, \n", + "iw_movie = ImageWidget(\n", + " data=gray_movie, \n", " slider_dims=[\"t\"],\n", - " cmap=\"gnuplot2\"\n", + " cmap=\"gray\"\n", ")" ] }, @@ -123,7 +129,7 @@ }, "outputs": [], "source": [ - "iw2.show()" + "iw_movie.show(sidecar=True)" ] }, { @@ -146,7 +152,7 @@ "outputs": [], "source": [ "# must be in the form of {dim: (func, window_size)}\n", - "iw2.window_funcs = {\"t\": (np.mean, 13)}" + "iw_movie.window_funcs = {\"t\": (np.mean, 13)}" ] }, { @@ -159,7 +165,7 @@ "outputs": [], "source": [ "# change the winow size\n", - "iw2.window_funcs[\"t\"].window_size = 23" + "iw_movie.window_funcs[\"t\"].window_size = 33" ] }, { @@ -172,7 +178,7 @@ "outputs": [], "source": [ "# change the function\n", - "iw2.window_funcs[\"t\"].func = np.max" + "iw_movie.window_funcs[\"t\"].func = np.max" ] }, { @@ -184,8 +190,8 @@ }, "outputs": [], "source": [ - "# or set it again\n", - "iw2.window_funcs = {\"t\": (np.min, 11)}" + "# or reset it\n", + "iw_movie.window_funcs = None" ] }, { @@ -196,6 +202,26 @@ "### Can also set new data" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "f9ee0e42-3d41-4613-a508-042c6c2c27e3", + "metadata": {}, + "outputs": [], + "source": [ + "new_data = iio.imread(\"imageio:stent.npz\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b85d2e08-1479-46fc-a429-2d7a265493d4", + "metadata": {}, + "outputs": [], + "source": [ + "new_data.shape" + ] + }, { "cell_type": "code", "execution_count": null, @@ -205,8 +231,8 @@ }, "outputs": [], "source": [ - "new_data = np.random.rand(500, 512, 512)\n", - "iw2.set_data(new_data=new_data)" + "iw_movie.set_data(new_data=new_data)\n", + "iw_movie.gridplot[0, 0].auto_scale()# sidecar is optional" ] }, { @@ -214,7 +240,7 @@ "id": "aca22179-1b1f-4c51-97bf-ce2d7044e451", "metadata": {}, "source": [ - "# Gridplot of txy data" + "# Gridplot of tzxy data" ] }, { @@ -226,8 +252,29 @@ }, "outputs": [], "source": [ - "dims = (100, 512, 512)\n", - "data = [np.random.rand(*dims) for i in range(4)]" + "zfish_data = np.load(\"./zfish_test.npy\")\n", + "# snippet of a dataset from Martin Haesemeyer" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b015720c-3de1-4575-80c5-d6eabe4b305f", + "metadata": {}, + "outputs": [], + "source": [ + "# data is tzxy\n", + "zfish_data.shape" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fdff388e-d44c-41ff-a177-801f2695fb88", + "metadata": {}, + "outputs": [], + "source": [ + "n_planes = zfish_data.shape[1]" ] }, { @@ -239,12 +286,10 @@ }, "outputs": [], "source": [ - "iw3 = ImageWidget(\n", - " data=data, \n", - " slider_dims=[\"t\"], \n", - " # dims_order=\"txy\", # you can set this manually if dim order is not the usual\n", - " names=[\"zero\", \"one\", \"two\", \"three\"],\n", + "iw_zfish = ImageWidget(\n", + " data=[zfish_data[:, i] for i in range(n_planes)],\n", " window_funcs={\"t\": (np.mean, 5)},\n", + " names=[f\"plane-{i}\" for i in range(n_planes)],\n", " cmap=\"gnuplot2\", \n", ")" ] @@ -254,7 +299,7 @@ "id": "0721dc40-677e-431d-94c6-da59606199cb", "metadata": {}, "source": [ - "### pan-zoom controllers are all synced in a `ImageWidget`" + "pan-zoom controllers are all synced across subplots in a `ImageWidget`" ] }, { @@ -266,7 +311,7 @@ }, "outputs": [], "source": [ - "iw3.show()" + "iw_zfish.show(sidecar=True)" ] }, { @@ -274,7 +319,7 @@ "id": "82545214-13c4-475e-87da-962117085834", "metadata": {}, "source": [ - "### Index the subplots using the names given to `ImageWidget`" + "Access the subplots using the names given to `ImageWidget`" ] }, { @@ -286,7 +331,7 @@ }, "outputs": [], "source": [ - "iw3.gridplot[\"two\"]" + "iw_zfish.gridplot[\"plane-2\"]" ] }, { @@ -294,7 +339,7 @@ "id": "dc727d1a-681e-4cbf-bfb2-898ceb31cbe0", "metadata": {}, "source": [ - "### change window functions just like before" + "change window functions" ] }, { @@ -306,70 +351,110 @@ }, "outputs": [], "source": [ - "iw3.window_funcs[\"t\"].func = np.max" + "iw_zfish.window_funcs[\"t\"].func = np.max" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ceeab465-bafa-4834-8775-d0e35bc5c880", + "metadata": {}, + "outputs": [], + "source": [ + "iw_zfish.window_funcs = None" ] }, { "cell_type": "markdown", - "id": "3e89c10f-6e34-4d63-9805-88403d487432", + "id": "8e16a503-0213-4e83-abd3-d1fd202cc193", "metadata": {}, "source": [ - "## Gridplot of volumetric data" + "**Frame-apply functions**\n", + "\n", + "These are applied on a frame before being displayed in the `ImageGraphic`" ] }, { "cell_type": "code", "execution_count": null, - "id": "b1587410-a08e-484c-8795-195a413d6374", - "metadata": { - "tags": [] - }, + "id": "9557f0a1-b8fd-4dfe-ac3f-47c90ce78625", + "metadata": {}, "outputs": [], "source": [ - "dims = (256, 256, 5, 100)\n", - "data = [np.random.rand(*dims) for i in range(4)]\n", - "\n", - "iw4 = ImageWidget(\n", - " data=data, \n", - " slider_dims=[\"t\", \"z\"], \n", - " dims_order=\"xyzt\", # example of how you can set this for non-standard orders\n", - " names=[\"zero\", \"one\", \"two\", \"three\"],\n", - " # window_funcs={\"t\": (np.mean, 5)}, # window functions can be slow when indexing multiple dims\n", - " cmap=\"gnuplot2\", \n", - ")" + "# scipy isn't a fastplotlib dependency, it's just used for this example\n", + "from scipy.ndimage import gaussian_filter" ] }, { "cell_type": "code", "execution_count": null, - "id": "3ccea6c6-9580-4720-bce8-a5507cf867a3", - "metadata": { - "tags": [] - }, + "id": "f87d7e33-93bc-46e3-9cd1-6c648b132841", + "metadata": {}, "outputs": [], "source": [ - "iw4.show()" + "iw_zfish.frame_apply = lambda frame: gaussian_filter(frame.astype(np.float32), sigma=3)\n", + "iw_zfish.reset_vmin_vmax()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8b0aa161-89f1-48e4-9976-4a6996f1e7e9", + "metadata": {}, + "outputs": [], + "source": [ + "# remove the frame-apply function\n", + "iw_zfish.frame_apply = None\n", + "iw_zfish.reset_vmin_vmax()" ] }, { "cell_type": "markdown", - "id": "2382809c-4c7d-4da4-9955-71d316dee46a", + "id": "3e89c10f-6e34-4d63-9805-88403d487432", "metadata": {}, "source": [ - "### window functions, can be slow when you have \"t\" and \"z\"" + "## z-sliders\n", + "\n", + "ImageWidget will also give you a slider for \"z\" in addition to \"t\" if necessary. \n", + "\n", + "This example uses the same example data shown above, but displays them in a single subplot and `ImageWidget` provides a z-slider. You can use `window_funcs`, `frame_apply` funcs, etc. There is no difference in `ImageWidget` behavior with the z-slider." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b1587410-a08e-484c-8795-195a413d6374", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "iw_z = ImageWidget(\n", + " data=zfish_data, # you can also provide a list of tzxy arrays\n", + " window_funcs={\"t\": (np.mean, 5)},\n", + " cmap=\"gnuplot2\", \n", + ")" ] }, { "cell_type": "code", "execution_count": null, - "id": "fd4433a9-2add-417c-a618-5891371efae0", + "id": "3ccea6c6-9580-4720-bce8-a5507cf867a3", "metadata": { "tags": [] }, "outputs": [], "source": [ - "iw4.window_funcs = {\"t\": (np.mean, 11)}" + "iw_z.show()" ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "aba0fa3a-f844-4937-8615-adbded02345d", + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { @@ -388,7 +473,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.3" + "version": "3.11.2" } }, "nbformat": 4, diff --git a/examples/notebooks/image_widget_test.ipynb b/examples/notebooks/image_widget_test.ipynb new file mode 100644 index 000000000..6602dfce9 --- /dev/null +++ b/examples/notebooks/image_widget_test.ipynb @@ -0,0 +1,468 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "07019035-83f2-4753-9e7c-628ae439b441", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "from fastplotlib.widgets import ImageWidget\n", + "import numpy as np\n", + "from scipy.ndimage import gaussian_filter\n", + "\n", + "import imageio.v3 as iio" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10b8ab40-944d-472c-9b7e-cae8a129e7ce", + "metadata": {}, + "outputs": [], + "source": [ + "from nb_test_utils import plot_test, notebook_finished " + ] + }, + { + "cell_type": "markdown", + "id": "0a9fe48a-cc24-4ecf-b442-b7425ce338d3", + "metadata": {}, + "source": [ + "# Single image" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "01dd4b84-2960-4c26-8162-f0499698e593", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "a = iio.imread(\"imageio:camera.png\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f9d98a12-9574-4b47-9271-013e659cb93f", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "iw = ImageWidget(\n", + " data=a,\n", + " cmap=\"viridis\"\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e9d86a12-b5f2-4ad7-bd70-4715c80b5ece", + "metadata": {}, + "outputs": [], + "source": [ + "iw.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "920ef882-e5e6-45b6-b002-5ed26aa3134e", + "metadata": {}, + "outputs": [], + "source": [ + "plot_test(\"image-widget-single\", iw.gridplot)\n", + "iw.gridplot[0, 0][\"image_widget_managed\"].cmap = \"gnuplot2\"\n", + "plot_test(\"image-widget-single-gnuplot2\", iw.gridplot)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "26120b76-c7b0-4e23-bdb1-411be1944687", + "metadata": {}, + "outputs": [], + "source": [ + "iw.close()" + ] + }, + { + "cell_type": "markdown", + "id": "ffde9820-b388-4f22-b7fd-d55a2ad421ad", + "metadata": {}, + "source": [ + "# Single image sequence" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2067c88f-2b71-4036-a413-59355fa64292", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "movie = iio.imread(\"imageio:cockatoo.mp4\")\n", + "\n", + "# convert RGB movie to grayscale, this could take a minute\n", + "gray_movie = np.dot(movie[..., :3], [0.299, 0.587, 0.114])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "05d3a905-849b-42ae-82ac-34bdc28c1414", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "iw_movie = ImageWidget(\n", + " data=gray_movie, \n", + " slider_dims=[\"t\"],\n", + " cmap=\"gray\"\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "92df8b8c-8d9f-4111-9b3b-fdab7cc7b3f4", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "iw_movie.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac0a7fc0-0323-4b9e-8c70-3ddc735b7893", + "metadata": {}, + "outputs": [], + "source": [ + "# testing cell ignore\n", + "assert iw_movie.sliders[\"t\"].max == gray_movie.shape[0] - 1\n", + "assert iw_movie.sliders[\"t\"].min == 0\n", + "plot_test(\"image-widget-movie-single-0\", iw_movie.gridplot)\n", + "iw_movie.sliders[\"t\"].value = 50\n", + "plot_test(\"image-widget-movie-single-50\", iw_movie.gridplot)\n", + "iw_movie.sliders[\"t\"].value = 279\n", + "plot_test(\"image-widget-movie-single-279\", iw_movie.gridplot)\n", + "iw_movie.sliders[\"t\"].value = 0\n", + "plot_test(\"image-widget-movie-single-0-reset\", iw_movie.gridplot)\n", + "iw_movie.sliders[\"t\"].value = 50\n", + "iw_movie.window_funcs = {\"t\": (np.mean, 13)}\n", + "# testing cell ignore\n", + "plot_test(\"image-widget-movie-single-50-window-mean-13\", iw_movie.gridplot)\n", + "iw_movie.window_funcs[\"t\"].window_size = 33\n", + "plot_test(\"image-widget-movie-single-50-window-mean-33\", iw_movie.gridplot)\n", + "iw_movie.window_funcs[\"t\"].func = np.max\n", + "plot_test(\"image-widget-movie-single-50-window-max-33\", iw_movie.gridplot)\n", + "iw_movie.window_funcs = None\n", + "plot_test(\"image-widget-movie-single-50-window-reset\", iw_movie.gridplot)\n", + "iw_movie.sliders[\"t\"].value = 0" + ] + }, + { + "cell_type": "markdown", + "id": "c5fb9aaa-b1a8-46b9-81a5-a656183ab16d", + "metadata": {}, + "source": [ + "# Set new data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e1730287-41de-4166-8d00-36ae2daabb47", + "metadata": {}, + "outputs": [], + "source": [ + "new_data = iio.imread(\"imageio:stent.npz\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9010dd43-83c8-4807-8874-71af8ef5c955", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "iw_movie.set_data(new_data=new_data)\n", + "iw_movie.gridplot[0, 0].auto_scale()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "06e6f4fb-fe58-40f0-adf0-191e02cdbd75", + "metadata": {}, + "outputs": [], + "source": [ + "plot_test(\"image-widget-movie-set_data\", iw_movie.gridplot)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e5d55d4e-57f2-4460-b252-7a9d21f7c217", + "metadata": {}, + "outputs": [], + "source": [ + "iw_movie.close()" + ] + }, + { + "cell_type": "markdown", + "id": "d2e6819b-209a-49af-97b7-0b8956023d1a", + "metadata": {}, + "source": [ + "# zfish" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cf9845b6-1cda-403b-96cd-e2b51803c96a", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "zfish_data = np.load(\"./zfish_test.npy\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f7f0315a-29c0-4852-9d49-edaeb3ee45c6", + "metadata": {}, + "outputs": [], + "source": [ + "# data is tzxy\n", + "zfish_data.shape" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "67e840fa-9eb9-4705-ab85-ec981ba29abb", + "metadata": {}, + "outputs": [], + "source": [ + "n_planes = zfish_data.shape[1]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "76535d56-e514-4c16-aa48-a6359f8019d5", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "iw_zfish = ImageWidget(\n", + " data=[zfish_data[:, i] for i in range(n_planes)],\n", + " window_funcs={\"t\": (np.mean, 5)},\n", + " names=[f\"plane-{i}\" for i in range(n_planes)],\n", + " cmap=\"gnuplot2\", \n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "13904849-cbc5-41e7-ad90-d6e8a4fc0077", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "iw_zfish.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "75e8cf08-f1d2-414c-84a2-1ba4c2a01072", + "metadata": {}, + "outputs": [], + "source": [ + "# testing cell ignore\n", + "assert iw_zfish.sliders[\"t\"].max == zfish_data.shape[0] - 1\n", + "assert iw_zfish.sliders[\"t\"].min == 0\n", + "plot_test(\"image-widget-zfish-grid-init-mean-window-5\", iw_zfish.gridplot)\n", + "iw_zfish.sliders[\"t\"].value = 50\n", + "plot_test(\"image-widget-zfish-grid-frame-50-mean-window-5\", iw_zfish.gridplot)\n", + "iw_zfish.window_funcs[\"t\"].window_size = 13\n", + "plot_test(\"image-widget-zfish-grid-frame-50-mean-window-13\", iw_zfish.gridplot)\n", + "iw_zfish.window_funcs = None\n", + "plot_test(\"image-widget-zfish-grid-frame-50\", iw_zfish.gridplot)\n", + "iw_zfish.sliders[\"t\"].value = 99\n", + "plot_test(\"image-widget-zfish-grid-frame-99\", iw_zfish.gridplot)\n", + "iw_zfish.sliders[\"t\"].value = 50\n", + "iw_zfish.window_funcs = {\"t\": (np.max, 13)}\n", + "plot_test(\"image-widget-zfish-grid-frame-50-max-window-13\", iw_zfish.gridplot)\n", + "iw_zfish.window_funcs = None\n", + "iw_zfish.frame_apply = lambda frame: gaussian_filter(frame.astype(np.float32), sigma=3)\n", + "iw_zfish.reset_vmin_vmax()\n", + "plot_test(\"image-widget-zfish-grid-frame-50-frame-apply-gaussian\", iw_zfish.gridplot)\n", + "iw_zfish.frame_apply = None\n", + "iw_zfish.reset_vmin_vmax()\n", + "plot_test(\"image-widget-zfish-grid-frame-50-frame-apply-reset\", iw_zfish.gridplot)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "34499f45-cab3-4abd-832a-a679746b1684", + "metadata": {}, + "outputs": [], + "source": [ + "# reverse planes and test set_data\n", + "iw_zfish.set_data(\n", + " [zfish_data[:, i] for i in range(n_planes - 1, -1, -1)],\n", + " reset_indices=False\n", + ")\n", + "\n", + "plot_test(\"image-widget-zfish-grid-set_data-reset-indices-false\", iw_zfish.gridplot)\n", + "\n", + "iw_zfish.set_data(\n", + " [zfish_data[:, i] for i in range(n_planes - 1, -1, -1)],\n", + " reset_indices=True\n", + ")\n", + "plot_test(\"image-widget-zfish-grid-set_data-reset-indices-true\", iw_zfish.gridplot)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "08501aad-8b56-4ae5-98ff-26942cbb5d67", + "metadata": {}, + "outputs": [], + "source": [ + "iw_zfish.close()" + ] + }, + { + "cell_type": "markdown", + "id": "bf5ff2c3-780c-41dd-91a7-74ef8b87d838", + "metadata": {}, + "source": [ + "## z-sliders" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "87280ff7-5009-46a7-9306-0c1fe03ba4bd", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "iw_z = ImageWidget(\n", + " data=zfish_data, # you can also provide a list of tzxy arrays\n", + " window_funcs={\"t\": (np.mean, 5)},\n", + " cmap=\"gnuplot2\", \n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "94142bdf-78d0-4512-b27a-f5a61b9aba5f", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "iw_z.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "06b66964-b3bf-4545-a0e6-79ddc996a1d0", + "metadata": {}, + "outputs": [], + "source": [ + "# same tests as with the gridplot\n", + "assert iw_z.sliders[\"t\"].max == zfish_data.shape[0] - 1\n", + "assert iw_z.sliders[\"t\"].min == 0\n", + "plot_test(\"image-widget-zfish-init-mean-window-5\", iw_z.gridplot)\n", + "iw_z.sliders[\"t\"].value = 50\n", + "plot_test(\"image-widget-zfish-frame-50-mean-window-5\", iw_z.gridplot)\n", + "iw_z.window_funcs[\"t\"].window_size = 13\n", + "plot_test(\"image-widget-zfish-frame-50-mean-window-13\", iw_z.gridplot)\n", + "iw_z.window_funcs = None\n", + "plot_test(\"image-widget-zfish-frame-50\", iw_z.gridplot)\n", + "iw_z.sliders[\"t\"].value = 99\n", + "plot_test(\"image-widget-zfish-frame-99\", iw_z.gridplot)\n", + "iw_z.sliders[\"t\"].value = 50\n", + "iw_z.window_funcs = {\"t\": (np.max, 13)}\n", + "plot_test(\"image-widget-zfish-frame-50-max-window-13\", iw_z.gridplot)\n", + "iw_z.window_funcs = None\n", + "iw_z.frame_apply = lambda frame: gaussian_filter(frame.astype(np.float32), sigma=3)\n", + "iw_z.reset_vmin_vmax()\n", + "plot_test(\"image-widget-zfish-frame-50-frame-apply-gaussian\", iw_z.gridplot)\n", + "iw_z.frame_apply = None\n", + "iw_z.reset_vmin_vmax()\n", + "plot_test(\"image-widget-zfish-frame-50-frame-apply-reset\", iw_z.gridplot)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b94ae517-4a48-4efe-b85b-7679ae02d233", + "metadata": {}, + "outputs": [], + "source": [ + "iw_z.close()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "870627ef-09d8-44e4-8952-aedb702d1526", + "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.11.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-set_data.png b/examples/notebooks/screenshots/nb-image-widget-movie-set_data.png new file mode 100644 index 000000000..33798861e --- /dev/null +++ b/examples/notebooks/screenshots/nb-image-widget-movie-set_data.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:df2f6e73fdb0946d915f40df1e27f2ae89ba6c6e5d0dadb84406fe8b753735ab +size 34974 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-0-reset.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-0-reset.png new file mode 100644 index 000000000..d09ce18e4 --- /dev/null +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-0-reset.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6b9187c64b7439f629a87a3828cc46a855e1b4609eca52d5484223d2c24e8bf7 +size 62562 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-0.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-0.png new file mode 100644 index 000000000..d09ce18e4 --- /dev/null +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-0.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6b9187c64b7439f629a87a3828cc46a855e1b4609eca52d5484223d2c24e8bf7 +size 62562 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-279.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-279.png new file mode 100644 index 000000000..d4c299683 --- /dev/null +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-279.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:11eb83e3489a8e9c55fcffb0e67f1cf773e538629ddf98e109601749927caa56 +size 72525 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-max-33.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-max-33.png new file mode 100644 index 000000000..ef5db6693 --- /dev/null +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-max-33.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:51bc6a90fba5c67935838b8e44dca477e250cbc4ee2b98ddd69f931e683ec17a +size 63906 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-13.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-13.png new file mode 100644 index 000000000..86287ea9e --- /dev/null +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-13.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1a2318ab1f242d70045b9784337e4421c635b2345331b6a5e8edc0f32ff15f07 +size 54432 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-33.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-33.png new file mode 100644 index 000000000..14c101960 --- /dev/null +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-33.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6c5b5df5b0efb0b3b641129e08429e4a92bd5d2c62b1c63c97337287867d685e +size 50341 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-reset.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-reset.png new file mode 100644 index 000000000..0df0df92b --- /dev/null +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-reset.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f2e5a5734992333165ef2d5f6f810d869e157e59fb7f54c8dd5f413ac750a3fa +size 65067 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50.png new file mode 100644 index 000000000..0df0df92b --- /dev/null +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f2e5a5734992333165ef2d5f6f810d869e157e59fb7f54c8dd5f413ac750a3fa +size 65067 diff --git a/examples/notebooks/screenshots/nb-image-widget-single-gnuplot2.png b/examples/notebooks/screenshots/nb-image-widget-single-gnuplot2.png new file mode 100644 index 000000000..da3033219 --- /dev/null +++ b/examples/notebooks/screenshots/nb-image-widget-single-gnuplot2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:10912f4358278fb26c5efa030a335b0bdb80ebcc2a57fd97838fea3780f9f5d1 +size 143543 diff --git a/examples/notebooks/screenshots/nb-image-widget-single.png b/examples/notebooks/screenshots/nb-image-widget-single.png new file mode 100644 index 000000000..346c1a987 --- /dev/null +++ b/examples/notebooks/screenshots/nb-image-widget-single.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bf23c969c93bc526244360af4babf72a1377a171f55b1f44443dc026faf12631 +size 134432 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-gaussian.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-gaussian.png new file mode 100644 index 000000000..6be1058e9 --- /dev/null +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-gaussian.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:49b65e35bb12e0bce8e753f6700084b2e1100eb6efd0336afc219d9e26972901 +size 64206 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-reset.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-reset.png new file mode 100644 index 000000000..ead51e894 --- /dev/null +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-reset.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:df13f86bb18ad52f962f8944cd780579440b4e1d40941019632d4f46a4d9dc2d +size 50107 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-max-window-13.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-max-window-13.png new file mode 100644 index 000000000..295180169 --- /dev/null +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-max-window-13.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:42676358a0199022972b69263c32a977f90a27ce6c3789d18129c67e9b730679 +size 121369 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-13.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-13.png new file mode 100644 index 000000000..adf129ab2 --- /dev/null +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-13.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f3d1972c575f1659fc2c611a2c703cb408b74ca47e30c2e4564641dc0a6ad887 +size 76550 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-5.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-5.png new file mode 100644 index 000000000..f123f83a9 --- /dev/null +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-5.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e24ea022d48ff41174206e43da911632f38237f6250340f73a7169a43d55f2a6 +size 72238 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50.png new file mode 100644 index 000000000..8c65f7840 --- /dev/null +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:983cf6804561b4287f6acfdac04e2a31810c9d3191108e2da4623cbb852258d5 +size 56707 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-99.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-99.png new file mode 100644 index 000000000..fdc616d07 --- /dev/null +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-99.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4e6f528bf9508397a97e33d463ccd8b3584d3ebd04499b976b4989e001648626 +size 45174 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-gaussian.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-gaussian.png new file mode 100644 index 000000000..66d5557ee --- /dev/null +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-gaussian.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:69f8a085b84ee851ad1579791ff96fe6bc73551d86f376524d1194c43edf819f +size 74941 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-reset.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-reset.png new file mode 100644 index 000000000..3f53f463b --- /dev/null +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-reset.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0616c08a8cf008fbc976638cfb7d76d533547bdb6cbfa56071567dac3c7e703d +size 75619 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-max-window-13.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-max-window-13.png new file mode 100644 index 000000000..e2f0161b7 --- /dev/null +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-max-window-13.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e4f7893445c9f2d26f126a5ac87c1fdf0737b31fec730f754ba4c771eae5ec5b +size 116744 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-13.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-13.png new file mode 100644 index 000000000..fde3c576b --- /dev/null +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-13.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a73e0ef31acf4304bc9a649f0761e0d282eb4517ade89538edfe6b7d8a9b61d0 +size 75487 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-5.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-5.png new file mode 100644 index 000000000..c100dcaf3 --- /dev/null +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-5.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:00ade44cd4478bf77e7bd33ccee64e2905136e35620171f013be7e90ddaa0ba6 +size 79123 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50.png new file mode 100644 index 000000000..08b244755 --- /dev/null +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1eb4d6a4cfbdbfffc198dbe5987f621e5cc3fdcf574699c544a12cf3b28bcdb7 +size 82281 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-99.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-99.png new file mode 100644 index 000000000..6868bf1ff --- /dev/null +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-99.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:758c4f444ab3792c6328e51d619c51f59f27fe2589795cc82bdef7fb5daf57e3 +size 79663 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-init-mean-window-5.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-init-mean-window-5.png new file mode 100644 index 000000000..3bcfe6c24 --- /dev/null +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-init-mean-window-5.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b75c179afde357fd69d7c3e918ad34e072685c536e388b1424596bd493b041e8 +size 81563 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-false.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-false.png new file mode 100644 index 000000000..4a8ee413a --- /dev/null +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-false.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e6d9460835d55d696ffcef1bff7cb7a86672d4c7345112c62050312669055870 +size 66010 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-true.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-true.png new file mode 100644 index 000000000..984f1537c --- /dev/null +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-true.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7b83f6353ce227d76fbf7362218bcc35afa26216e5600cf145cbcce666bb6dd6 +size 66520 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-init-mean-window-5.png b/examples/notebooks/screenshots/nb-image-widget-zfish-init-mean-window-5.png new file mode 100644 index 000000000..2767f8699 --- /dev/null +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-init-mean-window-5.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:130c7a58ceee6ffe0ec58f194bce04e6f0892c97448b43ef9f90ced85c9c10ba +size 62566 diff --git a/examples/notebooks/zfish_test.npy b/examples/notebooks/zfish_test.npy new file mode 100644 index 000000000..61f0474e6 --- /dev/null +++ b/examples/notebooks/zfish_test.npy @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ff73fdbde34cafaf01254b9e72bdc484d865e6c3288ef88fc1953f349fc02152 +size 26214528 diff --git a/fastplotlib/widgets/image.py b/fastplotlib/widgets/image.py index da256207f..522b0a962 100644 --- a/fastplotlib/widgets/image.py +++ b/fastplotlib/widgets/image.py @@ -40,7 +40,8 @@ def _is_arraylike(obj) -> bool: class _WindowFunctions: """Stores window function and window size""" - def __init__(self, func: callable, window_size: int): + def __init__(self, image_widget, func: callable, window_size: int): + self._image_widget = image_widget self._func = None self.func = func @@ -56,6 +57,9 @@ def func(self) -> callable: def func(self, func: callable): self._func = func + # force update + self._image_widget.current_index = self._image_widget.current_index + @property def window_size(self) -> int: """Get or set window size""" @@ -84,6 +88,8 @@ def window_size(self, ws: int): self._window_size = ws + self._image_widget.current_index = self._image_widget.current_index + def __repr__(self): return f"func: {self.func}, window_size: {self.window_size}" @@ -101,7 +107,7 @@ def widget(self): """ Output context, either an ipywidget or QWidget """ - return self.gridplot.widget + return self._output @property def managed_graphics(self) -> List[ImageGraphic]: @@ -185,6 +191,10 @@ def current_index(self) -> Dict[str, int]: @current_index.setter def current_index(self, index: Dict[str, int]): + # ignore if output context has not been created yet + if self.widget is None: + return + if not set(index.keys()).issubset(set(self._current_index.keys())): raise KeyError( f"All dimension keys for setting `current_index` must be present in the widget sliders. " @@ -299,6 +309,9 @@ def __init__( self._names = None + # output context + self._output = None + if isinstance(data, list): # verify that it's a list of np.ndarray if all([_is_arraylike(d) for d in data]): @@ -494,14 +507,14 @@ def __init__( f"`slider_dims` must a , or , you have passed a: {type(slider_dims)}" ) - self.frame_apply: Dict[int, callable] = dict() + self._frame_apply: Dict[int, callable] = dict() if frame_apply is not None: if callable(frame_apply): - self.frame_apply = {0: frame_apply} + self._frame_apply = {0: frame_apply} elif isinstance(frame_apply, dict): - self.frame_apply: Dict[int, callable] = dict.fromkeys( + self._frame_apply: Dict[int, callable] = dict.fromkeys( list(range(len(self.data))) ) @@ -510,7 +523,7 @@ def __init__( if not isinstance(data_ix, int): raise TypeError("`frame_apply` dict keys must be ") try: - self.frame_apply[data_ix] = frame_apply[data_ix] + self._frame_apply[data_ix] = frame_apply[data_ix] except Exception: raise IndexError( f"key index {data_ix} out of bounds for `frame_apply`, the bounds are 0 - {len(self.data)}" @@ -521,14 +534,14 @@ def __init__( f"you have passed a: <{type(frame_apply)}>" ) + # current_index stores {dimension_index: slice_index} for every dimension + self._current_index: Dict[str, int] = {sax: 0 for sax in self.slider_dims} + self._window_funcs = None self.window_funcs = window_funcs self._sliders: Dict[str, Any] = dict() - # current_index stores {dimension_index: slice_index} for every dimension - self._current_index: Dict[str, int] = {sax: 0 for sax in self.slider_dims} - # get max bound for all data arrays for all dimensions self._dims_max_bounds: Dict[str, int] = {k: np.inf for k in self.slider_dims} for _dim in list(self._dims_max_bounds.keys()): @@ -575,6 +588,19 @@ def __init__( self.block_sliders = False self._image_widget_toolbar = None + @property + def frame_apply(self) -> Union[dict, None]: + return self._frame_apply + + @frame_apply.setter + def frame_apply(self, frame_apply: Dict[int, callable]): + if frame_apply is None: + frame_apply = dict() + + self._frame_apply = frame_apply + # force update image graphic + self.current_index = self.current_index + @property def window_funcs(self) -> Dict[str, _WindowFunctions]: """ @@ -591,6 +617,8 @@ def window_funcs(self) -> Dict[str, _WindowFunctions]: def window_funcs(self, sa: Union[int, Dict[str, int]]): if sa is None: self._window_funcs = None + # force frame to update + self.current_index = self.current_index return # for a single dim @@ -606,7 +634,7 @@ def window_funcs(self, sa: Union[int, Dict[str, int]]): dim_str = self.slider_dims[0] self._window_funcs = dict() - self._window_funcs[dim_str] = _WindowFunctions(*sa) + self._window_funcs[dim_str] = _WindowFunctions(self, *sa) # for multiple dims elif isinstance(sa, dict): @@ -636,7 +664,7 @@ def window_funcs(self, sa: Union[int, Dict[str, int]]): if sa[k] is None: self._window_funcs[k] = None else: - self._window_funcs[k] = _WindowFunctions(*sa[k]) + self._window_funcs[k] = _WindowFunctions(self, *sa[k]) else: raise TypeError( @@ -644,6 +672,9 @@ def window_funcs(self, sa: Union[int, Dict[str, int]]): f"You have passed a {type(sa)}. See the docstring." ) + # force frame to update + self.current_index = self.current_index + def _process_indices( self, array: np.ndarray, slice_indices: Dict[Union[int, str], int] ) -> np.ndarray: @@ -750,14 +781,14 @@ def _get_window_indices(self, data_ix, dim, indices_dim): return indices_dim def _process_frame_apply(self, array, data_ix) -> np.ndarray: - if callable(self.frame_apply): - return self.frame_apply(array) + if callable(self._frame_apply): + return self._frame_apply(array) - if data_ix not in self.frame_apply.keys(): + if data_ix not in self._frame_apply.keys(): return array - elif self.frame_apply[data_ix] is not None: - return self.frame_apply[data_ix](array) + elif self._frame_apply[data_ix] is not None: + return self._frame_apply[data_ix](array) return array @@ -876,11 +907,12 @@ def set_data( def show(self, toolbar: bool = True, sidecar: bool = False, sidecar_kwargs: dict = None): """ - Show the widget + Show the widget. Returns ------- OutputContext + ImageWidget just uses the Gridplot output context """ if self.gridplot.canvas.__class__.__name__ == "JupyterWgpuCanvas": self._image_widget_toolbar = IpywidgetImageWidgetToolbar(self) @@ -888,13 +920,15 @@ def show(self, toolbar: bool = True, sidecar: bool = False, sidecar_kwargs: dict elif self.gridplot.canvas.__class__.__name__ == "QWgpuCanvas": self._image_widget_toolbar = QToolbarImageWidget(self) - return self.gridplot.show( + self._output = self.gridplot.show( toolbar=toolbar, sidecar=sidecar, sidecar_kwargs=sidecar_kwargs, add_widgets=[self._image_widget_toolbar] ) + return self._output + def close(self): """Close Widget""" self.gridplot.close() diff --git a/setup.py b/setup.py index f7195461c..8e8977b57 100644 --- a/setup.py +++ b/setup.py @@ -36,7 +36,7 @@ "pytest", "nbmake", "scipy", - "imageio", + "imageio[pyav]", "jupyterlab", "jupyter-rfb>=0.4.1", "ipywidgets>=8.0.0,<9", From 17f09c6a0ac12b49598187fc16d4a90047ccfbdf Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Thu, 16 Nov 2023 02:05:09 -0500 Subject: [PATCH 010/185] histogram calculation fix, now n_edges always == hist (#372) --- fastplotlib/widgets/histogram_lut.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fastplotlib/widgets/histogram_lut.py b/fastplotlib/widgets/histogram_lut.py index 0d8ca9f15..64feb8df6 100644 --- a/fastplotlib/widgets/histogram_lut.py +++ b/fastplotlib/widgets/histogram_lut.py @@ -180,7 +180,8 @@ def _calculate_histogram(self, data): hist_scaled = hist_flanked / (hist_flanked.max() / 100) if edges_flanked.size > hist_scaled.size: - edges_flanked = edges_flanked[:-1] + # we don't care about accuracy here so if it's off by 1-2 bins that's fine + edges_flanked = edges_flanked[:hist_scaled.size] return hist, edges, hist_scaled, edges_flanked From 568163f7e7bcb9c470384d8fb2e422bdc20fab06 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Thu, 16 Nov 2023 02:35:47 -0500 Subject: [PATCH 011/185] forget notebook_finished() call (#373) --- examples/notebooks/image_widget_test.ipynb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/notebooks/image_widget_test.ipynb b/examples/notebooks/image_widget_test.ipynb index 6602dfce9..90747757c 100644 --- a/examples/notebooks/image_widget_test.ipynb +++ b/examples/notebooks/image_widget_test.ipynb @@ -441,7 +441,9 @@ "id": "870627ef-09d8-44e4-8952-aedb702d1526", "metadata": {}, "outputs": [], - "source": [] + "source": [ + "notebook_finished()" + ] } ], "metadata": { From e53067cc07ef8bac4c3eb5bc0cc0e2aa382a6be0 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Fri, 17 Nov 2023 00:34:09 -0500 Subject: [PATCH 012/185] Delete requirements.txt --- requirements.txt | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index d0bc814f2..000000000 --- a/requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -numpy -jupyterlab -jupyter_rfb -pygfx>=0.1.10 -imageio \ No newline at end of file From f6d2e2b02cd3bb2b05fbb361a5d08c3fb61510f8 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Tue, 21 Nov 2023 19:59:42 -0500 Subject: [PATCH 013/185] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0f9ccf547..e0028971f 100644 --- a/README.md +++ b/README.md @@ -167,4 +167,4 @@ WGPU uses Metal instead of Vulkan on Mac. You will need at least Mac OSX 10.13. We welcome contributions! See the contributing guide: https://github.com/kushalkolar/fastplotlib/blob/main/CONTRIBUTING.md -You can also take a look at our [**Roadmap for 2023**](https://github.com/kushalkolar/fastplotlib/issues/55) and [**Issues**](https://github.com/kushalkolar/fastplotlib/issues) for ideas on how to contribute! +You can also take a look at our [**Roadmap for 2024**](https://github.com/kushalkolar/fastplotlib/issues/55) and [**Issues**](https://github.com/kushalkolar/fastplotlib/issues) for ideas on how to contribute! From dece084fa76a28eba6c69e069badc23fdc0cb2c9 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Wed, 22 Nov 2023 23:24:45 -0500 Subject: [PATCH 014/185] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e0028971f..8604d6aeb 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ Notebooks from talk: https://github.com/fastplotlib/fastplotlib-scipy2023 :heavy_check_mark: `wxPython` **Notes:**\ -:heavy_check_mark: You can use a non-blocking `glfw` canvas from a notebook, as long as you're working locally or have a way to forward the remote graphical desktop (such as X11 forwarding).\ +:heavy_check_mark: Non-blocking Qt output is supported in ipython and notebooks by using [`%gui qt`](https://ipython.readthedocs.io/en/stable/interactive/magics.html#magic-gui) before creating plots. This hook only supports pyqt6 at the moment.\ :grey_exclamation: We do not officially support `jupyter notebook` through `jupyter_rfb`, this may change with notebook v7\ :disappointed: [`jupyter_rfb`](https://github.com/vispy/jupyter_rfb) does not work in collab yet, see https://github.com/vispy/jupyter_rfb/pull/77 From c7622818e3ff6eebc8ccde562616dfe1696756c1 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Wed, 22 Nov 2023 23:31:37 -0500 Subject: [PATCH 015/185] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 8604d6aeb..eb64f3cc3 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ [**Installation**](https://github.com/kushalkolar/fastplotlib#installation) | [**GPU Drivers**](https://github.com/kushalkolar/fastplotlib#graphics-drivers) | +[**Documentation**](https://github.com/fastplotlib/fastplotlib#documentation) | [**Examples**](https://github.com/kushalkolar/fastplotlib#examples) | [**Contributing**](https://github.com/kushalkolar/fastplotlib#heart-contributing) From 5f47fd4cff7a40f77ad7cbdad6275cce67d98739 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Sun, 26 Nov 2023 06:25:59 -0500 Subject: [PATCH 016/185] fix bug, skip if hlut not in iw dock (#378) --- fastplotlib/widgets/image.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fastplotlib/widgets/image.py b/fastplotlib/widgets/image.py index 522b0a962..b25785ecf 100644 --- a/fastplotlib/widgets/image.py +++ b/fastplotlib/widgets/image.py @@ -814,8 +814,14 @@ def reset_vmin_vmax_frame(self): ImageGraphic instead of the data in the full data array. For example, if a post-processing function is used, the range of values in the ImageGraphic can be very different from the range of values in the full data array. + + TODO: We could think of applying the frame_apply funcs to a subsample of the entire array to get a better estimate of vmin vmax? """ + for subplot in self.gridplot: + if "histogram_lut" not in subplot.docks["right"]: + continue + hlut = subplot.docks["right"]["histogram_lut"] # set the data using the current image graphic data hlut.set_data(subplot["image_widget_managed"].data()) From 21762a5a1d6d7c3c33770e5819f955c99e807823 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Wed, 29 Nov 2023 21:30:21 -0500 Subject: [PATCH 017/185] cameras and controllers refactor (#382) * reorganize camera and controller initialization, allow setting them, WIP * controllers named sync done, not tested yet * new controllers work, ids also works, changing controllers works * bugfix * update nb examples * update nbs * controller changes for imagewidget * fix type annotation * update example * update example * update quickstart nb * update screenshot * use perspective camera for everything * typo Co-authored-by: Caitlin Lewis <69729525+clewis7@users.noreply.github.com> * typo Co-authored-by: Caitlin Lewis <69729525+clewis7@users.noreply.github.com> * more detailed exception * better names --------- Co-authored-by: Caitlin Lewis <69729525+clewis7@users.noreply.github.com> --- docs/source/quickstart.ipynb | 46 +--- .../desktop/gridplot/gridplot_non_square.py | 2 +- examples/desktop/scatter/scatter_size.py | 14 +- examples/notebooks/gridplot.ipynb | 150 +++--------- examples/notebooks/gridplot_simple.ipynb | 142 +++--------- examples/notebooks/image_widget.ipynb | 14 +- examples/notebooks/lineplot.ipynb | 62 +---- examples/notebooks/scatter.ipynb | 14 +- .../notebooks/scatter_sizes_animation.ipynb | 52 +++-- examples/notebooks/scatter_sizes_grid.ipynb | 26 +-- .../nb-image-widget-movie-set_data.png | 4 +- examples/notebooks/simple.ipynb | 44 +++- fastplotlib/layouts/_defaults.py | 43 ---- fastplotlib/layouts/_gridplot.py | 216 ++++++++++++------ fastplotlib/layouts/_plot.py | 14 +- fastplotlib/layouts/_plot_area.py | 112 ++++++--- fastplotlib/layouts/_subplot.py | 65 +++--- fastplotlib/layouts/_utils.py | 56 +++++ fastplotlib/widgets/image.py | 3 +- 19 files changed, 510 insertions(+), 569 deletions(-) diff --git a/docs/source/quickstart.ipynb b/docs/source/quickstart.ipynb index 6a3afec33..0de4667bf 100644 --- a/docs/source/quickstart.ipynb +++ b/docs/source/quickstart.ipynb @@ -97,21 +97,7 @@ "id": "be5b408f-dd91-4e36-807a-8c22c8d7d216", "metadata": {}, "source": [ - "**In live notebooks or desktop applications, you can use the handle on the bottom right corner of the _canvas_ to resize it. You can also pan and zoom using your mouse!**\n", - "\n", - "By default the origin is on the bottom left, you can click the flip button to flip the y-axis, or use `plot.camera.world.scale_y *= -1`" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9b4e977e-2a7d-4e2b-aee4-cfc36767b3c6", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "plot.camera.world.scale_y *= -1" + "**In live notebooks or desktop applications, you can use the handle on the bottom right corner of the _canvas_ to resize it. You can also pan and zoom using your mouse!**" ] }, { @@ -510,18 +496,6 @@ "plot_rgb.show()" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "7e4b5a30-4293-4ae3-87dc-06a1355bb2c7", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "plot_rgb.camera.world.scale_y *= -1" - ] - }, { "cell_type": "code", "execution_count": null, @@ -1361,7 +1335,7 @@ "outputs": [], "source": [ "# GridPlot of shape 2 x 3 with all controllers synced\n", - "grid_plot = fpl.GridPlot(shape=(2, 3), controllers=\"sync\")\n", + "grid_plot = fpl.GridPlot(shape=(2, 3), controller_ids=\"sync\")\n", "\n", "# Make a random image graphic for each subplot\n", "for subplot in grid_plot:\n", @@ -1549,7 +1523,7 @@ "# pan-zoom controllers for each view\n", "# views are synced if they have the \n", "# same controller ID\n", - "controllers = [\n", + "controller_ids = [\n", " [0, 3, 1], # id each controller with an integer\n", " [2, 2, 3]\n", "]\n", @@ -1564,7 +1538,7 @@ "# Create the grid plot\n", "grid_plot = fpl.GridPlot(\n", " shape=grid_shape,\n", - " controllers=controllers,\n", + " controller_ids=controller_ids,\n", " names=names,\n", ")\n", "\n", @@ -1678,18 +1652,6 @@ "grid_plot[1, 0][\"rand-image\"].vim = 0.1\n", "grid_plot[1, 0][\"rand-image\"].vmax = 0.3" ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8a5753b9-ee71-4ed1-bb0d-52bdb4ea365f", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "grid_plot[1, 0][\"rand-image\"].type" - ] } ], "metadata": { diff --git a/examples/desktop/gridplot/gridplot_non_square.py b/examples/desktop/gridplot/gridplot_non_square.py index a41bcd9f4..fe43a3c04 100644 --- a/examples/desktop/gridplot/gridplot_non_square.py +++ b/examples/desktop/gridplot/gridplot_non_square.py @@ -10,7 +10,7 @@ import imageio.v3 as iio -plot = fpl.GridPlot(shape=(2, 2), controllers="sync") +plot = fpl.GridPlot(shape=(2, 2), controller_ids="sync") # to force a specific framework such as glfw: # plot = fpl.GridPlot(canvas="glfw") diff --git a/examples/desktop/scatter/scatter_size.py b/examples/desktop/scatter/scatter_size.py index 5b6987b7c..2ad995584 100644 --- a/examples/desktop/scatter/scatter_size.py +++ b/examples/desktop/scatter/scatter_size.py @@ -9,16 +9,7 @@ import fastplotlib as fpl # grid with 2 rows and 3 columns -grid_shape = (2,1) - -# pan-zoom controllers for each view -# views are synced if they have the -# same controller ID -controllers = [ - [0], - [0] -] - +grid_shape = (2, 1) # you can give string names for each subplot within the gridplot names = [ @@ -29,7 +20,6 @@ # Create the grid plot plot = fpl.GridPlot( shape=grid_shape, - controllers=controllers, names=names, size=(1000, 1000) ) @@ -53,4 +43,4 @@ if __name__ == "__main__": print(__doc__) - fpl.run() \ No newline at end of file + fpl.run() diff --git a/examples/notebooks/gridplot.ipynb b/examples/notebooks/gridplot.ipynb index cfcae3705..f1ceb2180 100644 --- a/examples/notebooks/gridplot.ipynb +++ b/examples/notebooks/gridplot.ipynb @@ -10,7 +10,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "id": "a635b3b3-33fa-48f0-b1cc-bf83b1e883ab", "metadata": {}, "outputs": [], @@ -21,52 +21,10 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "id": "8de931e2-bdb3-44a3-9538-e0b3965779af", "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "62f67ebf3b00494c826d92fc5b8bbf76", - "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": "b6262056cff84619aea08fc48d00eb79", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "JupyterWgpuCanvas()" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# grid with 2 rows and 3 columns\n", "grid_shape = (2, 3)\n", @@ -74,9 +32,15 @@ "# pan-zoom controllers for each view\n", "# views are synced if they have the \n", "# same controller ID\n", - "controllers = [\n", - " [0, 3, 1], # id each controller with an integer\n", - " [2, 2, 3]\n", + "controller_ids = [\n", + " [0, -3, 1], # id each controller with an integer\n", + " [2, 2, -3]\n", + "]\n", + "\n", + "# another way to set controller_ids\n", + "controller_ids = [\n", + " [\"subplot0\", \"subplot4\"],\n", + " [\"subplot1\", \"subplot2\", \"subplot5\"],\n", "]\n", "\n", "\n", @@ -89,7 +53,7 @@ "# Create the grid plot\n", "grid_plot = GridPlot(\n", " shape=grid_shape,\n", - " controllers=controllers,\n", + " controller_ids=controller_ids,\n", " names=names,\n", ")\n", "\n", @@ -123,24 +87,10 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "id": "2a6f7eb5-776e-42a6-b6c2-c26009a26795", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "subplot0: Subplot @ 0x7fd496b32830\n", - " parent: None\n", - " Graphics:\n", - "\t'rand-image': ImageGraphic @ 0x7fd496b327d0" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# can access subplot by name\n", "grid_plot[\"subplot0\"]" @@ -148,24 +98,10 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "id": "45e83bca-5a44-48ce-874f-9ae9ca444233", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "subplot0: Subplot @ 0x7fd496b32830\n", - " parent: None\n", - " Graphics:\n", - "\t'rand-image': ImageGraphic @ 0x7fd496b327d0" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# can access subplot by index\n", "grid_plot[0, 0]" @@ -182,21 +118,10 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "id": "c8cf9bfd-e0cc-4173-b64e-a9f2c87bb2c6", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'rand-image': ImageGraphic @ 0x7fd496b327d0" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# can access graphic directly via name\n", "grid_plot[\"subplot0\"][\"rand-image\"]" @@ -204,13 +129,13 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "id": "1cfd1d45-8a60-4fc1-b873-46caa966fe6f", "metadata": {}, "outputs": [], "source": [ - "grid_plot[\"subplot0\"][\"rand-image\"].vmin = 0.6\n", - "grid_plot[\"subplot0\"][\"rand-image\"].vmax = 0.8" + "grid_plot[\"subplot0\"][\"rand-image\"].cmap.vmin = 0.6\n", + "grid_plot[\"subplot0\"][\"rand-image\"].cmap.vmax = 0.8" ] }, { @@ -223,40 +148,19 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "id": "2fafe992-4783-40f2-b044-26a2835dd50a", "metadata": {}, "outputs": [], "source": [ - "grid_plot[1, 0][\"rand-image\"].vim = 0.1\n", - "grid_plot[1, 0][\"rand-image\"].vmax = 0.3" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "a025b76c-77f8-4aeb-ac33-5bb6d0bb5a9a", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'image'" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "grid_plot[1, 0][\"rand-image\"].type" + "grid_plot[1, 0][\"rand-image\"].cmap.vim = 0.1\n", + "grid_plot[1, 0][\"rand-image\"].cmap.vmax = 0.3" ] }, { "cell_type": "code", "execution_count": null, - "id": "62584f91-a8ed-4317-98ee-28ad6d1800d6", + "id": "a61e34a5-ee1b-4abb-8718-ec4715ffaa52", "metadata": {}, "outputs": [], "source": [] @@ -278,7 +182,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.5" + "version": "3.11.3" } }, "nbformat": 4, diff --git a/examples/notebooks/gridplot_simple.ipynb b/examples/notebooks/gridplot_simple.ipynb index 8b50b2701..74807f55a 100644 --- a/examples/notebooks/gridplot_simple.ipynb +++ b/examples/notebooks/gridplot_simple.ipynb @@ -10,9 +10,13 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "id": "5171a06e-1bdc-4908-9726-3c1fd45dbb9d", "metadata": { + "ExecuteTime": { + "end_time": "2023-11-26T04:01:19.120171Z", + "start_time": "2023-11-26T04:01:18.618087Z" + }, "tags": [] }, "outputs": [], @@ -23,38 +27,19 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "id": "86a2488f-ae1c-4b98-a7c0-18eae8013af1", "metadata": { + "ExecuteTime": { + "end_time": "2023-11-26T04:01:19.467264Z", + "start_time": "2023-11-26T04:01:19.121813Z" + }, "tags": [] }, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "f9067cd724094b8c8dfecf60208acbfa", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "RFBOutputContext()" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/clewis7/repos/fastplotlib/fastplotlib/graphics/_features/_base.py:34: UserWarning: converting float64 array to float32\n", - " warn(f\"converting {array.dtype} array to float32\")\n" - ] - } - ], + "outputs": [], "source": [ "# GridPlot of shape 2 x 3 with all controllers synced\n", - "grid_plot = GridPlot(shape=(2, 3), controllers=\"sync\")\n", + "grid_plot = GridPlot(shape=(2, 3), controller_ids=\"sync\")\n", "\n", "# Make a random image graphic for each subplot\n", "for subplot in grid_plot:\n", @@ -88,27 +73,22 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, + "id": "52163bc7-2c77-4699-b7b0-e455a0ed7584", + "metadata": {}, + "outputs": [], + "source": [ + "grid_plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, "id": "17c6bc4a-5340-49f1-8597-f54528cfe915", "metadata": { "tags": [] }, - "outputs": [ - { - "data": { - "text/plain": [ - "unnamed: Subplot @ 0x7f15df4f5c50\n", - " parent: fastplotlib.GridPlot @ 0x7f15d3f27890\n", - "\n", - " Graphics:\n", - "\t'rand-img': ImageGraphic @ 0x7f15d3fb5390" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# positional indexing\n", "# row 0 and col 0\n", @@ -125,23 +105,12 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "id": "34130f12-9ef6-43b0-b929-931de8b7da25", "metadata": { "tags": [] }, - "outputs": [ - { - "data": { - "text/plain": [ - "(,)" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "grid_plot[0, 1].graphics" ] @@ -156,7 +125,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "id": "ef8a29a6-b19c-4e6b-a2ba-fb4823c01451", "metadata": { "tags": [] @@ -176,7 +145,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "id": "d6c2fa4b-c634-4dcf-8b61-f1986f7c4918", "metadata": { "tags": [] @@ -189,50 +158,24 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "id": "2f6b549c-3165-496d-98aa-45b96c3de674", "metadata": { "tags": [] }, - "outputs": [ - { - "data": { - "text/plain": [ - "top-right-plot: Subplot @ 0x7f15d3f769d0\n", - " parent: fastplotlib.GridPlot @ 0x7f15d3f27890\n", - "\n", - " Graphics:\n", - "\t'rand-img': ImageGraphic @ 0x7f15b83f7250" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "grid_plot[\"top-right-plot\"]" ] }, { "cell_type": "code", - "execution_count": 12, + "execution_count": null, "id": "be436e04-33a6-4597-8e6a-17e1e5225419", "metadata": { "tags": [] }, - "outputs": [ - { - "data": { - "text/plain": [ - "(0, 2)" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# view its position\n", "grid_plot[\"top-right-plot\"].position" @@ -240,23 +183,12 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "id": "6699cda6-af86-4258-87f5-1832f989a564", "metadata": { "tags": [] }, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# these are really the same\n", "grid_plot[\"top-right-plot\"] is grid_plot[0, 2]" @@ -272,19 +204,19 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, "id": "545b627b-d794-459a-a75a-3fde44f0ea95", "metadata": { "tags": [] }, "outputs": [], "source": [ - "grid_plot[\"top-right-plot\"][\"rand-img\"].vmin = 0.5" + "grid_plot[\"top-right-plot\"][\"rand-img\"].cmap.vmin = 0.5" ] }, { "cell_type": "code", - "execution_count": 15, + "execution_count": null, "id": "36432d5b-b76c-4a2a-a32c-097faf5ab269", "metadata": { "tags": [] diff --git a/examples/notebooks/image_widget.ipynb b/examples/notebooks/image_widget.ipynb index 296126f5e..56d5c8a81 100644 --- a/examples/notebooks/image_widget.ipynb +++ b/examples/notebooks/image_widget.ipynb @@ -235,6 +235,16 @@ "iw_movie.gridplot[0, 0].auto_scale()# sidecar is optional" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "3d323f67-4717-4241-ad84-5091e6caf2cd", + "metadata": {}, + "outputs": [], + "source": [ + "iw_movie.close()" + ] + }, { "cell_type": "markdown", "id": "aca22179-1b1f-4c51-97bf-ce2d7044e451", @@ -296,7 +306,7 @@ }, { "cell_type": "markdown", - "id": "0721dc40-677e-431d-94c6-da59606199cb", + "id": "037d899e-9e7c-4d58-a021-5f5b71e1db90", "metadata": {}, "source": [ "pan-zoom controllers are all synced across subplots in a `ImageWidget`" @@ -473,7 +483,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.2" + "version": "3.11.3" } }, "nbformat": 4, diff --git a/examples/notebooks/lineplot.ipynb b/examples/notebooks/lineplot.ipynb index e156a7150..667cae178 100644 --- a/examples/notebooks/lineplot.ipynb +++ b/examples/notebooks/lineplot.ipynb @@ -12,7 +12,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "id": "9c974494-712e-4981-bae2-a3ee176a6b20", "metadata": {}, "outputs": [], @@ -23,7 +23,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "id": "c3d8f967-f60f-4f0b-b6ba-21b1251b4856", "metadata": {}, "outputs": [], @@ -41,60 +41,10 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "id": "78cffe56-1147-4255-82c1-53cec6bc986a", "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "7993e0a4358f4678a7343b78b3b0b24c", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "RFBOutputContext()" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/kushalk/repos/fastplotlib/fastplotlib/layouts/_base.py:214: UserWarning: `center_scene()` not yet implemented for `PerspectiveCamera`\n", - " warn(\"`center_scene()` not yet implemented for `PerspectiveCamera`\")\n" - ] - }, - { - "data": { - "text/html": [ - "
initial snapshot
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "898a109f489741a5b4624be77bd27db0", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "JupyterWgpuCanvas()" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# grid with 2 rows and 2 columns\n", "shape = (2, 2)\n", @@ -104,7 +54,7 @@ "# same controller ID\n", "# in this example the first view has its own controller\n", "# and the last 3 views are synced\n", - "controllers = [\n", + "controller_ids = [\n", " [0, 1], # id each controller with an integer\n", " [1, 1]\n", "]\n", @@ -113,7 +63,7 @@ "grid_plot = GridPlot(\n", " shape=shape,\n", " cameras='3d', # 3D view for all subplots within the grid\n", - " controllers=controllers\n", + " controller_ids=controller_ids\n", ")\n", "\n", "for i, subplot in enumerate(grid_plot):\n", diff --git a/examples/notebooks/scatter.ipynb b/examples/notebooks/scatter.ipynb index 948403f11..9d7ff099f 100644 --- a/examples/notebooks/scatter.ipynb +++ b/examples/notebooks/scatter.ipynb @@ -78,7 +78,7 @@ "# same controller ID\n", "# you can only sync controllers that use the same camera type\n", "# i.e. you cannot sync between 2d and 3d subplots\n", - "controllers = [\n", + "controller_ids = [\n", " [0, 1],\n", " [1, 0]\n", "]\n", @@ -87,7 +87,7 @@ "grid_plot = GridPlot(\n", " shape=shape,\n", " cameras=cameras,\n", - " controllers=controllers\n", + " controller_ids=controller_ids\n", ")\n", "\n", "for subplot in grid_plot:\n", @@ -163,15 +163,7 @@ { "cell_type": "code", "execution_count": null, - "id": "fb49930f-b795-4b41-bbc6-014a27c2f463", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7d8aac54-4f36-41d4-8e5b-8d8da2f0d17d", + "id": "a18e7a17-c2af-4674-a499-bf5f3b27c8ca", "metadata": {}, "outputs": [], "source": [] diff --git a/examples/notebooks/scatter_sizes_animation.ipynb b/examples/notebooks/scatter_sizes_animation.ipynb index 061f444d6..06a6b11a2 100644 --- a/examples/notebooks/scatter_sizes_animation.ipynb +++ b/examples/notebooks/scatter_sizes_animation.ipynb @@ -2,9 +2,39 @@ "cells": [ { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "5d9f9913391a42af95d4d43d07c17b19", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "RFBOutputContext()" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "9cd08c319b814934a09fd266a1b6322b", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "JupyterOutputContext(children=(JupyterWgpuCanvas(), IpywidgetToolBar(children=(Button(icon='expand-arrows-alt'…" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "from time import time\n", "\n", @@ -21,8 +51,6 @@ " current_time = time()\n", " newPositions = points + np.sin(((current_time / 4) % 1)*np.pi)\n", " plot.graphics[0].data = newPositions\n", - " plot.camera.width = 4*np.max(newPositions[0,:])\n", - " plot.camera.height = 4*np.max(newPositions[1,:])\n", "\n", "def update_sizes():\n", " current_time = time()\n", @@ -30,12 +58,11 @@ " size_delta = sin_sample*size_delta_scales\n", " plot.graphics[0].sizes = min_sizes + size_delta\n", "\n", - "points = np.array([[0,0], \n", - " [1,1], \n", - " [2,2]])\n", "scatter = plot.add_scatter(points, colors=[\"red\", \"green\", \"blue\"], sizes=12)\n", "plot.add_animations(update_positions, update_sizes)\n", - "plot.show(autoscale=True)" + "\n", + "plot.camera.width = 12\n", + "plot.show(autoscale=False)" ] }, { @@ -48,7 +75,7 @@ ], "metadata": { "kernelspec": { - "display_name": "fastplotlib-dev", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -62,10 +89,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.4" - }, - "orig_nbformat": 4 + "version": "3.11.3" + } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/examples/notebooks/scatter_sizes_grid.ipynb b/examples/notebooks/scatter_sizes_grid.ipynb index ff64184f7..e152056c9 100644 --- a/examples/notebooks/scatter_sizes_grid.ipynb +++ b/examples/notebooks/scatter_sizes_grid.ipynb @@ -19,15 +19,6 @@ "# grid with 2 rows and 3 columns\n", "grid_shape = (2,1)\n", "\n", - "# pan-zoom controllers for each view\n", - "# views are synced if they have the \n", - "# same controller ID\n", - "controllers = [\n", - " [0],\n", - " [0]\n", - "]\n", - "\n", - "\n", "# you can give string names for each subplot within the gridplot\n", "names = [\n", " [\"scalar_size\"],\n", @@ -37,7 +28,6 @@ "# Create the grid plot\n", "plot = fpl.GridPlot(\n", " shape=grid_shape,\n", - " controllers=controllers,\n", " names=names,\n", " size=(1000, 1000)\n", ")\n", @@ -59,11 +49,18 @@ "\n", "plot.show()" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { "kernelspec": { - "display_name": "fastplotlib-dev", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -77,10 +74,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.4" - }, - "orig_nbformat": 4 + "version": "3.11.3" + } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-set_data.png b/examples/notebooks/screenshots/nb-image-widget-movie-set_data.png index 33798861e..61c3aeb8c 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-set_data.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-set_data.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:df2f6e73fdb0946d915f40df1e27f2ae89ba6c6e5d0dadb84406fe8b753735ab -size 34974 +oid sha256:60318615e4850d37a4ffae16ca1e3bbf2985ddafd0dd65ba6fae997e1d123d67 +size 31251 diff --git a/examples/notebooks/simple.ipynb b/examples/notebooks/simple.ipynb index 681980d39..f5901080b 100644 --- a/examples/notebooks/simple.ipynb +++ b/examples/notebooks/simple.ipynb @@ -100,7 +100,7 @@ "image_graphic = plot.add_image(data=data, name=\"sample-image\")\n", "\n", "# show the plot\n", - "plot.show()" + "plot.show(sidecar=True)" ] }, { @@ -589,7 +589,7 @@ "\n", "plot_sync.add_animations(update_data_2)\n", "\n", - "plot_sync.show(sidecar=False)" + "plot_sync.show()" ] }, { @@ -704,7 +704,7 @@ "sinc_graphic = plot_l.add_line(data=sinc, thickness=5, colors = colors)\n", "\n", "# show the plot\n", - "plot_l.show(sidecar_kwargs={\"title\": \"lines\", \"layout\": {'width': '800px'}})" + "plot_l.show(sidecar=True, sidecar_kwargs={\"title\": \"lines\"})" ] }, { @@ -1020,6 +1020,14 @@ "plot_l3d.show()" ] }, + { + "cell_type": "markdown", + "id": "29f07af0-cdcb-47cc-bbb3-2fa4449fa084", + "metadata": {}, + "source": [ + "**Use WASD keys and the mouse to move around, just like in a game :D. Use the mouse weel to control the speed of movement.**" + ] + }, { "cell_type": "code", "execution_count": null, @@ -1045,6 +1053,17 @@ "plot_test(\"lines-3d\", plot_l3d)" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "7a6da884-8b40-4ebf-837f-929b3e9cf4c4", + "metadata": {}, + "outputs": [], + "source": [ + "# change the FOV of the persepctive camera\n", + "plot_l3d.camera.fov = 70" + ] + }, { "cell_type": "code", "execution_count": null, @@ -1187,6 +1206,25 @@ "scatter_graphic.data[n_points:n_points * 2, 0] = np.linspace(-40, 0, n_points)" ] }, + { + "cell_type": "markdown", + "id": "5f3e206d-97af-4e07-9969-94f2fdb41004", + "metadata": {}, + "source": [ + "**Switch to a fly controller to move around the plot in 3D!**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c67944ca-52e7-4213-b820-6572cc3f76f0", + "metadata": {}, + "outputs": [], + "source": [ + "plot_s.camera = \"3d\"\n", + "plot_s.controller = \"fly\"" + ] + }, { "cell_type": "code", "execution_count": null, diff --git a/fastplotlib/layouts/_defaults.py b/fastplotlib/layouts/_defaults.py index 9a223855f..b28b04f64 100644 --- a/fastplotlib/layouts/_defaults.py +++ b/fastplotlib/layouts/_defaults.py @@ -1,46 +1,3 @@ -import pygfx -from typing import * -camera_types = { - "2d": pygfx.OrthographicCamera, - "3d": pygfx.PerspectiveCamera, -} -controller_types = { - "2d": pygfx.PanZoomController, - "3d": pygfx.OrbitController, - pygfx.OrthographicCamera: pygfx.PanZoomController, - pygfx.PerspectiveCamera: pygfx.OrbitController, -} - -def create_camera( - camera_type: str, big_camera: bool = False -) -> Union[pygfx.OrthographicCamera, pygfx.PerspectiveCamera]: - camera_type = camera_type.split("-") - - # kinda messy but works for now - if len(camera_type) > 1: - if camera_type[1] == "big": - big_camera = True - - camera_type = camera_type[0] - else: - camera_type = camera_type[0] - - cls = camera_types[camera_type] - - if cls is pygfx.OrthographicCamera: - if big_camera: - return cls(1024, 1024, -8192, 8192) - else: - return cls(1024, 1024) - - else: - return cls() - - -def create_controller(controller_type: str): - controller_type = controller_type.split("-")[0] - - return controller_types[controller_type]() diff --git a/fastplotlib/layouts/_gridplot.py b/fastplotlib/layouts/_gridplot.py index 473196f78..459aca5fd 100644 --- a/fastplotlib/layouts/_gridplot.py +++ b/fastplotlib/layouts/_gridplot.py @@ -1,4 +1,4 @@ -from itertools import product +from itertools import product, chain import numpy as np from typing import * from inspect import getfullargspec @@ -9,8 +9,8 @@ from wgpu.gui.auto import WgpuCanvas from ._frame import Frame -from ._utils import make_canvas_and_renderer -from ._defaults import create_controller +from ._utils import make_canvas_and_renderer, create_controller, create_camera +from ._utils import controller_types as valid_controller_types from ._subplot import Subplot from ._record_mixin import RecordMixin @@ -25,43 +25,47 @@ def to_array(a) -> np.ndarray: return np.array(a) -valid_cameras = ["2d", "2d-big", "3d", "3d-big"] - - class GridPlot(Frame, RecordMixin): def __init__( self, shape: Tuple[int, int], - cameras: Union[np.ndarray, str] = "2d", - controllers: Union[np.ndarray, str] = None, + cameras: Union[str, list, np.ndarray] = "2d", + controller_types: Union[str, list, np.ndarray] = None, + controller_ids: Union[str, list, np.ndarray] = None, canvas: Union[str, WgpuCanvas, pygfx.Texture] = None, renderer: pygfx.WgpuRenderer = None, size: Tuple[int, int] = (500, 300), - **kwargs, + names: Union[list, np.ndarray] = None, ): """ A grid of subplots. Parameters ---------- - shape: tuple of int + shape: (int, int) (n_rows, n_cols) - cameras: np.ndarray or str, optional + cameras: str, list, or np.ndarray, optional | One of ``"2d"`` or ``"3d"`` indicating 2D or 3D cameras for all subplots - | OR - | Array of ``2d`` and/or ``3d`` that specifies the camera type for each subplot + | list/array of ``2d`` and/or ``3d`` that specifies the camera type for each subplot + | list/array of pygfx.PerspectiveCamera instances + + controller_types: str, list or np.ndarray, optional + list or array that specifies the controller type for each subplot, or list/array of + pygfx.Controller instances - controllers: np.ndarray or str, optional + controller_ids: str, list or np.ndarray of int or str ids, optional | If `None` a unique controller is created for each subplot | If "sync" all the subplots use the same controller | If ``numpy.array``, its shape must be the same as ``grid_shape``. This allows custom assignment of controllers - | Example: - | unique controllers for a 2x2 gridplot: np.array([[0, 1], [2, 3]]) - | same controllers for first 2 plots and last 2 plots: np.array([[0, 0, 1], [2, 3, 3]]) + | Example with integers: + | sync first 2 plots, and sync last 2 plots: [[0, 0, 1], [2, 3, 3]] + | Example with str subplot names: + | list of lists of subplot names, each sublist is synced: [[subplot_a, subplot_b], [subplot_f, subplot_c]] + | this syncs subplot_a and subplot_b together; syncs subplot_f and subplot_c together canvas: WgpuCanvas, optional Canvas for drawing @@ -69,70 +73,147 @@ def __init__( renderer: pygfx.Renderer, optional pygfx renderer instance - size: (int, int) + size: (int, int), optional starting size of canvas, default (500, 300) + names: list or array of str, optional + subplot names """ self.shape = shape + if names is not None: + if len(list(chain(*names))) != self.shape[0] * self.shape[1]: + raise ValueError("must provide same number of subplot `names` as specified by gridplot shape") + + self.names = to_array(names).reshape(self.shape) + else: + self.names = None + canvas, renderer = make_canvas_and_renderer(canvas, renderer) if isinstance(cameras, str): - if cameras not in valid_cameras: - raise ValueError( - f"If passing a str, `cameras` must be one of: {valid_cameras}" - ) # create the array representing the views for each subplot in the grid cameras = np.array([cameras] * self.shape[0] * self.shape[1]).reshape( self.shape ) - if isinstance(controllers, str): - if controllers == "sync": - controllers = np.zeros( - self.shape[0] * self.shape[1], dtype=int - ).reshape(self.shape) + # list -> array if necessary + cameras = to_array(cameras).reshape(self.shape) - if controllers is None: - controllers = np.arange(self.shape[0] * self.shape[1]).reshape(self.shape) + if cameras.shape != self.shape: + raise ValueError("Number of cameras does not match the number of subplots") - controllers = to_array(controllers) + # create the cameras + self._cameras = np.empty(self.shape, dtype=object) + for i, j in product(range(self.shape[0]), range(self.shape[1])): + self._cameras[i, j] = create_camera(camera_type=cameras[i, j]) - if controllers.shape != self.shape: - raise ValueError + if controller_ids is None: + # individual controller for each subplot + controller_ids = np.arange(self.shape[0] * self.shape[1]).reshape(self.shape) - cameras = to_array(cameras) + elif isinstance(controller_ids, str): + if controller_ids == "sync": + controller_ids = np.zeros(self.shape, dtype=int) + else: + raise ValueError( + f"`controller_ids` must be one of 'sync', an array/list of subplot names, or an array/list of " + f"integer ids. See the docstring for more details." + ) - self._controllers = np.empty(shape=cameras.shape, dtype=object) + # list controller_ids + elif isinstance(controller_ids, (list, np.ndarray)): + ids_flat = list(chain(*controller_ids)) - if cameras.shape != self.shape: - raise ValueError - - # create controllers if the arguments were integers - if np.issubdtype(controllers.dtype, np.integer): - if not np.all( - np.sort(np.unique(controllers)) - == np.arange(np.unique(controllers).size) - ): - raise ValueError("controllers must be consecutive integers") - - for controller in np.unique(controllers): - cam = np.unique(cameras[controllers == controller]) - if cam.size > 1: + # list of str of subplot names, convert this to integer ids + if all([isinstance(item, str) for item in ids_flat]): + if self.names is None: + raise ValueError("must specify subplot `names` to use list of str for `controller_ids`") + + # make sure each controller_id str is a subplot name + if not all([n in self.names for n in ids_flat]): + raise KeyError( + f"all `controller_ids` strings must be one of the subplot names" + ) + + if len(ids_flat) > len(set(ids_flat)): raise ValueError( - f"Controller id: {controller} has been assigned to multiple different camera types" + "id strings must not appear twice in `controller_ids`" ) - self._controllers[controllers == controller] = create_controller(cam[0]) - # else assume it's a single pygfx.Controller instance or a list of controllers - else: - if isinstance(controllers, pygfx.Controller): - self._controllers = np.array( - [controllers] * shape[0] * shape[1] - ).reshape(shape) + # initialize controller_ids array + ids_init = np.arange(self.shape[0] * self.shape[1]).reshape(self.shape) + + # set id based on subplot position for each synced sublist + for i, sublist in enumerate(controller_ids): + for name in sublist: + ids_init[self.names == name] = -(i + 1) # use negative numbers because why not + + controller_ids = ids_init + + # integer ids + elif all([isinstance(item, (int, np.integer)) for item in ids_flat]): + controller_ids = to_array(controller_ids).reshape(self.shape) + else: - self._controllers = np.array(controllers).reshape(shape) + raise TypeError( + f"list argument to `controller_ids` must be a list of `str` or `int`, " + f"you have passed: {controller_ids}" + ) + + if controller_ids.shape != self.shape: + raise ValueError("Number of controller_ids does not match the number of subplots") + + if controller_types is None: + # `create_controller()` will auto-determine controller for each subplot based on defaults + controller_types = np.array(["default"] * self.shape[0] * self.shape[1]).reshape(self.shape) + + # validate controller types + types_flat = list(chain(*controller_types)) + # str controller_type or pygfx instances + valid_str = list(valid_controller_types.keys()) + ["default"] + valid_instances = tuple(valid_controller_types.values()) + + # make sure each controller type is valid + for controller_type in types_flat: + if controller_type is None: + continue + + if (controller_type not in valid_str) and (not isinstance(controller_type, valid_instances)): + raise ValueError( + f"You have passed an invalid controller type, valid controller_types arguments are:\n" + f"{valid_str} or instances of {[c.__name__ for c in valid_instances]}" + ) + + controller_types = to_array(controller_types).reshape(self.shape) + + # make the real controllers for each subplot + self._controllers = np.empty(shape=self.shape, dtype=object) + for cid in np.unique(controller_ids): + cont_type = controller_types[controller_ids == cid] + if np.unique(cont_type).size > 1: + raise ValueError( + "Multiple controller types have been assigned to the same controller id. " + "All controllers with the same id must use the same type of controller." + ) + + cont_type = cont_type[0] + + # get all the cameras that use this controller + cams = self._cameras[controller_ids == cid].ravel() + + if cont_type == "default": + # hacky fix for now because of how `create_controller()` works + cont_type = None + _controller = create_controller(controller_type=cont_type, camera=cams[0]) + + self._controllers[controller_ids == cid] = _controller + + # add the other cameras that go with this controller + if cams.size > 1: + for cam in cams[1:]: + _controller.add_camera(cam) if canvas is None: canvas = WgpuCanvas() @@ -140,13 +221,6 @@ def __init__( if renderer is None: renderer = pygfx.renderers.WgpuRenderer(canvas) - if "names" in kwargs.keys(): - self.names = to_array(kwargs["names"]) - if self.names.shape != self.shape: - raise ValueError - else: - self.names = None - self._canvas = canvas self._renderer = renderer @@ -158,7 +232,7 @@ def __init__( for i, j in self._get_iterator(): position = (i, j) - camera = cameras[i, j] + camera = self._cameras[i, j] controller = self._controllers[i, j] if self.names is not None: @@ -304,5 +378,15 @@ def __next__(self) -> Subplot: pos = self._current_iter.__next__() return self._subplots[pos] + def __str__(self): + return f"{self.__class__.__name__} @ {hex(id(self))}" + def __repr__(self): - return f"fastplotlib.{self.__class__.__name__} @ {hex(id(self))}\n" + newline = "\n\t" + + return ( + f"fastplotlib.{self.__class__.__name__} @ {hex(id(self))}\n" + f" Subplots:\n" + f"\t{newline.join(subplot.__str__() for subplot in self)}" + f"\n" + ) diff --git a/fastplotlib/layouts/_plot.py b/fastplotlib/layouts/_plot.py index 5aa04bb76..34027a276 100644 --- a/fastplotlib/layouts/_plot.py +++ b/fastplotlib/layouts/_plot.py @@ -11,10 +11,10 @@ class Plot(Subplot, Frame, RecordMixin): def __init__( self, - canvas: WgpuCanvas = None, + canvas: Union[str, WgpuCanvas] = None, renderer: pygfx.WgpuRenderer = None, - camera: str = "2d", - controller: Union[pygfx.PanZoomController, pygfx.OrbitController] = None, + camera: Union[str, pygfx.PerspectiveCamera] = "2d", + controller: Union[str, pygfx.Controller] = None, size: Tuple[int, int] = (500, 300), **kwargs, ): @@ -29,12 +29,14 @@ def __init__( renderer: pygfx.Renderer, optional pygfx renderer instance - camera:str, optional + camera: str or pygfx.PerspectiveCamera, optional | One of ``"2d"`` or ``"3d"`` indicating 2D or 3D camera - controller: None, PanZoomController or OrbitOrthoController, optional + controller: str or pygfx.Controller, optional Usually ``None``, you can pass an existing controller from another - ``Plot`` or ``Subplot`` within a ``GridPlot`` to synchronize them. + ``Plot`` or ``Subplot`` to synchronize them. + + You can also pass str arguments of valid controller names, see Subplot docstring for valid names size: (int, int) starting size of canvas, default (500, 300) diff --git a/fastplotlib/layouts/_plot_area.py b/fastplotlib/layouts/_plot_area.py index dec5d891e..9522832d3 100644 --- a/fastplotlib/layouts/_plot_area.py +++ b/fastplotlib/layouts/_plot_area.py @@ -6,18 +6,10 @@ import numpy as np import pygfx -from pygfx import ( - Scene, - OrthographicCamera, - PerspectiveCamera, - PanZoomController, - OrbitController, - Viewport, - WgpuRenderer, -) from pylinalg import vec_transform, vec_unproject from wgpu.gui.auto import WgpuCanvas +from ._utils import create_camera, create_controller from ..graphics._base import Graphic from ..graphics.selectors._base_selector import BaseSelector @@ -33,11 +25,11 @@ def __init__( self, parent, position: Any, - camera: Union[OrthographicCamera, PerspectiveCamera], - controller: Union[PanZoomController, OrbitController], - scene: Scene, + camera: Union[pygfx.PerspectiveCamera], + controller: Union[pygfx.Controller], + scene: pygfx.Scene, canvas: WgpuCanvas, - renderer: WgpuRenderer, + renderer: pygfx.WgpuRenderer, name: str = None, ): """ @@ -53,25 +45,23 @@ def __init__( typical use will be for ``subplots`` in a ``gridplot``, position would correspond to the ``[row, column]`` location of the ``subplot`` in its ``gridplot`` - camera: pygfx OrthographicCamera or pygfx PerspectiveCamera - ``OrthographicCamera`` type is used to visualize 2D content and ``PerspectiveCamera`` type is used to view - 3D content, used to view the scene + camera: pygfx.PerspectiveCamera + Use perspective camera for both perspective and orthographic views. Set fov = 0 for orthographic mode. - controller: pygfx PanZoomController or pygfx OrbitController - ``PanZoomController`` type is used for 2D pan-zoom camera control and ``OrbitController`` type is used for - rotating the camera around a center position, used to control the camera + controller: pygfx.Controller + One of the pygfx controllers, panzoom, fly, orbit, or trackball - scene: pygfx Scene + scene: pygfx.Scene represents the root of a scene graph, will be viewed by the given ``camera`` canvas: WgpuCanvas provides surface on which a scene will be rendered - renderer: WgpuRenderer - object used to render scenes using wgpu + renderer: pygfx.WgpuRenderer + renders the scene onto the canvas name: str, optional - name of ``subplot`` or ``plot`` subclass being instantiated + name this ``subplot`` or ``plot`` """ @@ -82,14 +72,14 @@ def __init__( self._canvas = canvas self._renderer = renderer if parent is None: - self._viewport: Viewport = Viewport(renderer) + self._viewport: pygfx.Viewport = pygfx.Viewport(renderer) else: - self._viewport = Viewport(parent.renderer) + self._viewport = pygfx.Viewport(parent.renderer) self._camera = camera self._controller = controller - self.controller.add_camera(self.camera) + self.controller.add_camera(self._camera) self.controller.register_events( self.viewport, ) @@ -126,7 +116,7 @@ def position(self) -> Union[Tuple[int, int], Any]: return self._position @property - def scene(self) -> Scene: + def scene(self) -> pygfx.Scene: """The Scene where Graphics lie in this plot area""" return self._scene @@ -136,26 +126,82 @@ def canvas(self) -> WgpuCanvas: return self._canvas @property - def renderer(self) -> WgpuRenderer: + def renderer(self) -> pygfx.WgpuRenderer: """Renderer associated to the plot area""" return self._renderer @property - def viewport(self) -> Viewport: + def viewport(self) -> pygfx.Viewport: """The rectangular area of the renderer associated to this plot area""" return self._viewport @property - def camera(self) -> Union[OrthographicCamera, PerspectiveCamera]: + def camera(self) -> pygfx.PerspectiveCamera: """camera used to view the scene""" return self._camera + @camera.setter + def camera(self, new_camera: Union[str, pygfx.PerspectiveCamera]): + # user wants to set completely new camera, remove current camera from controller + if isinstance(new_camera, pygfx.PerspectiveCamera): + self.controller.remove_camera(self._camera) + # add new camera to controller + self.controller.add_camera(new_camera) + + self._camera = new_camera + + # modify FOV if necessary + elif isinstance(new_camera, str): + if new_camera == "2d": + self._camera.fov = 0 + + elif new_camera == "3d": + # orthographic -> perspective only if fov = 0, i.e. if camera is in ortho mode + # otherwise keep same FOV + if self._camera.fov == 0: + self._camera.fov = 50 + + else: + raise ValueError("camera must be one of '2d', '3d' or a pygfx.PerspectiveCamera instance") + else: + raise ValueError("camera must be one of '2d', '3d' or a pygfx.PerspectiveCamera instance") + # in the future we can think about how to allow changing the controller @property - def controller(self) -> Union[PanZoomController, OrbitController]: - """controller used to control camera""" + def controller(self) -> pygfx.Controller: + """controller used to control the camera""" return self._controller + @controller.setter + def controller(self, new_controller: Union[str, pygfx.Controller]): + new_controller = create_controller(new_controller, self._camera) + + cameras_list = list() + + # remove all the cameras associated to this controller + for camera in self._controller.cameras: + self._controller.remove_camera(camera) + cameras_list.append(camera) + + # add the associated cameras to the new controller + for camera in cameras_list: + new_controller.add_camera(camera) + + new_controller.register_events( + self.viewport + ) + + # TODO: monkeypatch until we figure out a better + # pygfx plans on refactoring viewports anyways + if self.parent is not None: + if self.parent.__class__.__name__ == "GridPlot": + for subplot in self.parent: + if subplot.camera in cameras_list: + new_controller.register_events(subplot.viewport) + subplot._controller = new_controller + + self._controller = new_controller + @property def graphics(self) -> Tuple[Graphic, ...]: """Graphics in the plot area. Always returns a proxy to the Graphic instances.""" @@ -663,7 +709,7 @@ def __repr__(self): return ( f"{self}\n" - f" parent: {self.parent}\n" + f" parent: {self.parent.__str__()}\n" f" Graphics:\n" f"\t{newline.join(graphic.__repr__() for graphic in self.graphics)}" f"\n" diff --git a/fastplotlib/layouts/_subplot.py b/fastplotlib/layouts/_subplot.py index c178c0fca..e9eae7603 100644 --- a/fastplotlib/layouts/_subplot.py +++ b/fastplotlib/layouts/_subplot.py @@ -2,22 +2,13 @@ import numpy as np -from pygfx import ( - Scene, - OrthographicCamera, - PanZoomController, - OrbitController, - AxesHelper, - GridHelper, - WgpuRenderer, - Texture, -) +import pygfx + from wgpu.gui.auto import WgpuCanvas from ..graphics import TextGraphic -from ._utils import make_canvas_and_renderer +from ._utils import make_canvas_and_renderer, create_camera, create_controller from ._plot_area import PlotArea -from ._defaults import create_camera, create_controller from .graphic_methods_mixin import GraphicMethodsMixin @@ -27,12 +18,11 @@ def __init__( parent: Any = None, position: Tuple[int, int] = None, parent_dims: Tuple[int, int] = None, - camera: str = "2d", - controller: Union[PanZoomController, OrbitController] = None, - canvas: Union[str, WgpuCanvas, Texture] = None, - renderer: WgpuRenderer = None, + camera: Union[str, pygfx.PerspectiveCamera] = "2d", + controller: Union[str, pygfx.Controller] = None, + canvas: Union[str, WgpuCanvas, pygfx.Texture] = None, + renderer: pygfx.WgpuRenderer = None, name: str = None, - **kwargs, ): """ General plot object that composes a ``Gridplot``. Each ``Gridplot`` instance will have [n rows, n columns] @@ -43,21 +33,25 @@ def __init__( Parameters ---------- - position: int tuple, optional + parent: Any + parent GridPlot instance + + position: (int, int), optional corresponds to the [row, column] position of the subplot within a ``Gridplot`` - parent_dims: int tuple, optional + parent_dims: (int, int), optional dimensions of the parent ``GridPlot`` - camera: str, default '2d' - indicates the kind of pygfx camera that will be instantiated, '2d' uses pygfx ``OrthographicCamera`` and - '3d' uses pygfx ``PerspectiveCamera`` + camera: str or pygfx.PerspectiveCamera, default '2d' + indicates the FOV for the camera, '2d' sets ``fov = 0``, '3d' sets ``fov = 50``. + ``fov`` can be changed at any time. - controller: PanZoomController or OrbitOrthoController, optional - ``PanZoomController`` type is used for 2D pan-zoom camera control and ``OrbitController`` type is used for - rotating the camera around a center position, used to control the camera + controller: str or pygfx.Controller, optional + | if ``None``, uses a PanZoomController for "2d" camera or FlyController for "3d" camera. + | if ``str``, must be one of: `"panzoom", "fly", "trackball", or "orbit"`. + | also accepts a pygfx.Controller instance - canvas: WgpuCanvas, Texture, or one of "jupyter", "glfw", "qt", optional + canvas: one of "jupyter", "glfw", "qt", WgpuCanvas, or pygfx.Texture, optional Provides surface on which a scene will be rendered. Can optionally provide a WgpuCanvas instance or a str to force the PlotArea to use a specific canvas from one of the following options: "jupyter", "glfw", "qt". Can also provide a pygfx Texture to render to. @@ -82,25 +76,26 @@ def __init__( self.nrows, self.ncols = parent_dims - if controller is None: - controller = create_controller(camera) + camera = create_camera(camera) + + controller = create_controller(controller_type=controller, camera=camera) self._docks = dict() self.spacing = 2 - self._axes: AxesHelper = AxesHelper(size=100) + self._axes: pygfx.AxesHelper = pygfx.AxesHelper(size=100) for arrow in self._axes.children: self._axes.remove(arrow) - self._grid: GridHelper = GridHelper(size=100, thickness=1) + self._grid: pygfx.GridHelper = pygfx.GridHelper(size=100, thickness=1) super(Subplot, self).__init__( parent=parent, position=position, - camera=create_camera(camera), + camera=camera, controller=controller, - scene=Scene(), + scene=pygfx.Scene(), canvas=canvas, renderer=renderer, name=name, @@ -221,9 +216,9 @@ def __init__( super(Dock, self).__init__( parent=parent, position=position, - camera=OrthographicCamera(), - controller=PanZoomController(), - scene=Scene(), + camera=pygfx.OrthographicCamera(), + controller=pygfx.PanZoomController(), + scene=pygfx.Scene(), canvas=parent.canvas, renderer=parent.renderer, ) diff --git a/fastplotlib/layouts/_utils.py b/fastplotlib/layouts/_utils.py index e8201d073..7db1d84c4 100644 --- a/fastplotlib/layouts/_utils.py +++ b/fastplotlib/layouts/_utils.py @@ -1,5 +1,6 @@ from typing import * +import pygfx from pygfx import WgpuRenderer, Texture # default auto-determined canvas @@ -88,3 +89,58 @@ def make_canvas_and_renderer( renderer = WgpuRenderer(canvas) return canvas, renderer + + +def create_camera( + camera_type: Union[pygfx.PerspectiveCamera, str], +) -> pygfx.PerspectiveCamera: + if isinstance(camera_type, pygfx.PerspectiveCamera): + return camera_type + + elif camera_type == "2d": + # use perspective for orthographic, makes it easier to then switch to controllers that make sense with fov > 0 + return pygfx.PerspectiveCamera(fov=0) + + elif camera_type == "3d": + return pygfx.PerspectiveCamera() + + else: + raise ValueError( + "camera must be one of: '2d', '3d' or an instance of pygfx.PerspectiveCamera" + ) + + +controller_types = { + "fly": pygfx.FlyController, + "panzoom": pygfx.PanZoomController, + "trackball": pygfx.TrackballController, + "orbit": pygfx.OrbitController, +} + + +def create_controller( + controller_type: Union[pygfx.Controller, None, str], + camera: pygfx.PerspectiveCamera, +) -> pygfx.Controller: + """ + Creates the controllers and adds the camera to it. + """ + if isinstance(controller_type, pygfx.Controller): + controller_type.add_camera(camera) + return controller_type + + if controller_type is None: + # default controllers + if camera.fov == 0: + # default for orthographic + return pygfx.PanZoomController(camera) + else: + return pygfx.FlyController(camera) + + if controller_type not in controller_types.keys(): + raise KeyError( + f"controller must be a valid pygfx.Controller or one of: " + f"{list(controller_types.keys())}, you have passed: {controller_type}" + ) + + return controller_types[controller_type](camera) diff --git a/fastplotlib/widgets/image.py b/fastplotlib/widgets/image.py index b25785ecf..a3f6335c9 100644 --- a/fastplotlib/widgets/image.py +++ b/fastplotlib/widgets/image.py @@ -550,7 +550,7 @@ def __init__( self._dims_max_bounds[_dim], array.shape[order.index(_dim)] ) - grid_plot_kwargs_default = {"controllers": "sync"} + grid_plot_kwargs_default = {"controller_ids": "sync"} if grid_plot_kwargs is None: grid_plot_kwargs = dict() @@ -891,6 +891,7 @@ def set_data( frame = self._process_frame_apply(frame, i) new_graphic = ImageGraphic(data=frame, name="image_widget_managed") subplot.insert_graphic(graphic=new_graphic) + subplot.docks["right"]["histogram_lut"].image_graphic = new_graphic if new_array.ndim > 2: # to set max of time slider, txy or tzxy From 2873a677462551f47881e4c6dad637165f5230f6 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Thu, 30 Nov 2023 13:52:49 -0500 Subject: [PATCH 018/185] Update VERSION --- fastplotlib/VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastplotlib/VERSION b/fastplotlib/VERSION index a0ad4fd5d..d9958b371 100644 --- a/fastplotlib/VERSION +++ b/fastplotlib/VERSION @@ -1 +1 @@ -0.1.0.a15 +0.1.0.a16 From 96217404837f078f643180691fba3e7a5af35532 Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Mon, 4 Dec 2023 19:32:58 -0500 Subject: [PATCH 019/185] update contrib (#391) --- .gitignore | 1 + CONTRIBUTING.md | 9 ++++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index f87eb1c51..9857fe4b2 100644 --- a/.gitignore +++ b/.gitignore @@ -131,3 +131,4 @@ dmypy.json # Pycharm .idea/ +examples/desktop/diffs/*.png diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7bf5c69ea..23810b956 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,7 +8,7 @@ Contributions are welcome! :smile: ![image](https://github.com/kushalkolar/fastplotlib/assets/9403332/82612021-37b2-48dd-b7e4-01a919535c17) -2. Clone the repo and install according to the development instructions. Replace the `YOUR_ACCOUNT` in the repo URL to the fork on your account: +2. Clone the repo and install according to the development instructions. Replace the `YOUR_ACCOUNT` in the repo URL to the fork on your account. Note that fastplotlib uses [git-lfs](https://git-lfs.com) for storing large files, so you will need to [install it](https://github.com/git-lfs/git-lfs#installing) before cloning the repo. ```bash git clone https://github.com/YOUR_ACCOUNT/fastplotlib.git @@ -18,11 +18,14 @@ cd fastplotlib pip install -e ".[notebook,docs,tests] ``` +> If you cloned before installing `git-lfs`, you can run `git lfs pull` at any +> time to properly download files. + 3. Checkout the `main` branch, and then checkout your feature or bug fix branch, and run tests: > **Warning** -> Do not commit or add any changes from `examples/screenshots` or `examples/diffs`. -> If you are creating new test examples that generate or change screenshots please post an issue on the repo and we will help you. The screenshots will be generated on github actions servers, which you can then copy into the screenshots dir. :) +> Do not commit or add any changes from `examples/desktop/screenshots`. +> If you are creating new test examples that generate or change screenshots please post an issue on the repo and we will help you. The screenshots will be generated on github actions servers, which you can then copy into the screenshots dir. :) ```bash cd fastplotlib From e45d8f8d00b1c9b35d3bd923b7fc89512da115b2 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Fri, 8 Dec 2023 02:20:21 -0500 Subject: [PATCH 020/185] Update README.md --- README.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index eb64f3cc3..0c15e916d 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ [**Examples**](https://github.com/kushalkolar/fastplotlib#examples) | [**Contributing**](https://github.com/kushalkolar/fastplotlib#heart-contributing) -A fast plotting library built using the [`pygfx`](https://github.com/pygfx/pygfx) rendering engine that can utilize [Vulkan](https://en.wikipedia.org/wiki/Vulkan), [DX12](https://en.wikipedia.org/wiki/DirectX#DirectX_12), or [Metal](https://developer.apple.com/metal/) via WGPU, so it is very fast! We also aim to be an expressive plotting library that enables rapid prototyping for large scale explorative scientific visualization. +Next-gen plotting library built using the [`pygfx`](https://github.com/pygfx/pygfx) rendering engine that can utilize [Vulkan](https://en.wikipedia.org/wiki/Vulkan), [DX12](https://en.wikipedia.org/wiki/DirectX#DirectX_12), or [Metal](https://developer.apple.com/metal/) via WGPU, so it is very fast! We also aim to be an expressive plotting library that enables rapid prototyping for large scale explorative scientific visualization. ![scipy-fpl](https://github.com/fastplotlib/fastplotlib/assets/9403332/b981a54c-05f9-443f-a8e4-52cd01cd802a) @@ -70,7 +70,7 @@ pip install fastplotlib pip install "fastplotlib[notebook]" ``` -**Optional: install `simplejpeg` for much faster notebook visualization, you will need C compilers and [libjpeg-turbo](https://libjpeg-turbo.org/) to install it:** +**Optional: install `simplejpeg` for much faster notebook visualization** ```bash pip install simplejpeg @@ -96,7 +96,8 @@ pip install -e ".[notebook,docs,tests]" > `fastplotlib` and `pygfx` are fast evolving, you may require the latest `pygfx` and `fastplotlib` from github to use the examples in the main branch. Note that `fastplotlib` code is basically identical between desktop and notebook usage. The differences are: -- Running in `Qt` or `glfw` require a `fastplotlib.run()` call (which is really just a `wgpu` `run()` call) +- Running in `glfw` requires a `fastplotlib.run()` call (which is really just a `wgpu` `run()` call) +- To use it in `Qt` you must encapsulate it within a `QApplication`, see `examples/qt` - Notebooks plots have ipywidget-based toolbars and widgets 😄 ### Desktop examples using `glfw` or `Qt` @@ -129,7 +130,7 @@ pip install imageio ### Video -You can watch our SciPy 2023 talk if you prefer watching demos: https://github.com/fastplotlib/fastplotlib#scipy-talk +Our SciPy 2023 talk walks through numerous demos: https://github.com/fastplotlib/fastplotlib#scipy-talk ## Graphics drivers @@ -140,9 +141,9 @@ For more information see: https://wgpu-py.readthedocs.io/en/stable/start.html#pl ### Windows: Vulkan drivers should be installed by default on Windows 11, but you will need to install your GPU manufacturer's driver package (Nvidia or AMD). If you have an integrated GPU within your CPU, you might still need to install a driver package too, check your CPU manufacturer's info. -We also recommend installing C compilers so that you can install `simplejpeg` which improves remote frame buffer performance in notebooks. - ### Linux: +You will generally need a linux distro that is from ~2020 or newer (ex. Ubuntu 18.04 won't work), this is due to the `glibc` requirements of the `wgpu-native` binary. + Debian based distros: ```bash From 5053f02582d68c18d0bec5b9b3c4c5371e785139 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Fri, 8 Dec 2023 02:31:40 -0500 Subject: [PATCH 021/185] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0c15e916d..568b0ee4a 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ pip install fastplotlib pip install "fastplotlib[notebook]" ``` -**Optional: install `simplejpeg` for much faster notebook visualization** +**Recommended: install `simplejpeg` for much faster notebook visualization, this requires you to first install [libjpeg-turbo](https://libjpeg-turbo.org/)** ```bash pip install simplejpeg From 65ccc8fb537d7f73806cdf6543bd802add2f8bf1 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Thu, 15 Feb 2024 00:57:30 -0500 Subject: [PATCH 022/185] add faq (#400) --- docs/source/index.rst | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/source/index.rst b/docs/source/index.rst index 7e1d3865a..a6d36dd4f 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -39,12 +39,23 @@ For installation please see the instructions on GitHub: https://github.com/kushalkolar/fastplotlib#installation +FAQ +=== + +1. Axes, axis, ticks, labels, legends + +A: They are on the `roadmap `_ and expected by summer 2024 :) + +2. Why the parrot logo? + +A: The logo is a `swift parrot `_, they are the fastest species of parrot and they are colorful like fastplotlib visualizations :D + Contributing ============ Contributions are welcome! See the contributing guide on GitHub: https://github.com/kushalkolar/fastplotlib/blob/master/CONTRIBUTING.md. -Also take a look at the `Roadmap 2023 `_ for future plans or ways in which you could contribute. +Also take a look at the `Roadmap 2025 `_ for future plans or ways in which you could contribute. Indices and tables ================== From 1d72a15966272a726e099a9cd5896f1a01ca75c0 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Sat, 17 Feb 2024 01:25:53 -0500 Subject: [PATCH 023/185] Create GOVERNANCE.md --- GOVERNANCE.md | 119 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 GOVERNANCE.md diff --git a/GOVERNANCE.md b/GOVERNANCE.md new file mode 100644 index 000000000..5102707ce --- /dev/null +++ b/GOVERNANCE.md @@ -0,0 +1,119 @@ +# fastplotlib governance + +The governance of fastplotlib applies to all fastplotlib related activities. This includes the fastplotlib github organization, all repositories under the fastplotlib github organization, as well as any events or workshops organized by members of the fastplotlib Leadership Team. + +## Mission + +Leverage new graphics APIs and modern GPU hardware to create fast and interactive scientific visualizations using an expressive and elegant API. + +## Leadership team + +### Maintainers + +The maintainers are the core developers of fastplotlib and together have a complete understanding of the codebase. + +1. [Kushal Kolar](https://github.com/kushalkolar) +1. [Caitlin Lewis](https://github.com/clewis7) + + +### Advisory Committee + +The advisory committee hold a significant stake in fastplotlib as determined by the **Maintainers**. The responsibilities of the **Advisory Committee** are to 1) attend a yearly roadmap meeting, 2) be available for conflict resolution. + +1. Eric Thomson +1. Guillaume Viejo +1. Andrea Giovannucci +1. John Pearson + +### Neutral moderator + +No voting power, has no stake in the fastplotlib project + +* Reagan Bullins + +### Voting power distribution + +Maintainers: 50% + +Advisory Committee: 50% + +Veto: Any vote can be vetoed by a unanimous vote within the maintainers. + +Note that currently the voting power is primarily held by the maintainers - Kushal Kolar & Caitlin Lewis. This is intentional since the library is in an early stage. Knowledge of the codebase and its inner workings are predominantly held by Kushal & Caitlin, and nobody else. + +### Voting Process + +1. For the purpose of conflict resolution the leadership team must be convened by the neutral moderator. In all other situations the leadership team may be convened by one of the maintainers. +1. Once the leadership committee is convened and discussions have occurred, voting must conclude within 1 hour. +1. Voting is performed anonymously and handled solely by the neutral moderator in the case of conflict resolution, or by one of the maintainers. +1. After voting has finished, any maintainer may invoke a veto vote. If the veto succeeds, then the same item may not be voted on for 100 days. + +## Adding members to the Leadership Team + +### Confirming Maintainers + +* Given to individuals on merit basis after they have demonstrated strong expertise of the library through contributions, reviews and discussions. +* Adding a maintainer requires one of: + * a unanimous vote by current maintainers + * 75% vote within the advisory committee +* For continued membership in the maintainer group the individual has to demonstrate strong and continued alignment with the fastplotlib mission. The individual must also actively commit to the repo, respond to issues, and review pull requests. +* The membership is for an individual, not a company or organization. +* There must always be a minimum of 2 maintainers. +* A maintainer may be removed by one of: + * 60% vote within the maintainers + * 75% vote within the advisory committee + +### Confirming an advisory committee member + +* A candidate advisory committee member may only be nominated by a current maintainer or advisory committee member. +* Candidate must have used fastplotlib in their own work or library, or made contributions to fastplotlib. +* Candidate must be committed to the mission and demonstrate, with examples, how their role on the advisory committee would further the mission. +* If the Individual fulfills the above criteria they may be considered. The Leadership Team then requires an overall 80% vote to add the candidate as an advisory member. +* The membership is for an individual, not a company or organization. + * 60% vote within the maintainers + * 75% vote within the advisory committee + +## Invoking a vote from the leadership team + +### Conflict + +Anyone (absolutely anyone, not just the leadership team members) who feels that the code of conduct or governance document has been breached may invoke a vote by contacting the neutral moderator. + +#### Process + +1. Contact the neutral moderator with a description of the conflict, max of 250 words. +2. Neutral moderator must schedule a vote within 15 days. If that is not possible then within the next 45 days. +3. If there is a special case that requires urgency, such as upcoming events, workshops, etc., the neutral moderator may try to schedule a vote ASAP. Exercising this urgency is solely at the discretion of the neutral moderator. +4. The individual who has invoked the conflict vote can choose to present their case, or they may choose to let the neutral moderator represent them. + * Every individual involved in the conflict is given a maximum of 15 minutes to be represented. This time limit may be expanded at the discretion of the neutral moderator if a justifiable reason is provided. +5. The Leadership Team votes on one of the actions from “Enforcement Guidelines”: https://www.contributor-covenant.org/version/2/1/code_of_conduct/. It is advised that the first offense leads to action (1) “Correction” and then go down the list for repeated conflicts from the same individual/organization. Very bad behavior, as determined by the leadership team, can justify a first offense resulting in (3) “Temporary Ban” or (4) “Permanent Ban”. +6. Voting is performed as described in the section **Voting Process**. + +### Adding a member to the leadership team +1. A current maintainer or advisory committee member may nominate a candidate and then contact the neutral moderator with a written summary, of no more than 100 words, with reasons for adding the candidate to the Leadership Team. +1. The nominator should specify whether the candidate should be a maintainer or advisory committee member. +1. The neutral moderator must then schedule a vote within 100 days. +1. The nominator and candidate may then speak for no more than 10 minutes each on why the candidate should be added to the leadership team. +1. Voting is performed as described in the section **Voting Process**. + +## Changes to this governance document + +#### Until February 28, 2025 + +During early stages of fastplotlib development, changes to the governance document may be made directly through unanimous approval by the original maintainers, Kushal Kolar & Caitlin Lewis, without contacting the neutral moderator. + +#### After February 28, 2025 + +Any member of the leadership team can propose changes to the governance document. Approving the changes requires a 75% vote within the maintainers and a 75% vote within the advisory committee. + +## Reasons for invoking a vote + +Things that can be voted on include but aren’t limited to: +1. Nominating a member to the leadership team +1. Adding a nominee to the leadership team +1. Removing a member from the leadership team +1. Banning any person (not just leadership team members) or organization from interacting with the fastplotlib github repository and/or fastplotlib github organization. +1. Appeal to un-ban a previously banned person or organization. + * Can only occur 3 months after the initial ban + * Person must contact the neutral moderator directly to start an appeal + * Maximum of 3 appeal attempts are allowed From 30cc44eb6a9eede3d234e206e8d11cab699b89e2 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Sat, 17 Feb 2024 02:28:10 -0500 Subject: [PATCH 024/185] Update GOVERNANCE.md --- GOVERNANCE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GOVERNANCE.md b/GOVERNANCE.md index 5102707ce..8c4f9b3cf 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -100,7 +100,7 @@ Anyone (absolutely anyone, not just the leadership team members) who feels that #### Until February 28, 2025 -During early stages of fastplotlib development, changes to the governance document may be made directly through unanimous approval by the original maintainers, Kushal Kolar & Caitlin Lewis, without contacting the neutral moderator. +During early stages of fastplotlib development, changes to the governance document may be made directly through unanimous approval by the original maintainers, Kushal Kolar & Caitlin Lewis, without contacting the neutral moderator or consulting with the advisory committee. They (Kushal & Caitlin) may also add new members to the advisory committee through unanimous approval. #### After February 28, 2025 From cf4a04b95ec75bb83f8b1243478fe4b4b683b094 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Mon, 19 Feb 2024 05:43:45 -0500 Subject: [PATCH 025/185] fix bug when remove_graphic() is used (#405) --- fastplotlib/layouts/_plot_area.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fastplotlib/layouts/_plot_area.py b/fastplotlib/layouts/_plot_area.py index 9522832d3..819efa205 100644 --- a/fastplotlib/layouts/_plot_area.py +++ b/fastplotlib/layouts/_plot_area.py @@ -373,6 +373,12 @@ def add_graphic(self, graphic: Graphic, center: bool = True): Center the camera on the newly added Graphic """ + + if graphic in self: + # graphic is already in this plot but was removed from the scene, add it back + self.scene.add(graphic.world_object) + return + self._add_or_insert_graphic(graphic=graphic, center=center, action="add") graphic.position_z = len(self) From 78f1d2a7a1251d8b9fb92b67cadd4201ce941786 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Mon, 19 Feb 2024 22:35:35 -0500 Subject: [PATCH 026/185] add 'Deleted' as a graphic feature (#404) --- fastplotlib/graphics/_base.py | 11 +++++- fastplotlib/graphics/_features/__init__.py | 2 ++ fastplotlib/graphics/_features/_deleted.py | 41 ++++++++++++++++++++++ fastplotlib/graphics/image.py | 7 ++-- fastplotlib/graphics/line.py | 2 +- fastplotlib/graphics/line_collection.py | 2 +- fastplotlib/graphics/scatter.py | 2 +- 7 files changed, 60 insertions(+), 7 deletions(-) create mode 100644 fastplotlib/graphics/_features/_deleted.py diff --git a/fastplotlib/graphics/_base.py b/fastplotlib/graphics/_base.py index a0b4881fb..eea78142c 100644 --- a/fastplotlib/graphics/_base.py +++ b/fastplotlib/graphics/_base.py @@ -8,7 +8,7 @@ from pygfx import WorldObject -from ._features import GraphicFeature, PresentFeature, GraphicFeatureIndexable +from ._features import GraphicFeature, PresentFeature, GraphicFeatureIndexable, Deleted # dict that holds all world objects for a given python kernel/session # Graphic objects only use proxies to WorldObjects @@ -45,6 +45,12 @@ def __init_subclass__(cls, **kwargs): class Graphic(BaseGraphic): + feature_events = {} + + def __init_subclass__(cls, **kwargs): + # all graphics give off a feature event when deleted + cls.feature_events = {*cls.feature_events, "deleted"} + def __init__( self, name: str = None, @@ -72,6 +78,8 @@ def __init__( # store hex id str of Graphic instance mem location self.loc: str = hex(id(self)) + self.deleted = Deleted(self, False) + @property def world_object(self) -> WorldObject: """Associated pygfx WorldObject. Always returns a proxy, real object cannot be accessed directly.""" @@ -168,6 +176,7 @@ def _cleanup(self): pass def __del__(self): + self.deleted = True del WORLD_OBJECTS[self.loc] diff --git a/fastplotlib/graphics/_features/__init__.py b/fastplotlib/graphics/_features/__init__.py index a6ce9c3a3..a1769b010 100644 --- a/fastplotlib/graphics/_features/__init__.py +++ b/fastplotlib/graphics/_features/__init__.py @@ -5,6 +5,7 @@ from ._thickness import ThicknessFeature from ._base import GraphicFeature, GraphicFeatureIndexable, FeatureEvent, to_gpu_supported_dtype from ._selection_features import LinearSelectionFeature, LinearRegionSelectionFeature +from ._deleted import Deleted __all__ = [ "ColorFeature", @@ -23,4 +24,5 @@ "to_gpu_supported_dtype", "LinearSelectionFeature", "LinearRegionSelectionFeature", + "Deleted", ] diff --git a/fastplotlib/graphics/_features/_deleted.py b/fastplotlib/graphics/_features/_deleted.py new file mode 100644 index 000000000..2fca1c719 --- /dev/null +++ b/fastplotlib/graphics/_features/_deleted.py @@ -0,0 +1,41 @@ +from ._base import GraphicFeature, FeatureEvent + + +class Deleted(GraphicFeature): + """ + Used when a graphic is deleted, triggers events that can be useful to indicate this graphic has been deleted + + **event pick info:** + + ==================== ======================== ========================================================================= + key type description + ==================== ======================== ========================================================================= + "collection-index" int the index of the graphic within the collection that triggered the event + "world_object" pygfx.WorldObject world object + ==================== ======================== ========================================================================= + """ + + def __init__(self, parent, value: bool): + super(Deleted, self).__init__(parent, value) + + def _set(self, value: bool): + value = self._parse_set_value(value) + self._feature_changed(key=None, new_data=value) + + def _feature_changed(self, key, new_data): + # this is a non-indexable feature so key=None + + pick_info = { + "index": None, + "collection-index": self._collection_index, + "world_object": self._parent.world_object, + "new_data": new_data, + } + + event_data = FeatureEvent(type="deleted", pick_info=pick_info) + + self._call_event_handlers(event_data) + + def __repr__(self) -> str: + s = f"DeletedFeature for {self._parent}" + return s diff --git a/fastplotlib/graphics/image.py b/fastplotlib/graphics/image.py index 10f09eefb..3d629c10f 100644 --- a/fastplotlib/graphics/image.py +++ b/fastplotlib/graphics/image.py @@ -196,7 +196,7 @@ def _add_plot_area_hook(self, plot_area): class ImageGraphic(Graphic, Interaction, _AddSelectorsMixin): - feature_events = ("data", "cmap", "present") + feature_events = {"data", "cmap", "present"} def __init__( self, @@ -345,10 +345,11 @@ def col_chunk_index(self, index: int): class HeatmapGraphic(Graphic, Interaction, _AddSelectorsMixin): - feature_events = ( + feature_events = { "data", "cmap", - ) + "present" + } def __init__( self, diff --git a/fastplotlib/graphics/line.py b/fastplotlib/graphics/line.py index d6f061ab0..9ac7568a7 100644 --- a/fastplotlib/graphics/line.py +++ b/fastplotlib/graphics/line.py @@ -12,7 +12,7 @@ class LineGraphic(Graphic, Interaction): - feature_events = ("data", "colors", "cmap", "thickness", "present") + feature_events = {"data", "colors", "cmap", "thickness", "present"} def __init__( self, diff --git a/fastplotlib/graphics/line_collection.py b/fastplotlib/graphics/line_collection.py index 38597a830..a5c398130 100644 --- a/fastplotlib/graphics/line_collection.py +++ b/fastplotlib/graphics/line_collection.py @@ -15,7 +15,7 @@ class LineCollection(GraphicCollection, Interaction): child_type = LineGraphic.__name__ - feature_events = ("data", "colors", "cmap", "thickness", "present") + feature_events = {"data", "colors", "cmap", "thickness", "present"} def __init__( self, diff --git a/fastplotlib/graphics/scatter.py b/fastplotlib/graphics/scatter.py index 961324c23..63689fad9 100644 --- a/fastplotlib/graphics/scatter.py +++ b/fastplotlib/graphics/scatter.py @@ -9,7 +9,7 @@ class ScatterGraphic(Graphic): - feature_events = ("data", "sizes", "colors", "cmap", "present") + feature_events = {"data", "sizes", "colors", "cmap", "present"} def __init__( self, From 5ac4c9c05e510e8735751d2d643f1b09e9b00119 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Tue, 20 Feb 2024 14:30:04 -0500 Subject: [PATCH 027/185] Apply suggestions from code review Co-authored-by: Caitlin Lewis <69729525+clewis7@users.noreply.github.com> --- GOVERNANCE.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/GOVERNANCE.md b/GOVERNANCE.md index 8c4f9b3cf..1abd48611 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -1,12 +1,12 @@ # fastplotlib governance -The governance of fastplotlib applies to all fastplotlib related activities. This includes the fastplotlib github organization, all repositories under the fastplotlib github organization, as well as any events or workshops organized by members of the fastplotlib Leadership Team. +The governance of fastplotlib applies to all fastplotlib related activities. This includes the fastplotlib GitHub organization, all repositories under the fastplotlib GitHub organization, as well as any events or workshops organized by members of the fastplotlib Leadership Team. ## Mission Leverage new graphics APIs and modern GPU hardware to create fast and interactive scientific visualizations using an expressive and elegant API. -## Leadership team +## Leadership Team ### Maintainers @@ -18,7 +18,7 @@ The maintainers are the core developers of fastplotlib and together have a compl ### Advisory Committee -The advisory committee hold a significant stake in fastplotlib as determined by the **Maintainers**. The responsibilities of the **Advisory Committee** are to 1) attend a yearly roadmap meeting, 2) be available for conflict resolution. +The Advisory Committee hold a significant stake in fastplotlib as determined by the **Maintainers**. The responsibilities of the **Advisory Committee** are to 1) attend a yearly Roadmap meeting, 2) be available for conflict resolution. 1. Eric Thomson 1. Guillaume Viejo @@ -27,7 +27,7 @@ The advisory committee hold a significant stake in fastplotlib as determined by ### Neutral moderator -No voting power, has no stake in the fastplotlib project +No voting power, has no stake in the fastplotlib project. * Reagan Bullins @@ -43,8 +43,8 @@ Note that currently the voting power is primarily held by the maintainers - Kush ### Voting Process -1. For the purpose of conflict resolution the leadership team must be convened by the neutral moderator. In all other situations the leadership team may be convened by one of the maintainers. -1. Once the leadership committee is convened and discussions have occurred, voting must conclude within 1 hour. +1. For the purpose of conflict resolution the Leadership Team must be convened by the neutral moderator. In all other situations the Leadership Team may be convened by one of the maintainers. +1. Once the Leadership Team is convened and discussions have occurred, voting must conclude within 1 hour. 1. Voting is performed anonymously and handled solely by the neutral moderator in the case of conflict resolution, or by one of the maintainers. 1. After voting has finished, any maintainer may invoke a veto vote. If the veto succeeds, then the same item may not be voted on for 100 days. @@ -104,15 +104,15 @@ During early stages of fastplotlib development, changes to the governance docume #### After February 28, 2025 -Any member of the leadership team can propose changes to the governance document. Approving the changes requires a 75% vote within the maintainers and a 75% vote within the advisory committee. +Any member of the Leadership Team can propose changes to the governance document. Approving the changes requires a 75% vote within the maintainers and a 75% vote within the advisory committee. ## Reasons for invoking a vote Things that can be voted on include but aren’t limited to: -1. Nominating a member to the leadership team -1. Adding a nominee to the leadership team -1. Removing a member from the leadership team -1. Banning any person (not just leadership team members) or organization from interacting with the fastplotlib github repository and/or fastplotlib github organization. +1. Nominating a member to the Leadership Team +1. Adding a nominee to the Leadership Team +1. Removing a member from the Leadership Team +1. Banning any person (not just Leadership Team members) or organization from interacting with the fastplotlib GitHub repository and/or fastplotlib GitHub organization. 1. Appeal to un-ban a previously banned person or organization. * Can only occur 3 months after the initial ban * Person must contact the neutral moderator directly to start an appeal From 1b76169570e32e6a4055b9b38df82ac4a31bf5cc Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Tue, 20 Feb 2024 23:56:11 -0500 Subject: [PATCH 028/185] Update GOVERNANCE.md --- GOVERNANCE.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/GOVERNANCE.md b/GOVERNANCE.md index 1abd48611..ff7b107b5 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -2,6 +2,8 @@ The governance of fastplotlib applies to all fastplotlib related activities. This includes the fastplotlib GitHub organization, all repositories under the fastplotlib GitHub organization, as well as any events or workshops organized by members of the fastplotlib Leadership Team. +The purpose of this document is to formalize the governance process used by the fastplotlib project, to clarify how decisions are made and how the various elements of our community interact. + ## Mission Leverage new graphics APIs and modern GPU hardware to create fast and interactive scientific visualizations using an expressive and elegant API. From e831f152b37e674d3e6d7c017dfc5a7687222f74 Mon Sep 17 00:00:00 2001 From: Caitlin Lewis <69729525+clewis7@users.noreply.github.com> Date: Sat, 24 Feb 2024 21:47:25 -0500 Subject: [PATCH 029/185] Update README.md (#411) Update README install instructions to be from fastplotlib org. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 568b0ee4a..37a2d0779 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ pip install simplejpeg ### For developers ```bash -git clone https://github.com/kushalkolar/fastplotlib.git +git clone https://github.com/fastplotlib/fastplotlib.git cd fastplotlib # install all extras in place From 7921516f3f72a281a071b5c4b2375b8b28f58da1 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Tue, 27 Feb 2024 01:14:44 -0500 Subject: [PATCH 030/185] gpu info upon notebook import (#410) * displaying fpl logo and gpu info works on import in nb * add face logo for nb and add to MANIFEST * remove line that snuck in from other branch * git lfs to pypi build action * fix import and add easter egg --- .github/workflows/pypi-publish.yml | 7 ++ MANIFEST.in | 2 + fastplotlib/__init__.py | 11 ++++ fastplotlib/assets/egg.gif | 3 + fastplotlib/assets/fastplotlib_face_logo.png | 3 + .../layouts/_frame/_ipywidget_toolbar.py | 16 +++++ fastplotlib/utils/__init__.py | 14 ++++ fastplotlib/utils/_gpu_info.py | 66 +++++++++++++++++++ 8 files changed, 122 insertions(+) create mode 100644 fastplotlib/assets/egg.gif create mode 100644 fastplotlib/assets/fastplotlib_face_logo.png create mode 100644 fastplotlib/utils/_gpu_info.py diff --git a/.github/workflows/pypi-publish.yml b/.github/workflows/pypi-publish.yml index 207d92351..9ebe52b87 100644 --- a/.github/workflows/pypi-publish.yml +++ b/.github/workflows/pypi-publish.yml @@ -21,7 +21,14 @@ jobs: runs-on: ubuntu-latest steps: + - name: Install git-lfs + run: | + sudo apt install --no-install-recommends -y git-lfs - uses: actions/checkout@v3 + - name: fetch git lfs files + run: | + git lfs fetch --all + git lfs pull - name: Set up Python uses: actions/setup-python@v3 with: diff --git a/MANIFEST.in b/MANIFEST.in index 121ea2fd0..b8debd28d 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1,4 @@ recursive-include fastplotlib/utils/colormaps/ * include fastplotlib/VERSION +recursive-include fastplotlib/assets/ * + diff --git a/fastplotlib/__init__.py b/fastplotlib/__init__.py index 301412aff..b3434012f 100644 --- a/fastplotlib/__init__.py +++ b/fastplotlib/__init__.py @@ -3,6 +3,7 @@ from .layouts import Plot, GridPlot from .graphics import * from .graphics.selectors import * +from .utils import _notebook_print_banner, config from wgpu.gui.auto import run @@ -13,6 +14,16 @@ else: from .widgets import ImageWidget +from wgpu.backends.wgpu_native import enumerate_adapters + +adapters = [a.request_adapter_info() for a in enumerate_adapters()] + +if len(adapters) < 1: + raise IndexError( + "No WGPU adapters found, fastplotlib will not work." + ) + +_notebook_print_banner() with open(Path(__file__).parent.joinpath("VERSION"), "r") as f: __version__ = f.read().split("\n")[0] diff --git a/fastplotlib/assets/egg.gif b/fastplotlib/assets/egg.gif new file mode 100644 index 000000000..0ff189075 --- /dev/null +++ b/fastplotlib/assets/egg.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7251090fd17fa81eae8a5ad176608755b4a68d620b125ebd571acc6d68daa017 +size 4589 diff --git a/fastplotlib/assets/fastplotlib_face_logo.png b/fastplotlib/assets/fastplotlib_face_logo.png new file mode 100644 index 000000000..a5a8bd90e --- /dev/null +++ b/fastplotlib/assets/fastplotlib_face_logo.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:84976200545d143f559b6840a332f3121d58f8d7de8dad7cb8a6d027d854c153 +size 10239 diff --git a/fastplotlib/layouts/_frame/_ipywidget_toolbar.py b/fastplotlib/layouts/_frame/_ipywidget_toolbar.py index f27856e61..552ee9827 100644 --- a/fastplotlib/layouts/_frame/_ipywidget_toolbar.py +++ b/fastplotlib/layouts/_frame/_ipywidget_toolbar.py @@ -3,6 +3,7 @@ from itertools import product from math import copysign from functools import partial +from pathlib import Path from typing import * @@ -17,10 +18,12 @@ BoundedIntText, Play, jslink, + Image, ) from ...graphics.selectors import PolygonSelector from ._toolbar import ToolBar +from ...utils import config class IpywidgetToolBar(HBox, ToolBar): @@ -92,6 +95,19 @@ def __init__(self, plot): self._record_button, ] + if config.party_parrot: + gif_path = Path(__file__).parent.parent.parent.joinpath("assets", "egg.gif") + with open(gif_path, "rb") as f: + gif = f.read() + + image = Image( + value=gif, + format="png", + width=35, + height=25, + ) + widgets.append(image) + if hasattr(self.plot, "_subplots"): positions = list(product(range(self.plot.shape[0]), range(self.plot.shape[1]))) values = list() diff --git a/fastplotlib/utils/__init__.py b/fastplotlib/utils/__init__.py index c8f754883..addea140d 100644 --- a/fastplotlib/utils/__init__.py +++ b/fastplotlib/utils/__init__.py @@ -1 +1,15 @@ +from dataclasses import dataclass + + from .functions import * +from ._gpu_info import _notebook_print_banner + + +@dataclass +class _Config: + party_parrot: bool + + +config = _Config( + party_parrot=False +) diff --git a/fastplotlib/utils/_gpu_info.py b/fastplotlib/utils/_gpu_info.py new file mode 100644 index 000000000..9387f3ece --- /dev/null +++ b/fastplotlib/utils/_gpu_info.py @@ -0,0 +1,66 @@ +from pathlib import Path + +from wgpu.backends.wgpu_native import enumerate_adapters +from wgpu.utils import get_default_device + +try: + ip = get_ipython() + from ipywidgets import Image + from wgpu.gui.jupyter import JupyterWgpuCanvas +except (NameError, ModuleNotFoundError, ImportError): + NOTEBOOK = False +else: + from IPython.display import display + if ip.has_trait("kernel") and (JupyterWgpuCanvas is not False): + NOTEBOOK = True + else: + NOTEBOOK = False + + +def _notebook_print_banner(): + if NOTEBOOK is False: + return + + logo_path = Path(__file__).parent.parent.joinpath("assets", "fastplotlib_face_logo.png") + + with open(logo_path, "rb") as f: + logo_data = f.read() + + image = Image( + value=logo_data, + format="png", + width=300, + height=55 + ) + + display(image) + + # print logo and adapter info + adapters = [a for a in enumerate_adapters()] + adapters_info = [a.request_adapter_info() for a in adapters] + + ix_default = adapters_info.index(get_default_device().adapter.request_adapter_info()) + + if len(adapters) > 0: + print("Available devices:") + + for ix, adapter in enumerate(adapters_info): + atype = adapter["adapter_type"] + backend = adapter["backend_type"] + driver = adapter["description"] + device = adapter["device"] + + if atype == "DiscreteGPU" and backend != "OpenGL": + charactor = chr(0x2705) + elif atype == "IntegratedGPU" and backend != "OpenGL": + charactor = chr(0x0001FBC4) + else: + charactor = chr(0x2757) + + if ix == ix_default: + default = " (default) " + else: + default = " " + + output_str = f"{charactor}{default}| {device} | {atype} | {backend} | {driver}" + print(output_str) From baa859b151a0668e30ce5b6a32d44772ede8b3df Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Tue, 27 Feb 2024 02:12:09 -0500 Subject: [PATCH 031/185] use default renderer pixel_ratio=2 (#412) --- fastplotlib/layouts/_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastplotlib/layouts/_utils.py b/fastplotlib/layouts/_utils.py index 7db1d84c4..1662f00c5 100644 --- a/fastplotlib/layouts/_utils.py +++ b/fastplotlib/layouts/_utils.py @@ -86,7 +86,7 @@ def make_canvas_and_renderer( ) if renderer is None: - renderer = WgpuRenderer(canvas) + renderer = WgpuRenderer(canvas, pixel_ratio=2) return canvas, renderer From a8b021a369a974d96ae4fae0898fe32578cb6972 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Tue, 27 Feb 2024 02:12:41 -0500 Subject: [PATCH 032/185] add gpu info to docs (#409) --- docs/source/index.rst | 6 +++ docs/source/user_guide/gpu.rst | 82 ++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 docs/source/user_guide/gpu.rst diff --git a/docs/source/index.rst b/docs/source/index.rst index a6d36dd4f..9dbd30783 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -12,6 +12,12 @@ Welcome to fastplotlib's documentation! quickstart +.. toctree:: + :caption: User Guide + :maxdepth: 1 + + GPU Info + .. toctree:: :maxdepth: 1 :caption: API diff --git a/docs/source/user_guide/gpu.rst b/docs/source/user_guide/gpu.rst new file mode 100644 index 000000000..006f78872 --- /dev/null +++ b/docs/source/user_guide/gpu.rst @@ -0,0 +1,82 @@ +GPU Info +******** + +FAQ +--- + +1. Do I need a GPU? + +Technically no, you can perform limited software rendering on linux using lavapipe (see drivers link below). However +``fastplotlib`` is intentionally built for realtime rendering using the latest GPU technologies, so we strongly +recommend that you use a GPU. + +2. My kernel keeps crashing when I create visualizations. + +This can happen under the following circumstances: + +- You have ran out of GPU VRAM. +- Driver issues (see next section). + +If you aren't able to solve it please post an issue on GitHub. :) + +Drivers +------- + +See the README: https://github.com/fastplotlib/fastplotlib?tab=readme-ov-file#graphics-drivers + +If you notice weird graphic artifacts, things not rendering, or other glitches try updating to the latest stable +drivers. + + +View available GPU +------------------ + +You can view all GPUs that are available to ``WGPU`` like this:: + + from wgpu.backends.wgpu_native import enumerate_adapters + from pprint import pprint + + for adapter in enumerate_adapters(): + pprint(adapter.request_adapter_info()) + +For example, on a Thinkpad AMD laptop with a dedicated nvidia GPU this returns:: + + {'adapter_type': 'IntegratedGPU', + 'architecture': '', + 'backend_type': 'Vulkan', + 'description': 'Mesa 22.3.6', + 'device': 'AMD Radeon Graphics (RADV REMBRANDT)', + 'vendor': 'radv'} + {'adapter_type': 'DiscreteGPU', + 'architecture': '', + 'backend_type': 'Vulkan', + 'description': '535.129.03', + 'device': 'NVIDIA T1200 Laptop GPU', + 'vendor': 'NVIDIA'} + {'adapter_type': 'CPU', + 'architecture': '', + 'backend_type': 'Vulkan', + 'description': 'Mesa 22.3.6 (LLVM 15.0.6)', + 'device': 'llvmpipe (LLVM 15.0.6, 256 bits)', + 'vendor': 'llvmpipe'} + {'adapter_type': 'Unknown', + 'architecture': '', + 'backend_type': 'OpenGL', + 'description': '', + 'device': 'AMD Radeon Graphics (rembrandt, LLVM 15.0.6, DRM 3.52, ' + '6.4.0-0.deb12.2-amd64)', + 'vendor': ''} + +GPU currently in use +-------------------- + +If you want to know the GPU that a current plot is using you can check the adapter that the renderer is using:: + + # for example if we make a plot + plot = fpl.Plot() + plot.add_image(np.random.rand(100, 100)) + plot.show() + + # GPU that is currently in use by the renderer + plot.renderer.device.adapter.request_adapter_info() + From 87f6469d8ff716a3f4d2c9221f3cb7088ee0e0f2 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Tue, 27 Feb 2024 17:42:31 -0500 Subject: [PATCH 033/185] update screenshots, pygfx line shader changes (#414) --- examples/desktop/screenshots/line.png | 4 ++-- examples/desktop/screenshots/line_cmap.png | 4 ++-- examples/desktop/screenshots/line_collection.png | 4 ++-- examples/desktop/screenshots/line_collection_cmap_values.png | 4 ++-- .../screenshots/line_collection_cmap_values_qualitative.png | 4 ++-- examples/desktop/screenshots/line_collection_colors.png | 4 ++-- examples/desktop/screenshots/line_colorslice.png | 4 ++-- examples/desktop/screenshots/line_dataslice.png | 4 ++-- examples/desktop/screenshots/line_present_scaling.png | 4 ++-- examples/desktop/screenshots/line_stack.png | 4 ++-- .../notebooks/screenshots/nb-image-widget-movie-set_data.png | 4 ++-- .../screenshots/nb-image-widget-movie-single-0-reset.png | 4 ++-- .../notebooks/screenshots/nb-image-widget-movie-single-0.png | 4 ++-- .../screenshots/nb-image-widget-movie-single-279.png | 4 ++-- .../nb-image-widget-movie-single-50-window-max-33.png | 4 ++-- .../nb-image-widget-movie-single-50-window-mean-13.png | 4 ++-- .../nb-image-widget-movie-single-50-window-mean-33.png | 4 ++-- .../nb-image-widget-movie-single-50-window-reset.png | 4 ++-- .../notebooks/screenshots/nb-image-widget-movie-single-50.png | 4 ++-- .../notebooks/screenshots/nb-image-widget-single-gnuplot2.png | 4 ++-- examples/notebooks/screenshots/nb-image-widget-single.png | 4 ++-- .../nb-image-widget-zfish-frame-50-frame-apply-gaussian.png | 4 ++-- .../nb-image-widget-zfish-frame-50-frame-apply-reset.png | 4 ++-- .../nb-image-widget-zfish-frame-50-max-window-13.png | 4 ++-- .../nb-image-widget-zfish-frame-50-mean-window-13.png | 4 ++-- .../nb-image-widget-zfish-frame-50-mean-window-5.png | 4 ++-- .../notebooks/screenshots/nb-image-widget-zfish-frame-50.png | 4 ++-- .../notebooks/screenshots/nb-image-widget-zfish-frame-99.png | 4 ++-- ...-image-widget-zfish-grid-frame-50-frame-apply-gaussian.png | 4 ++-- .../nb-image-widget-zfish-grid-frame-50-frame-apply-reset.png | 4 ++-- .../nb-image-widget-zfish-grid-frame-50-max-window-13.png | 4 ++-- .../nb-image-widget-zfish-grid-frame-50-mean-window-13.png | 4 ++-- .../nb-image-widget-zfish-grid-frame-50-mean-window-5.png | 4 ++-- .../screenshots/nb-image-widget-zfish-grid-frame-50.png | 4 ++-- .../screenshots/nb-image-widget-zfish-grid-frame-99.png | 4 ++-- .../nb-image-widget-zfish-grid-init-mean-window-5.png | 4 ++-- ...b-image-widget-zfish-grid-set_data-reset-indices-false.png | 4 ++-- ...nb-image-widget-zfish-grid-set_data-reset-indices-true.png | 4 ++-- .../screenshots/nb-image-widget-zfish-init-mean-window-5.png | 4 ++-- examples/notebooks/screenshots/nb-lines-3d.png | 4 ++-- .../notebooks/screenshots/nb-lines-cmap-jet-values-cosine.png | 4 ++-- examples/notebooks/screenshots/nb-lines-cmap-jet-values.png | 4 ++-- examples/notebooks/screenshots/nb-lines-cmap-jet.png | 4 ++-- examples/notebooks/screenshots/nb-lines-cmap-tab-10.png | 4 ++-- .../notebooks/screenshots/nb-lines-cmap-viridis-values.png | 4 ++-- examples/notebooks/screenshots/nb-lines-cmap-viridis.png | 4 ++-- examples/notebooks/screenshots/nb-lines-cmap-white.png | 4 ++-- examples/notebooks/screenshots/nb-lines-colors.png | 4 ++-- examples/notebooks/screenshots/nb-lines-data.png | 4 ++-- examples/notebooks/screenshots/nb-lines-underlay.png | 4 ++-- examples/notebooks/screenshots/nb-lines.png | 4 ++-- 51 files changed, 102 insertions(+), 102 deletions(-) diff --git a/examples/desktop/screenshots/line.png b/examples/desktop/screenshots/line.png index cbc0a7b21..8e3e6ae64 100644 --- a/examples/desktop/screenshots/line.png +++ b/examples/desktop/screenshots/line.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:018f4a36c60b3de2f1406aa2823b751de5fae2c2340f9d49368d007ba7379637 -size 44422 +oid sha256:81038ebca5d41f22f5dde3fd152d94760ef51cc055ff248be18298bc7537b569 +size 44312 diff --git a/examples/desktop/screenshots/line_cmap.png b/examples/desktop/screenshots/line_cmap.png index a07b15d36..b96c9a1dd 100644 --- a/examples/desktop/screenshots/line_cmap.png +++ b/examples/desktop/screenshots/line_cmap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c6df49f5f900caa217a5a0b01f601d6e83e1ee5c3a60c8968b9be837df785905 -size 43673 +oid sha256:e7a9cf65bbd19290ed96e418930e896fd0ec463dc2c6797f8b407d56e9e4444d +size 43730 diff --git a/examples/desktop/screenshots/line_collection.png b/examples/desktop/screenshots/line_collection.png index 60ec82bc8..89d613c2c 100644 --- a/examples/desktop/screenshots/line_collection.png +++ b/examples/desktop/screenshots/line_collection.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e0ddd75bc2fd8e4844ccb46c53fe463dc604927737ed00c8fb6b1c29fd2b1ab2 -size 146797 +oid sha256:09e8c2be9815edf5c29a98dc61758fac3aeb2603c8547aa8c3c05b01538886e3 +size 147244 diff --git a/examples/desktop/screenshots/line_collection_cmap_values.png b/examples/desktop/screenshots/line_collection_cmap_values.png index 7223db9ee..38d9adc6e 100644 --- a/examples/desktop/screenshots/line_collection_cmap_values.png +++ b/examples/desktop/screenshots/line_collection_cmap_values.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:311c7ef6f6f46e32983a5a531adc179b0179f382300f3cafbb6d8a7d4aeab565 -size 93676 +oid sha256:98d7468dc1701d3523c779a0e857cf185201766b751731e1db44e3c2bc753335 +size 93839 diff --git a/examples/desktop/screenshots/line_collection_cmap_values_qualitative.png b/examples/desktop/screenshots/line_collection_cmap_values_qualitative.png index 500de82bb..4f14e49b9 100644 --- a/examples/desktop/screenshots/line_collection_cmap_values_qualitative.png +++ b/examples/desktop/screenshots/line_collection_cmap_values_qualitative.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8714dd43aa3d5c791fb9359e745895447d3a3234e3e8598d171ea3666a3fd7a3 -size 95660 +oid sha256:31d79a2aeb2e43b569a9045edd21fb59e7bddf2b5cc18133f4b346e0d6be7fd1 +size 95696 diff --git a/examples/desktop/screenshots/line_collection_colors.png b/examples/desktop/screenshots/line_collection_colors.png index f60faae32..c71c623c6 100644 --- a/examples/desktop/screenshots/line_collection_colors.png +++ b/examples/desktop/screenshots/line_collection_colors.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e2fad15985608a4c6b1fda3005dc89950b4cad5fed956f5d26672257385985d0 -size 82753 +oid sha256:381fc320bda54f5aa235b4cd0183ff511e3a0484c00ab37f76c89df959d48626 +size 82806 diff --git a/examples/desktop/screenshots/line_colorslice.png b/examples/desktop/screenshots/line_colorslice.png index 7b652f165..7775c1918 100644 --- a/examples/desktop/screenshots/line_colorslice.png +++ b/examples/desktop/screenshots/line_colorslice.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5bd80cee80f491da6ab64c868a3c70254a68072e0bc0caad80c7999cadcb2df9 -size 50497 +oid sha256:3ade0416355a05de01e8f4a55485aacc40bd4c3d57a1a9fcff43317e13a43856 +size 50560 diff --git a/examples/desktop/screenshots/line_dataslice.png b/examples/desktop/screenshots/line_dataslice.png index d68a554bd..2907dd64a 100644 --- a/examples/desktop/screenshots/line_dataslice.png +++ b/examples/desktop/screenshots/line_dataslice.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:823651905a775b5cbcc2ce0f79d25d69b29f17b4c060c244d80ae87019f05d5b -size 69332 +oid sha256:8fc4265c0ac6b4cee0476acbc5362968f60a965a387c7a0a3b66e89e522eb21c +size 69917 diff --git a/examples/desktop/screenshots/line_present_scaling.png b/examples/desktop/screenshots/line_present_scaling.png index 02cd2b1f8..b4b883855 100644 --- a/examples/desktop/screenshots/line_present_scaling.png +++ b/examples/desktop/screenshots/line_present_scaling.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3d18669d5e75cee3326d0380ae5dd26cab71ea97725ff99bc5228d2555d51454 -size 30373 +oid sha256:f37f2a227136af0cfc112bb1e5c9ba01fb362f33bad0971f5253adb61e89785e +size 30264 diff --git a/examples/desktop/screenshots/line_stack.png b/examples/desktop/screenshots/line_stack.png index 443184247..47e59ba8c 100644 --- a/examples/desktop/screenshots/line_stack.png +++ b/examples/desktop/screenshots/line_stack.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:29580e5ebb0597d54adb2b7f2f91de44e4480cf23c0c271ee44426203b4c1c44 -size 360892 +oid sha256:0081d587956056934c4feb0c0f695f69ea19a253243f1af1dc7de80c6406a642 +size 365226 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-set_data.png b/examples/notebooks/screenshots/nb-image-widget-movie-set_data.png index 61c3aeb8c..a728df223 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-set_data.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-set_data.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:60318615e4850d37a4ffae16ca1e3bbf2985ddafd0dd65ba6fae997e1d123d67 -size 31251 +oid sha256:794d4ba4e31884a95c5a98596d142aa2d2af75647c5ad73dc573ff18493c1a07 +size 31256 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-0-reset.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-0-reset.png index d09ce18e4..8e624128e 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-0-reset.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-0-reset.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6b9187c64b7439f629a87a3828cc46a855e1b4609eca52d5484223d2c24e8bf7 -size 62562 +oid sha256:edd99517550a035ee55fb81301fb1c3e08e3e90bc4345e781449d83fa307a8a9 +size 62630 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-0.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-0.png index d09ce18e4..8e624128e 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-0.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6b9187c64b7439f629a87a3828cc46a855e1b4609eca52d5484223d2c24e8bf7 -size 62562 +oid sha256:edd99517550a035ee55fb81301fb1c3e08e3e90bc4345e781449d83fa307a8a9 +size 62630 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-279.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-279.png index d4c299683..d27e49568 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-279.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-279.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:11eb83e3489a8e9c55fcffb0e67f1cf773e538629ddf98e109601749927caa56 -size 72525 +oid sha256:3d5f5889af4490cdc81a47fd8235493bdf0384a015edcb5177ffea274efb64b1 +size 72626 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-max-33.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-max-33.png index ef5db6693..12dce091c 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-max-33.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-max-33.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:51bc6a90fba5c67935838b8e44dca477e250cbc4ee2b98ddd69f931e683ec17a -size 63906 +oid sha256:ec662212f6299cd27c3e26bb6b9fa1a99b4dd2808934536640e2f54d8bf8f699 +size 63968 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-13.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-13.png index 86287ea9e..44ccb8020 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1a2318ab1f242d70045b9784337e4421c635b2345331b6a5e8edc0f32ff15f07 -size 54432 +oid sha256:ffe73fa414fa1af8e2daa0bdb7fa75fd47d9d575b60c729a9426deb42225bb5a +size 54513 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-33.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-33.png index 14c101960..f0354cbb2 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-33.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-33.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6c5b5df5b0efb0b3b641129e08429e4a92bd5d2c62b1c63c97337287867d685e -size 50341 +oid sha256:10d82b8d15a5d1099f8a909401d78cc6a87d22708a7cd4c32d96bb316d570da3 +size 50431 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-reset.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-reset.png index 0df0df92b..25b65bd48 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-reset.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-reset.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f2e5a5734992333165ef2d5f6f810d869e157e59fb7f54c8dd5f413ac750a3fa -size 65067 +oid sha256:79776dadbe6947e9f20c96e5dc2d6ee718100f24fc09579609e7b25614869b74 +size 65150 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50.png index 0df0df92b..25b65bd48 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f2e5a5734992333165ef2d5f6f810d869e157e59fb7f54c8dd5f413ac750a3fa -size 65067 +oid sha256:79776dadbe6947e9f20c96e5dc2d6ee718100f24fc09579609e7b25614869b74 +size 65150 diff --git a/examples/notebooks/screenshots/nb-image-widget-single-gnuplot2.png b/examples/notebooks/screenshots/nb-image-widget-single-gnuplot2.png index da3033219..75fa3ef5a 100644 --- a/examples/notebooks/screenshots/nb-image-widget-single-gnuplot2.png +++ b/examples/notebooks/screenshots/nb-image-widget-single-gnuplot2.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:10912f4358278fb26c5efa030a335b0bdb80ebcc2a57fd97838fea3780f9f5d1 -size 143543 +oid sha256:7e292d6e43e7a98cb72ca077e62bbc9ad3acde4753098ea2248c66ee07a9fc46 +size 143675 diff --git a/examples/notebooks/screenshots/nb-image-widget-single.png b/examples/notebooks/screenshots/nb-image-widget-single.png index 346c1a987..1321d01bb 100644 --- a/examples/notebooks/screenshots/nb-image-widget-single.png +++ b/examples/notebooks/screenshots/nb-image-widget-single.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bf23c969c93bc526244360af4babf72a1377a171f55b1f44443dc026faf12631 -size 134432 +oid sha256:50f17ab342efff0d1d3bc06f36d2b7b372eed4426e282ffa2b12e3e7dc913b68 +size 134516 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-gaussian.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-gaussian.png index 6be1058e9..2543f9a5d 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-gaussian.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-gaussian.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:49b65e35bb12e0bce8e753f6700084b2e1100eb6efd0336afc219d9e26972901 -size 64206 +oid sha256:5834c92427c08b210a0971667018b4a63fd4e8d916bb02a582ba352cb0d5aad7 +size 64281 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-reset.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-reset.png index ead51e894..1a062cdf7 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-reset.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-reset.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:df13f86bb18ad52f962f8944cd780579440b4e1d40941019632d4f46a4d9dc2d -size 50107 +oid sha256:00f8173e7476a781826617bbaf03241348cece3c25d355e13832961cb022145f +size 50155 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-max-window-13.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-max-window-13.png index 295180169..00af23a9a 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-max-window-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-max-window-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:42676358a0199022972b69263c32a977f90a27ce6c3789d18129c67e9b730679 -size 121369 +oid sha256:676217864c9f5ca4792c08a4b51574acdceb8c02b186e467633583b358bdb0f8 +size 121420 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-13.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-13.png index adf129ab2..bd31b760d 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f3d1972c575f1659fc2c611a2c703cb408b74ca47e30c2e4564641dc0a6ad887 -size 76550 +oid sha256:4bb119d1e38ec69285092c551da7ff12541a15c4ef144689e6535c520e0e988b +size 76596 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-5.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-5.png index f123f83a9..ccbbd13d6 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-5.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e24ea022d48ff41174206e43da911632f38237f6250340f73a7169a43d55f2a6 -size 72238 +oid sha256:57202d813c24f93fc106a1e66edd04af5b877ebf291380b3894b583ddccbda19 +size 72288 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50.png index 8c65f7840..d4020153c 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:983cf6804561b4287f6acfdac04e2a31810c9d3191108e2da4623cbb852258d5 -size 56707 +oid sha256:cc156be2788a1d82f587a1ae0a7b29f4b6d75f07bfd83055ecefe92b213c01fd +size 56749 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-99.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-99.png index fdc616d07..368c1b9f8 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-99.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-99.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4e6f528bf9508397a97e33d463ccd8b3584d3ebd04499b976b4989e001648626 -size 45174 +oid sha256:03da34d72c3437de1bebea5a545e1a75fb7b6a7ea309cbd88340031acc5cb852 +size 45223 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-gaussian.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-gaussian.png index 66d5557ee..c722c8ec1 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-gaussian.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-gaussian.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:69f8a085b84ee851ad1579791ff96fe6bc73551d86f376524d1194c43edf819f -size 74941 +oid sha256:309384b0062003c9af321a005ddb5e0a78db382cf2b40285dd8ca43423cb13dd +size 74975 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-reset.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-reset.png index 3f53f463b..9be3a311f 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-reset.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-reset.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0616c08a8cf008fbc976638cfb7d76d533547bdb6cbfa56071567dac3c7e703d -size 75619 +oid sha256:28291e3913c28a2933affa12810254627f352fd4059a77d3637e2756737d11c0 +size 75617 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-max-window-13.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-max-window-13.png index e2f0161b7..912374687 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-max-window-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-max-window-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e4f7893445c9f2d26f126a5ac87c1fdf0737b31fec730f754ba4c771eae5ec5b -size 116744 +oid sha256:c248700a71fb8284d039714d9b11672f6788bcd0df716c26aa936436c5035c28 +size 116784 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-13.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-13.png index fde3c576b..66971bfb1 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a73e0ef31acf4304bc9a649f0761e0d282eb4517ade89538edfe6b7d8a9b61d0 -size 75487 +oid sha256:a5fe9fce8c8e63e497b558370d0d3edd8f86b0ad27939feec02715dd84fe7353 +size 75550 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-5.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-5.png index c100dcaf3..76f6c71a2 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-5.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:00ade44cd4478bf77e7bd33ccee64e2905136e35620171f013be7e90ddaa0ba6 -size 79123 +oid sha256:b7d563033c432006b88adce07aabf0ab6a2c6559b519f6f820ebb58066f4d1b2 +size 79214 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50.png index 08b244755..90639b14f 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1eb4d6a4cfbdbfffc198dbe5987f621e5cc3fdcf574699c544a12cf3b28bcdb7 -size 82281 +oid sha256:8408e6e841d09b4c2f1704803515c878cdc46e4f7d4cd67b1634efb4efba8e98 +size 82327 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-99.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-99.png index 6868bf1ff..97e3787ea 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-99.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-99.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:758c4f444ab3792c6328e51d619c51f59f27fe2589795cc82bdef7fb5daf57e3 -size 79663 +oid sha256:4c21bbd9a9d1548d5480f9a0f0f6d4ace0abfd80e4d09e295557163310c7095d +size 79733 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-init-mean-window-5.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-init-mean-window-5.png index 3bcfe6c24..24eda7928 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-init-mean-window-5.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-init-mean-window-5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b75c179afde357fd69d7c3e918ad34e072685c536e388b1424596bd493b041e8 -size 81563 +oid sha256:2b6222d1a7291609f5d2637e97e9455a4e734b6dca4d6ebf9246ea46f32ce4de +size 81628 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-false.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-false.png index 4a8ee413a..3285e7875 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-false.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-false.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e6d9460835d55d696ffcef1bff7cb7a86672d4c7345112c62050312669055870 -size 66010 +oid sha256:b6f5d03a9929a12232628da3a0c89673a650c94737ead73bf74e0daa587e75f9 +size 65977 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-true.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-true.png index 984f1537c..63bd3d070 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-true.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-true.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7b83f6353ce227d76fbf7362218bcc35afa26216e5600cf145cbcce666bb6dd6 -size 66520 +oid sha256:182410c74215dc70140561200cdb414555e98a1ea7767205764a19cc9b72bf6d +size 66489 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-init-mean-window-5.png b/examples/notebooks/screenshots/nb-image-widget-zfish-init-mean-window-5.png index 2767f8699..e82e34241 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-init-mean-window-5.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-init-mean-window-5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:130c7a58ceee6ffe0ec58f194bce04e6f0892c97448b43ef9f90ced85c9c10ba -size 62566 +oid sha256:59eaa00b1ba39188f664719964036857df5cb143e2f70171d86e822958e98fa7 +size 62617 diff --git a/examples/notebooks/screenshots/nb-lines-3d.png b/examples/notebooks/screenshots/nb-lines-3d.png index 727450428..4ac6b8b92 100644 --- a/examples/notebooks/screenshots/nb-lines-3d.png +++ b/examples/notebooks/screenshots/nb-lines-3d.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1da691b87d324d1a5b2c4f9231be89c0c3dddb0584615a07f28a4d027dc59d5c -size 23057 +oid sha256:467cf0f08f6861c9556a412ed57e0bc6ff4499e62d576fa10dcdc2ca9ef4de6a +size 23693 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-jet-values-cosine.png b/examples/notebooks/screenshots/nb-lines-cmap-jet-values-cosine.png index 2f149e7f4..0c875da24 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-jet-values-cosine.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-jet-values-cosine.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a755f36126b805b8d63d6ea679ffa270dc20d976a9a3a2cd1420ccdf0e981474 -size 17158 +oid sha256:c870f3010432310ba6d1937410a23836de6ddb656eb83bfb1b928d7d1211bd09 +size 17263 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-jet-values.png b/examples/notebooks/screenshots/nb-lines-cmap-jet-values.png index 45a34c5f0..e193c85b3 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-jet-values.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-jet-values.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:76e55108ce7ace466fb9b90b852a1102ff8b4c931e16f05af231a854519c8467 -size 18505 +oid sha256:5c98af9372102a7fef96fe45650ca7e0b4f6e3589abd5fc3c1205130f3613c08 +size 18859 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-jet.png b/examples/notebooks/screenshots/nb-lines-cmap-jet.png index ed8138ab1..acc52de78 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-jet.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-jet.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7e463ac93808329ffa4fe421838ff8d39ce49081b888d09954405cd170af8a85 -size 16267 +oid sha256:bf3494a4cc2d4ea061fbd3465bd2133120b63bab6b248681307687e4e8e395a2 +size 16322 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-tab-10.png b/examples/notebooks/screenshots/nb-lines-cmap-tab-10.png index e10d6f5e9..d18cadb51 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-tab-10.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-tab-10.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:99c10da11e298d69cd85d587b133fe31a5528657ba456e5f0050ca0e48ed0f31 -size 14865 +oid sha256:b66364f0b7ba1da27b713e48cc757215f84143f6646451e8dfe7938d90a69102 +size 14863 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-viridis-values.png b/examples/notebooks/screenshots/nb-lines-cmap-viridis-values.png index da5693226..ecdbb0db7 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-viridis-values.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-viridis-values.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d356938c29834263d9879c3217782414d631e998ea02f11f3c00c0f64d8a63a6 -size 15084 +oid sha256:2e1b04f1ffc5c63dd93c9676bbf751f8ca698e113ce3403534f40ab868421960 +size 14974 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-viridis.png b/examples/notebooks/screenshots/nb-lines-cmap-viridis.png index ddcb6d54a..a78bd9d54 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-viridis.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-viridis.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dfd17b5b191edf58377451b0c34636d047f69de1a89087d9d2d561d967c4d236 -size 19118 +oid sha256:69166f21efd52253b2b8444c32c9770a87983b3f21afcf49f4d0a94c51e5280c +size 19206 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-white.png b/examples/notebooks/screenshots/nb-lines-cmap-white.png index 93efe221c..c61050a7d 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-white.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-white.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d3b512a195ab30075a028d19884716c5f776acd6f118e682046d6659d1e1095e -size 12962 +oid sha256:1556f875c6e5538ca289db5ced0e59530174d899d6baa19876e1ad753571d4d1 +size 12921 diff --git a/examples/notebooks/screenshots/nb-lines-colors.png b/examples/notebooks/screenshots/nb-lines-colors.png index a8af1a4be..33a751e98 100644 --- a/examples/notebooks/screenshots/nb-lines-colors.png +++ b/examples/notebooks/screenshots/nb-lines-colors.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c8db3d4ccb1e56919866a6ba510033dc903d23a5af06a961f90ddb7382d16ec7 -size 40477 +oid sha256:fb407b2008cb7a6793e7e53d487b959e1178de2a3b7fbd504b8ef3b92a7b09ea +size 40842 diff --git a/examples/notebooks/screenshots/nb-lines-data.png b/examples/notebooks/screenshots/nb-lines-data.png index 369a999b2..7447b5b86 100644 --- a/examples/notebooks/screenshots/nb-lines-data.png +++ b/examples/notebooks/screenshots/nb-lines-data.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2907942f9307eb21764ea02d363a8c6c8b7e4fbf375257b7c19225e1f7b66279 -size 54404 +oid sha256:3fe83c11993dc99085526e11db60ac6797756b2b35145458009b36e56991a0d9 +size 55297 diff --git a/examples/notebooks/screenshots/nb-lines-underlay.png b/examples/notebooks/screenshots/nb-lines-underlay.png index d6b630362..692aa76ea 100644 --- a/examples/notebooks/screenshots/nb-lines-underlay.png +++ b/examples/notebooks/screenshots/nb-lines-underlay.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:70800d1739bb5ba7d5d9d2399335ebc1ce8a0874042ed4b6256b6d041014eb18 -size 55884 +oid sha256:f65834683f945ae52612fa1ae8415d25866ccd6d776dd94798b30b08769b0387 +size 56338 diff --git a/examples/notebooks/screenshots/nb-lines.png b/examples/notebooks/screenshots/nb-lines.png index 2fcd0637f..b89e9f47c 100644 --- a/examples/notebooks/screenshots/nb-lines.png +++ b/examples/notebooks/screenshots/nb-lines.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4f70a9dd8cc631337ba1ccc7931ef2d412d0d6713d5403011994d59258f61e34 -size 37714 +oid sha256:cc0511ba508333566c902a3f1a902e76044a33a41f75bfc43ee158ff5b7bd6c7 +size 37671 From 481c5ea4a5d4e741a8b48e45fd9fe46b4b955305 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Tue, 27 Feb 2024 23:41:22 -0500 Subject: [PATCH 034/185] Update .gitattributes (#415) add gif to .gitattributes for git lfs --- .gitattributes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index e4a509285..c921772ae 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,3 @@ *.png filter=lfs diff=lfs merge=lfs -text +*.gif filter=lfs diff=lfs merge=lfs -text *.npy filter=lfs diff=lfs merge=lfs -text - From 8919d7c09d9b23a7ec63efd91450cc73f92efa9e Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Wed, 28 Feb 2024 23:29:23 -0500 Subject: [PATCH 035/185] legends, part 1 (#406) * start legends, not functional yet * add faq (#400) * fix bug when remove_graphic() is used (#405) * add 'Deleted' as a graphic feature (#404) * very basic adding line legends works * use OrderedDict for legend items * allow accessing legend items via Graphic, updating colors works * legend mesh resizes properly * remove legend items and reorder works * enforce all legend labels to be unique * add legends property to PlotArea * checks for Graphic name * highlight linegraphic when legend item is clicked * legend is moveable * remove weird characters that were committed for some reason * progress on legend grid placement, not yet working * max_rows works for legend * just allow max_rows kwarg for legends, no cols * line that snuck in from another branch * small changes --- fastplotlib/__init__.py | 7 +- fastplotlib/graphics/_base.py | 21 +- fastplotlib/layouts/_plot_area.py | 25 ++- fastplotlib/legends/__init__.py | 3 + fastplotlib/legends/legend.py | 354 ++++++++++++++++++++++++++++++ fastplotlib/utils/mesh_masks.py | 128 +++++++++++ 6 files changed, 530 insertions(+), 8 deletions(-) create mode 100644 fastplotlib/legends/__init__.py create mode 100644 fastplotlib/legends/legend.py create mode 100644 fastplotlib/utils/mesh_masks.py diff --git a/fastplotlib/__init__.py b/fastplotlib/__init__.py index b3434012f..ca872f4e4 100644 --- a/fastplotlib/__init__.py +++ b/fastplotlib/__init__.py @@ -3,7 +3,7 @@ from .layouts import Plot, GridPlot from .graphics import * from .graphics.selectors import * -from .utils import _notebook_print_banner, config +from .legends import * from wgpu.gui.auto import run @@ -23,8 +23,6 @@ "No WGPU adapters found, fastplotlib will not work." ) -_notebook_print_banner() - with open(Path(__file__).parent.joinpath("VERSION"), "r") as f: __version__ = f.read().split("\n")[0] @@ -32,5 +30,6 @@ "Plot", "GridPlot", "run", - "ImageWidget" + "ImageWidget", + "Legend", ] diff --git a/fastplotlib/graphics/_base.py b/fastplotlib/graphics/_base.py index eea78142c..4f342faa2 100644 --- a/fastplotlib/graphics/_base.py +++ b/fastplotlib/graphics/_base.py @@ -48,6 +48,7 @@ class Graphic(BaseGraphic): feature_events = {} def __init_subclass__(cls, **kwargs): + super().__init_subclass__(**kwargs) # all graphics give off a feature event when deleted cls.feature_events = {*cls.feature_events, "deleted"} @@ -62,14 +63,16 @@ def __init__( Parameters ---------- name: str, optional - name this graphic, makes it indexable within plots + name this graphic to use it as a key to access from the plot metadata: Any, optional metadata attached to this Graphic, this is for the user to manage """ + if (name is not None) and (not isinstance(name, str)): + raise TypeError("Graphic `name` must be of type ") - self.name = name + self._name = name self.metadata = metadata self.collection_index = collection_index self.registered_callbacks = dict() @@ -80,6 +83,20 @@ def __init__( self.deleted = Deleted(self, False) + self._plot_area = None + + @property + def name(self) -> Union[str, None]: + """str name reference for this item""" + return self._name + + @name.setter + def name(self, name: str): + if not isinstance(name, str): + raise TypeError("`Graphic` name must be of type ") + if self._plot_area is not None: + self._plot_area._check_graphic_name_exists(name) + @property def world_object(self) -> WorldObject: """Associated pygfx WorldObject. Always returns a proxy, real object cannot be accessed directly.""" diff --git a/fastplotlib/layouts/_plot_area.py b/fastplotlib/layouts/_plot_area.py index 819efa205..cc7526398 100644 --- a/fastplotlib/layouts/_plot_area.py +++ b/fastplotlib/layouts/_plot_area.py @@ -12,6 +12,7 @@ from ._utils import create_camera, create_controller from ..graphics._base import Graphic from ..graphics.selectors._base_selector import BaseSelector +from ..legends import Legend # dict to store Graphic instances # this is the only place where the real references to Graphics are stored in a Python session @@ -62,7 +63,7 @@ def __init__( name: str, optional name this ``subplot`` or ``plot`` - + """ self._parent: PlotArea = parent @@ -208,6 +209,8 @@ def graphics(self) -> Tuple[Graphic, ...]: proxies = list() for loc in self._graphics: p = weakref.proxy(GRAPHICS[loc]) + if p.__class__.__name__ == "Legend": + continue proxies.append(p) return tuple(proxies) @@ -222,6 +225,17 @@ def selectors(self) -> Tuple[BaseSelector, ...]: return tuple(proxies) + @property + def legends(self) -> Tuple[Legend, ...]: + """Legends in the plot area.""" + proxies = list() + for loc in self._graphics: + p = weakref.proxy(GRAPHICS[loc]) + if p.__class__.__name__ == "Legend": + proxies.append(p) + + return tuple(proxies) + @property def name(self) -> Any: """The name of this plot area""" @@ -486,6 +500,9 @@ def _check_graphic_name_exists(self, name): for s in self.selectors: graphic_names.append(s.name) + for l in self.legends: + graphic_names.append(l.name) + if name in graphic_names: raise ValueError( f"graphics must have unique names, current graphic names are:\n {graphic_names}" @@ -666,6 +683,10 @@ def __getitem__(self, name: str): if selector.name == name: return selector + for legend in self.legends: + if legend.name == name: + return legend + graphic_names = list() for g in self.graphics: graphic_names.append(g.name) @@ -681,7 +702,7 @@ def __getitem__(self, name: str): ) def __contains__(self, item: Union[str, Graphic]): - to_check = [*self.graphics, *self.selectors] + to_check = [*self.graphics, *self.selectors, *self.legends] if isinstance(item, Graphic): if item in to_check: diff --git a/fastplotlib/legends/__init__.py b/fastplotlib/legends/__init__.py new file mode 100644 index 000000000..507251f59 --- /dev/null +++ b/fastplotlib/legends/__init__.py @@ -0,0 +1,3 @@ +from .legend import Legend + +__all__ = ["Legend"] diff --git a/fastplotlib/legends/legend.py b/fastplotlib/legends/legend.py new file mode 100644 index 000000000..e29665e0f --- /dev/null +++ b/fastplotlib/legends/legend.py @@ -0,0 +1,354 @@ +from functools import partial +from collections import OrderedDict +from typing import * + +import numpy as np +import pygfx + +from ..graphics._base import Graphic +from ..graphics._features._base import FeatureEvent +from ..graphics import LineGraphic, ScatterGraphic, ImageGraphic +from ..utils import mesh_masks + + +class LegendItem: + def __init__( + self, + label: str, + color: pygfx.Color, + ): + """ + + Parameters + ---------- + label: str + + color: pygfx.Color + """ + self._label = label + self._color = color + + +class LineLegendItem(LegendItem): + def __init__( + self, + parent, + graphic: LineGraphic, + label: str, + position: Tuple[int, int] + ): + """ + + Parameters + ---------- + graphic: LineGraphic + + label: str + + position: [x, y] + """ + + if label is not None: + pass + + elif graphic.name is not None: + pass + + else: + raise ValueError("Must specify `label` or Graphic must have a `name` to auto-use as the label") + + # for now only support lines with a single color + if np.unique(graphic.colors(), axis=0).shape[0] > 1: + raise ValueError("Use colorbars for multi-colored lines, not legends") + + color = pygfx.Color(np.unique(graphic.colors(), axis=0).ravel()) + + self._parent = parent + + super().__init__(label, color) + + graphic.colors.add_event_handler(self._update_color) + + # construct Line WorldObject + data = np.array( + [[0, 0, 0], + [3, 0, 0]], + dtype=np.float32 + ) + + material = pygfx.LineMaterial + + self._line_world_object = pygfx.Line( + geometry=pygfx.Geometry(positions=data), + material=material(thickness=8, color=self._color) + ) + + # self._line_world_object.world.x = position[0] + + self._label_world_object = pygfx.Text( + geometry=pygfx.TextGeometry( + text=str(label), + font_size=6, + screen_space=False, + anchor="middle-left", + ), + material=pygfx.TextMaterial( + color="w", + outline_color="w", + outline_thickness=0, + ) + ) + + self.world_object = pygfx.Group() + self.world_object.add(self._line_world_object, self._label_world_object) + + self.world_object.world.x = position[0] + # add 10 to x to account for space for the line + self._label_world_object.world.x = position[0] + 10 + + self.world_object.world.y = position[1] + self.world_object.world.z = 2 + + self.world_object.add_event_handler(partial(self._highlight_graphic, graphic), "click") + + @property + def label(self) -> str: + return self._label + + @label.setter + def label(self, text: str): + self._parent._check_label_unique(text) + self._label_world_object.geometry.set_text(text) + + def _update_color(self, ev: FeatureEvent): + new_color = ev.pick_info["new_data"] + if np.unique(new_color, axis=0).shape[0] > 1: + raise ValueError("LegendError: LineGraphic colors no longer appropriate for legend") + + self._color = new_color[0] + self._line_world_object.material.color = pygfx.Color(self._color) + + def _highlight_graphic(self, graphic, ev): + graphic_color = pygfx.Color(np.unique(graphic.colors(), axis=0).ravel()) + + if graphic_color == self._parent.highlight_color: + graphic.colors = self._color + else: + # hacky but fine for now + orig_color = pygfx.Color(self._color) + graphic.colors = self._parent.highlight_color + self._color = orig_color + + +class Legend(Graphic): + def __init__( + self, + plot_area, + highlight_color: Union[str, tuple, np.ndarray] = "w", + max_rows: int = 5, + *args, + **kwargs + ): + """ + + Parameters + ---------- + plot_area: Union[Plot, Subplot, Dock] + plot area to put the legend in + + highlight_color: Union[str, tuple, np.ndarray], default "w" + highlight color + + max_rows: int, default 5 + maximum number of rows allowed in the legend + + """ + self._graphics: List[Graphic] = list() + + # hex id of Graphic, i.e. graphic.loc are the keys + self._items: OrderedDict[str: LegendItem] = OrderedDict() + + super().__init__(*args, **kwargs) + + group = pygfx.Group() + self._legend_items_group = pygfx.Group() + self._set_world_object(group) + + self._mesh = pygfx.Mesh( + pygfx.box_geometry(50, 10, 1), + pygfx.MeshBasicMaterial(color=pygfx.Color([0.1, 0.1, 0.1, 1]), wireframe_thickness=10) + ) + + self.world_object.add(self._mesh) + self.world_object.add(self._legend_items_group) + + self.highlight_color = pygfx.Color(highlight_color) + + self._plot_area = plot_area + self._plot_area.add_graphic(self) + + if self._plot_area.__class__.__name__ == "Dock": + if self._plot_area.size < 1: + self._plot_area.size = 100 + + # TODO: refactor with "moveable graphic" base class once that's done + self._mesh.add_event_handler(self._pointer_down, "pointer_down") + self._plot_area.renderer.add_event_handler(self._pointer_move, "pointer_move") + self._plot_area.renderer.add_event_handler(self._pointer_up, "pointer_up") + + self._last_position = None + self._initial_controller_state = self._plot_area.controller.enabled + + self._max_rows = max_rows + + self._row_counter = 0 + self._col_counter = 0 + + def graphics(self) -> Tuple[Graphic, ...]: + return tuple(self._graphics) + + def _check_label_unique(self, label): + for legend_item in self._items.values(): + if legend_item.label == label: + raise ValueError( + f"You have passed the label '{label}' which is already used for another legend item. " + f"All labels within a legend must be unique." + ) + + def add_graphic(self, graphic: Graphic, label: str = None): + if graphic in self._graphics: + raise KeyError( + f"Graphic already exists in legend with label: '{self._items[graphic.loc].label}'" + ) + + self._check_label_unique(label) + + new_col_ix = self._col_counter + new_row_ix = self._row_counter + + x_pos = 0 + y_pos = 0 + + if self._row_counter == self._max_rows: + # set counters + new_col_ix = self._col_counter + 1 + + # get x position offset for this new column of LegendItems + # start by getting the LegendItems in the previous column + prev_column_items: List[LegendItem] = list(self._items.values())[-self._max_rows:] + # x position of LegendItems in previous column + x_pos = prev_column_items[-1].world_object.world.x + max_width = 0 + # get width of widest LegendItem in previous column to add to x_pos offset for this column + for item in prev_column_items: + bbox = item.world_object.get_world_bounding_box() + width, height, depth = bbox.ptp(axis=0) + max_width = max(max_width, width) + + # x position offset for this new column + x_pos = x_pos + max_width + 15 # add 15 for spacing + + # rest row index for next iteration + new_row_ix = 1 + else: + if len(self._items) > 0: + x_pos = list(self._items.values())[-1].world_object.world.x + + y_pos = new_row_ix * -10 + new_row_ix = self._row_counter + 1 + + if isinstance(graphic, LineGraphic): + legend_item = LineLegendItem(self, graphic, label, position=(x_pos, y_pos)) + else: + raise ValueError("Legend only supported for LineGraphic for now.") + + self._legend_items_group.add(legend_item.world_object) + self._reset_mesh_dims() + + self._graphics.append(graphic) + self._items[graphic.loc] = legend_item + + graphic.deleted.add_event_handler(partial(self.remove_graphic, graphic)) + + self._col_counter = new_col_ix + self._row_counter = new_row_ix + + def _reset_mesh_dims(self): + bbox = self._legend_items_group.get_world_bounding_box() + + width, height, _ = bbox.ptp(axis=0) + + self._mesh.geometry.positions.data[mesh_masks.x_right] = width + 7 + self._mesh.geometry.positions.data[mesh_masks.x_left] = -5 + self._mesh.geometry.positions.data[mesh_masks.y_bottom] = 0 + self._mesh.geometry.positions.data[mesh_masks.y_bottom] = -height - 3 + self._mesh.geometry.positions.update_range() + + def remove_graphic(self, graphic: Graphic): + self._graphics.remove(graphic) + legend_item = self._items.pop(graphic.loc) + self._legend_items_group.remove(legend_item.world_object) + self._reset_item_positions() + + def _reset_item_positions(self): + for i, (graphic_loc, legend_item) in enumerate(self._items.items()): + y_pos = i * -10 + legend_item.world_object.world.y = y_pos + + self._reset_mesh_dims() + + def reorder(self, labels: Iterable[str]): + all_labels = [legend_item.label for legend_item in self._items.values()] + + if not set(labels) == set(all_labels): + raise ValueError("Must pass all existing legend labels") + + new_items = OrderedDict() + + for label in labels: + for graphic_loc, legend_item in self._items.items(): + if label == legend_item.label: + new_items[graphic_loc] = self._items.pop(graphic_loc) + break + + self._items = new_items + self._reset_item_positions() + + def _pointer_down(self, ev): + self._last_position = self._plot_area.map_screen_to_world(ev) + self._initial_controller_state = self._plot_area.controller.enabled + + def _pointer_move(self, ev): + if self._last_position is None: + return + + self._plot_area.controller.enabled = False + + world_pos = self._plot_area.map_screen_to_world(ev) + + # outside viewport + if world_pos is None: + return + + delta = world_pos - self._last_position + + self.world_object.world.x = self.world_object.world.x + delta[0] + self.world_object.world.y = self.world_object.world.y + delta[1] + + self._last_position = world_pos + + self._plot_area.controller.enabled = self._initial_controller_state + + def _pointer_up(self, ev): + self._last_position = None + if self._initial_controller_state is not None: + self._plot_area.controller.enabled = self._initial_controller_state + + def __getitem__(self, graphic: Graphic) -> LegendItem: + if not isinstance(graphic, Graphic): + raise TypeError("Must index Legend with Graphics") + + if graphic.loc not in self._items.keys(): + raise KeyError("Graphic not in legend") + + return self._items[graphic.loc] diff --git a/fastplotlib/utils/mesh_masks.py b/fastplotlib/utils/mesh_masks.py new file mode 100644 index 000000000..600e5ab6d --- /dev/null +++ b/fastplotlib/utils/mesh_masks.py @@ -0,0 +1,128 @@ +import numpy as np + + +""" +positions for indexing the BoxGeometry to set the "width" and "size" of the box +hacky, but I don't think we can morph meshes in pygfx yet: https://github.com/pygfx/pygfx/issues/346 +""" + +x_right = np.array( + [ + True, + True, + True, + True, + False, + False, + False, + False, + False, + True, + False, + True, + True, + False, + True, + False, + False, + True, + False, + True, + True, + False, + True, + False, + ] +) + +x_left = np.array( + [ + False, + False, + False, + False, + True, + True, + True, + True, + True, + False, + True, + False, + False, + True, + False, + True, + True, + False, + True, + False, + False, + True, + False, + True, + ] +) + +y_top = np.array( + [ + False, + True, + False, + True, + False, + True, + False, + True, + True, + True, + True, + True, + False, + False, + False, + False, + False, + False, + True, + True, + False, + False, + True, + True, + ] +) + +y_bottom = np.array( + [ + True, + False, + True, + False, + True, + False, + True, + False, + False, + False, + False, + False, + True, + True, + True, + True, + True, + True, + False, + False, + True, + True, + False, + False, + ] +) + +x_right = (x_right, 0) +x_left = (x_left, 0) +y_top = (y_top, 1) +y_bottom = (y_bottom, 1) \ No newline at end of file From 0fbc7d44da270f67d8d49b0da1ad626a82e0b815 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Mon, 4 Mar 2024 06:01:04 -0500 Subject: [PATCH 036/185] use pytest<8 until things catch up (#419) --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 8e8977b57..1d274fc0a 100644 --- a/setup.py +++ b/setup.py @@ -33,7 +33,7 @@ "tests": [ - "pytest", + "pytest<8.0.0", "nbmake", "scipy", "imageio[pyav]", @@ -47,7 +47,7 @@ "tests-desktop": [ - "pytest", + "pytest<8.0.0", "scipy", "imageio", "scikit-learn", From 30cf1fc10371b2b74c280804aefbdc7f32852920 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Mon, 4 Mar 2024 06:16:35 -0500 Subject: [PATCH 037/185] more detailed contrib docs (#418) --- CONTRIBUTING.md | 171 +++++++++++++++++++++++++++++- fastplotlib/layouts/_plot_area.py | 23 +++- fastplotlib/layouts/_subplot.py | 3 +- 3 files changed, 191 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 23810b956..84114c61f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,7 +2,7 @@ Contributions are welcome! :smile: -## Instructions +## Installation 1. Fork the repo to your own GitHub account, click the "Fork" button at the top: @@ -53,3 +53,172 @@ git push origin my-new-feature-branch ``` 4. Finally make a **draft** PR against the `main` branch. When you think the PR is ready, mark it for review to trigger tests using our CI pipeline. If you need to make changes, please set the PR to a draft when pushing further commits until it's ready for review scion. We will get back to your with any further suggestions! + +## How fastplotlib works + +Fastplotlib uses the [`pygfx`](https://github.com/pygfx/pygfx) rendering engine to give users a high-level scientific +plotting library. Some degree of familiarity with [`pygfx`](https://github.com/pygfx/pygfx) or rendering engines may +be useful depending on the type of contribution you're working on. + +There are currently 2 major subpackages within `fastplotlib`, `layouts` and `graphics`. The two user-facing public +classes within `layouts` are `Plot` and `GridPlot`. A user is intended to create either a `Plot` or `GridPlot`, and +then add *Graphics* to that layout, such as an `ImageGraphic`, `LineGraphic`, etc. + +### Graphics + +A `Graphic` is something that can be added to a `PlotArea` (described in detail in a later section). All the various +fastplotlib graphics, such as `ImageGraphic`, `ScatterGraphic`, etc. inherit from the `Graphic` base class in +`fastplotlib/graphics/_base.py`. It has a few properties that mostly wrap `pygfx` `WorldObject` properties and transforms. +These might change in the future (ex. `Graphic.position_x` etc.). + +All graphics can be given a string name for the user's convenience. This allows graphics to be easily accessed from +plots, ex: `plot["some_image"]`. + +All graphics contain a `world_object` property which is just the `pygfx.WorldObject` that this graphic uses. Fastplotlib +keeps a *private* global dictionary of all `WorldObject` instances and users are only given a weakref proxy to this world object. +This is due to garbage collection. This may be quite complicated for beginners, for more details see this PR: https://github.com/fastplotlib/fastplotlib/pull/160 . +If you are curious or have more questions on garbage collection in fastplotlib you're welcome to post an issue :D. + +#### Graphic Features + +There is one important thing that `fastplotlib` uses which we call "graphic features". +The "graphic features" subpackage can be found at `fastplotlib/graphics/_features`. As we can see this +is a private subpackage and never meant to be accessible to users. In `fastplotlib` "graphic features" are the various +aspects of a graphic that the user can change. Users can also run callbacks whenever a graphic feature changes. + +##### LineGraphic + +For example let's look at `LineGraphic` in `fastplotlib/graphics/line.py`. Every graphic has a class variable called +`feature_events` which is a set of all graphic features. It has the following graphic features: "data", "colors", "cmap", "thickness", "present". + +Now look at the constructor for `LineGraphic`, it first creates an instance of `PointsDataFeature`. This is basically a +class that wraps the positions buffer, the vertex positions that define the line, and provides additional useful functionality. +For example, every time that the `data` is changed event handlers will be called (if any event handlers are registered). + +`ColorFeature`behaves similarly, but it can perform additional parsing that can create the colors buffer from different forms of user input. For example if a user runs: +`line_graphic.colors = "blue"`, then `ColorFeature.__setitem__()` will create a buffer that corresponds to what `pygfx.Color` thinks is "blue". +Users can also take advantage of fancy indexing, ex: `line_graphics.colors[bool_array] = "red"` :smile: + +`LineGraphic` also has a `CmapFeature`, this is a subclass of `ColorFeature` which can parse colormaps, for example: +`line_graphic.cmap = "jet"` or even `line_graphic.cmap[50:] = "viridis"`. + +`LineGraphic` also has `ThicknessFeature` which is pretty simple, `PresentFeature` which indicates if a graphic is +currently in the scene, and `DeletedFeature` which is useful if you need callbacks to indicate that the graphic has been +deleted (for example, removing references to a graphic from a legend). + +Other graphics have graphic features that are relevant to them, for example `ImageGraphic` has a `cmap` feature which is +unique to images or heatmaps. + +#### Selectors + +Selectors are a fairly new subpackage at `fastplotlib/graphics/selectors` which is likely to change significantly +after https://github.com/pygfx/pygfx/pull/665 . This subpackage contains selection tools, such as line selectors +(horizontal or vertical lines that can be moved), linear region selectors, and a primitive polygon drawing selection tool. +All selector tools inherit from `BaseSelector` in `graphics/selectors/_base_selector.py` but this is likely to change +after the aforementioned `Input` class PR in `pygfx` and after https://github.com/fastplotlib/fastplotlib/pull/413 . + +### Layouts + +#### PlotArea + +This is the main base class within layouts. Every kind of "plot area", whether it's a single `Plot`, subplots within a +`GridPlot`, or `Dock` area, use `PlotArea` in some way. + +`PlotArea` has the following key properties that allow it to be a "plot area" that can be used to view graphical objects: + +* scene - instance of `pygfx.Scene` +* canvas - instance of `WgpuCanvas` +* renderer - instance of `pygfx.WgpuRenderer` +* viewport - instance of `pygfx.Viewport` +* camera - instance of `pygfx.PerspectiveCamera`, we always just use `PerspectiveCamera` and just set `camera.fov = 0` for orthographic projections +* controller - instance of `pygfx.Controller` + +Abstract method that must be implemented in subclasses: + +* get_rect - musut return [x, y, width, height] that defines the viewport rect for this `PlotArea` + +Properties specifically used by subplots in a gridplot: + +* parent - A parent if relevant, used by individual `Subplots` in `GridPlot`, and by `Dock` which are "docked" subplots at the edges of a subplot. +* position - if a subplot within a gridplot, it is the position of this subplot within the `GridPlot` + +Other important properties: + +* graphics - a tuple of weakref proxies to all `Graphics` within this `PlotArea`, users are only given weakref proxies to `Graphic` objects, all `Graphic` objects are stored in a private global dict. +* selectors - a tuple of weakref proxies to all selectors within this `PlotArea` +* legend - a tuple of weakref proxies to all legend graphics within this `PlotArea` +* name - plot areas are allowed to have names that the user can use for their convenience + +Important methods: + +* add_graphic - add a `Graphic` to the `PlotArea`, append to the end of the `PlotArea._graphics` list +* insert_graphic - insert a `Graphic` to the `PlotArea`, insert to a specific position of the `PlotArea._graphics` list +* remove_graphic - remove a graphic from the `Scene`, **does not delete it** +* delete_graphic - delete a graphic from the `PlotArea`, performs garbage collection +* clear - deletes all graphics from the `PlotArea` +* center_graphic - center camera w.r.t. a `Graphic` +* center_scene - center camera w.r.t. entire `Scene` +* auto_scale - Auto-scale the camera w.r.t to the `Scene` + +In addition, `PlotArea` supports `__getitem__`, so you can do: `plot_area["graphic_name"]` to retrieve a `Graphic` by +name :smile: + +You can also check if a `PlotArea` has certain graphics, ex: `"some_image_name" in plot_area`, or `graphic_instance in plot_area` + +#### Subplot + +This class inherits from `PlotArea` and `GraphicMethodsMixin`. + +`GraphicMethodsMixin` is a simple class that just has all the `add_` methods. It is autogenerated by a utility script like this: + +```bash +python fastplotlib/utils/generate_add_methods.py +``` + +Each `add_` method basically creates an instance of `Graphic`, adds it to the `Subplot`, and returns a weakref +proxy to the `Graphic`. + +Subplot has one property that is not in `PlotArea`: + +* docks: a `dict` of `PlotAreas` which are located at the "top", "right", "left", and "bottom" edges of a `Subplot`. By default their size is `0`. They are useful for putting things like histogram LUT tools. + +The key method in `Subplot` is an implementation of `get_rect` that returns the viewport rect for this subplot. + +#### Plot, GridPlot, and Frame + +Now that we have understood `PlotArea` and `Subplot` we need a way for the user to create either single plots or gridplots +and display them! + +There's one more class to talk about, `Frame`. This is a class that "frames" a `Plot` or `GridPlot`. Depending on +whether the plot's `Canvas` is a Qt or jupyter canvas, `Frame.show()` will create a plot toolbar and place this toolbar +below the `Canvas`. If using a glfw canvas it just returns the canvas. + +`Plot` and `GridPlot` both inherit from `Frame` which gives them `show()`. `Plot` is just a single `Subplot` with the +addition of `Frame`. `GridPlot.__init__` basically does a lot of parsing of user arguments to determine how to create +the subplots. All subplots within a `GridPlot` share the same canvas and use different viewports to create the subplots. + +## Running tests + +The CI pipeline for a plotting library is is supposed to produce things that "look visually correct" is a bit more +complicated than the CI pipeline for most libraries. + +Our CI pipeline is modelled after `pygfx`. Basically, a bunch of examples exist within the `examples` dir. Each of these +examples are run and a screenshot of the canvas is taken and compared with a ground-truth screenshot that we have +manually inspected. Screenshots are stored using `git-lfs`. + +At the moment these tests will produce slightly different imperceptible (to a human) results on different hardware, but +nonetheless the image arrays will have a small difference. Because of this, we only run tests on GitHub actions. There +is a specific actions workflow that only generate screenshots and doesn't run tests which is run specifically to create +ground-truth screenshot on GitHub actions servers to use for test. + +If your contribution modifies a ground-truth test screenshot the general workflow is like this, if you have questions +don't hesitate to ask us :smile: + +1. Create a PR with your code changes, **do not upload any new/modified test screenshots" +1. See if the CI failed, if so it will indicate the specific examples that have failed. +1. Go to the details page for the "Screenshots / Regenerate" workflow. Click on "Summary" in the top left, and download +the build artifact which is named "screenshots". This is a zip file of generated ground-truth screenshots. +1. Visually inspect the specific new/modified screenshot that corresponds to your code change. Make sure it looks like, +past a copy of it in the PR as a reply +1. Copy over the new/modified screenshots to your local repo. +1. Add and commit the new/modified screenshot files, and push. The tests should now pass. diff --git a/fastplotlib/layouts/_plot_area.py b/fastplotlib/layouts/_plot_area.py index cc7526398..d4fc62e1f 100644 --- a/fastplotlib/layouts/_plot_area.py +++ b/fastplotlib/layouts/_plot_area.py @@ -108,7 +108,7 @@ def __init__( # several read-only properties @property def parent(self): - """The parent PlotArea""" + """A parent if relevant, used by individual Subplots in GridPlot""" return self._parent @property @@ -237,16 +237,31 @@ def legends(self) -> Tuple[Legend, ...]: return tuple(proxies) @property - def name(self) -> Any: + def name(self) -> str: """The name of this plot area""" return self._name @name.setter - def name(self, name: Any): + def name(self, name: str): + if name is None: + self._name = None + return + + if not isinstance(name, str): + raise TypeError("PlotArea `name` must be of type ") self._name = name def get_rect(self) -> Tuple[float, float, float, float]: - """allows setting the region occupied by the viewport w.r.t. the parent""" + """ + Returns the viewport rect to define the rectangle + occupied by the viewport w.r.t. the Canvas. + + If this is a subplot within a GridPlot, it returns the rectangle + for only this subplot w.r.t. the parent canvas. + + Must return: [x_pos, y_pos, width_viewport, height_viewport] + + """ raise NotImplementedError("Must be implemented in subclass") def map_screen_to_world( diff --git a/fastplotlib/layouts/_subplot.py b/fastplotlib/layouts/_subplot.py index e9eae7603..e776fddb6 100644 --- a/fastplotlib/layouts/_subplot.py +++ b/fastplotlib/layouts/_subplot.py @@ -90,6 +90,8 @@ def __init__( self._grid: pygfx.GridHelper = pygfx.GridHelper(size=100, thickness=1) + self._title_graphic: TextGraphic = None + super(Subplot, self).__init__( parent=parent, position=position, @@ -107,7 +109,6 @@ def __init__( self.docks[pos] = dv self.children.append(dv) - self._title_graphic: TextGraphic = None if self.name is not None: self.set_title(self.name) From 835f0c56717a175f9088f3c53b18f05e634ebb7a Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Mon, 4 Mar 2024 07:11:41 -0500 Subject: [PATCH 038/185] ruff format everything (#421) * ruff format everything * dont use formatting on GraphicMethodsMixin since it is autogenerated --- fastplotlib/__init__.py | 4 +- fastplotlib/graphics/_base.py | 2 +- fastplotlib/graphics/_features/__init__.py | 7 ++- fastplotlib/graphics/_features/_colors.py | 10 +++- fastplotlib/graphics/_features/_sizes.py | 16 +++-- fastplotlib/graphics/image.py | 6 +- fastplotlib/graphics/scatter.py | 4 +- .../graphics/selectors/_base_selector.py | 2 +- fastplotlib/graphics/selectors/_linear.py | 19 +++--- .../graphics/selectors/_linear_region.py | 20 +++---- .../graphics/selectors/_mesh_positions.py | 1 - fastplotlib/graphics/selectors/_polygon.py | 26 +++++---- fastplotlib/graphics/selectors/_sync.py | 4 +- fastplotlib/graphics/text.py | 3 +- fastplotlib/layouts/_defaults.py | 2 - fastplotlib/layouts/_frame/_frame.py | 24 ++++---- .../layouts/_frame/_ipywidget_toolbar.py | 19 ++++-- fastplotlib/layouts/_frame/_jupyter_output.py | 13 +++-- fastplotlib/layouts/_frame/_qt_output.py | 9 +-- fastplotlib/layouts/_frame/_qt_toolbar.py | 30 +++++++--- fastplotlib/layouts/_gridplot.py | 28 ++++++--- fastplotlib/layouts/_plot_area.py | 38 ++++++------ fastplotlib/legends/legend.py | 58 ++++++++++--------- fastplotlib/utils/__init__.py | 4 +- fastplotlib/utils/_gpu_info.py | 16 ++--- fastplotlib/utils/generate_add_methods.py | 39 +++++++------ fastplotlib/utils/mesh_masks.py | 2 +- fastplotlib/widgets/histogram_lut.py | 47 +++++++++------ fastplotlib/widgets/image.py | 27 +++++---- 29 files changed, 275 insertions(+), 205 deletions(-) diff --git a/fastplotlib/__init__.py b/fastplotlib/__init__.py index ca872f4e4..f0f3cc7b1 100644 --- a/fastplotlib/__init__.py +++ b/fastplotlib/__init__.py @@ -19,9 +19,7 @@ adapters = [a.request_adapter_info() for a in enumerate_adapters()] if len(adapters) < 1: - raise IndexError( - "No WGPU adapters found, fastplotlib will not work." - ) + raise IndexError("No WGPU adapters found, fastplotlib will not work.") with open(Path(__file__).parent.joinpath("VERSION"), "r") as f: __version__ = f.read().split("\n")[0] diff --git a/fastplotlib/graphics/_base.py b/fastplotlib/graphics/_base.py index 4f342faa2..91ccb143e 100644 --- a/fastplotlib/graphics/_base.py +++ b/fastplotlib/graphics/_base.py @@ -315,7 +315,7 @@ def link( feature=feature, new_data=new_data, callback=callback, - bidirectional=False # else infinite recursion, otherwise target will call + bidirectional=False, # else infinite recursion, otherwise target will call # this instance .link(), and then it will happen again etc. ) diff --git a/fastplotlib/graphics/_features/__init__.py b/fastplotlib/graphics/_features/__init__.py index a1769b010..fb25db287 100644 --- a/fastplotlib/graphics/_features/__init__.py +++ b/fastplotlib/graphics/_features/__init__.py @@ -3,7 +3,12 @@ from ._sizes import PointsSizesFeature from ._present import PresentFeature from ._thickness import ThicknessFeature -from ._base import GraphicFeature, GraphicFeatureIndexable, FeatureEvent, to_gpu_supported_dtype +from ._base import ( + GraphicFeature, + GraphicFeatureIndexable, + FeatureEvent, + to_gpu_supported_dtype, +) from ._selection_features import LinearSelectionFeature, LinearRegionSelectionFeature from ._deleted import Deleted diff --git a/fastplotlib/graphics/_features/_colors.py b/fastplotlib/graphics/_features/_colors.py index b6723b34b..ad9e673ef 100644 --- a/fastplotlib/graphics/_features/_colors.py +++ b/fastplotlib/graphics/_features/_colors.py @@ -1,7 +1,13 @@ import numpy as np import pygfx -from ...utils import make_colors, get_cmap_texture, make_pygfx_colors, parse_cmap_values, quick_min_max +from ...utils import ( + make_colors, + get_cmap_texture, + make_pygfx_colors, + parse_cmap_values, + quick_min_max, +) from ._base import ( GraphicFeature, GraphicFeatureIndexable, @@ -426,4 +432,4 @@ def vmax(self) -> float: @vmax.setter def vmax(self, value: float): """Maximum contrast limit.""" - self._parent._material.clim = (self._parent._material.clim[0], value) \ No newline at end of file + self._parent._material.clim = (self._parent._material.clim[0], value) diff --git a/fastplotlib/graphics/_features/_sizes.py b/fastplotlib/graphics/_features/_sizes.py index e951064e4..8fceb8df3 100644 --- a/fastplotlib/graphics/_features/_sizes.py +++ b/fastplotlib/graphics/_features/_sizes.py @@ -34,12 +34,16 @@ def __getitem__(self, item): def _fix_sizes(self, sizes, parent): graphic_type = parent.__class__.__name__ - + n_datapoints = parent.data().shape[0] if not isinstance(sizes, (list, tuple, np.ndarray)): - sizes = np.full(n_datapoints, sizes, dtype=np.float32) # force it into a float to avoid weird gpu errors - elif not isinstance(sizes, np.ndarray): # if it's not a ndarray already, make it one - sizes = np.array(sizes, dtype=np.float32) # read it in as a numpy.float32 + sizes = np.full( + n_datapoints, sizes, dtype=np.float32 + ) # force it into a float to avoid weird gpu errors + elif not isinstance( + sizes, np.ndarray + ): # if it's not a ndarray already, make it one + sizes = np.array(sizes, dtype=np.float32) # read it in as a numpy.float32 if (sizes.ndim != 1) or (sizes.size != parent.data().shape[0]): raise ValueError( f"sequence of `sizes` must be 1 dimensional with " @@ -49,7 +53,9 @@ def _fix_sizes(self, sizes, parent): sizes = to_gpu_supported_dtype(sizes) if any(s < 0 for s in sizes): - raise ValueError("All sizes must be positive numbers greater than or equal to 0.0.") + raise ValueError( + "All sizes must be positive numbers greater than or equal to 0.0." + ) if sizes.ndim == 1: if graphic_type == "ScatterGraphic": diff --git a/fastplotlib/graphics/image.py b/fastplotlib/graphics/image.py index 3d629c10f..1cad33f22 100644 --- a/fastplotlib/graphics/image.py +++ b/fastplotlib/graphics/image.py @@ -345,11 +345,7 @@ def col_chunk_index(self, index: int): class HeatmapGraphic(Graphic, Interaction, _AddSelectorsMixin): - feature_events = { - "data", - "cmap", - "present" - } + feature_events = {"data", "cmap", "present"} def __init__( self, diff --git a/fastplotlib/graphics/scatter.py b/fastplotlib/graphics/scatter.py index 63689fad9..f6104aeb7 100644 --- a/fastplotlib/graphics/scatter.py +++ b/fastplotlib/graphics/scatter.py @@ -90,7 +90,9 @@ def __init__( super(ScatterGraphic, self).__init__(*args, **kwargs) world_object = pygfx.Points( - pygfx.Geometry(positions=self.data(), sizes=self.sizes(), colors=self.colors()), + pygfx.Geometry( + positions=self.data(), sizes=self.sizes(), colors=self.colors() + ), material=pygfx.PointsMaterial(color_mode="vertex", vertex_sizes=True), ) diff --git a/fastplotlib/graphics/selectors/_base_selector.py b/fastplotlib/graphics/selectors/_base_selector.py index e6796f270..6c1f8c6ae 100644 --- a/fastplotlib/graphics/selectors/_base_selector.py +++ b/fastplotlib/graphics/selectors/_base_selector.py @@ -44,7 +44,7 @@ def __init__( hover_responsive: Tuple[WorldObject, ...] = None, arrow_keys_modifier: str = None, axis: str = None, - name: str = None + name: str = None, ): if edges is None: edges = tuple() diff --git a/fastplotlib/graphics/selectors/_linear.py b/fastplotlib/graphics/selectors/_linear.py index 16ccab1b4..ff617c5e3 100644 --- a/fastplotlib/graphics/selectors/_linear.py +++ b/fastplotlib/graphics/selectors/_linear.py @@ -28,10 +28,10 @@ def limits(self, values: Tuple[float, float]): # check that `values` is an iterable of two real numbers # using `Real` here allows it to work with builtin `int` and `float` types, and numpy scaler types if len(values) != 2 or not all(map(lambda v: isinstance(v, Real), values)): - raise TypeError( - "limits must be an iterable of two numeric values" - ) - self._limits = tuple(map(round, values)) # if values are close to zero things get weird so round them + raise TypeError("limits must be an iterable of two numeric values") + self._limits = tuple( + map(round, values) + ) # if values are close to zero things get weird so round them self.selection._limits = self._limits # TODO: make `selection` arg in graphics data space not world space @@ -267,11 +267,7 @@ def make_ipywidget_slider(self, kind: str = "IntSlider", **kwargs): return slider - def add_ipywidget_handler( - self, - widget, - step: Union[int, float] = None - ): + def add_ipywidget_handler(self, widget, step: Union[int, float] = None): """ Bidirectionally connect events with a ipywidget slider @@ -285,7 +281,10 @@ def add_ipywidget_handler( """ - if not isinstance(widget, (ipywidgets.IntSlider, ipywidgets.FloatSlider, ipywidgets.FloatLogSlider)): + if not isinstance( + widget, + (ipywidgets.IntSlider, ipywidgets.FloatSlider, ipywidgets.FloatLogSlider), + ): raise TypeError( f"`widget` must be one of: ipywidgets.IntSlider, ipywidgets.FloatSlider, or ipywidgets.FloatLogSlider\n" f"You have passed a: <{type(widget)}" diff --git a/fastplotlib/graphics/selectors/_linear_region.py b/fastplotlib/graphics/selectors/_linear_region.py index 2a7547d5b..4ffbd2cc2 100644 --- a/fastplotlib/graphics/selectors/_linear_region.py +++ b/fastplotlib/graphics/selectors/_linear_region.py @@ -27,10 +27,10 @@ def limits(self, values: Tuple[float, float]): # check that `values` is an iterable of two real numbers # using `Real` here allows it to work with builtin `int` and `float` types, and numpy scaler types if len(values) != 2 or not all(map(lambda v: isinstance(v, Real), values)): - raise TypeError( - "limits must be an iterable of two numeric values" - ) - self._limits = tuple(map(round, values)) # if values are close to zero things get weird so round them + raise TypeError("limits must be an iterable of two numeric values") + self._limits = tuple( + map(round, values) + ) # if values are close to zero things get weird so round them self.selection._limits = self._limits def __init__( @@ -243,7 +243,7 @@ def __init__( hover_responsive=self.edges, arrow_keys_modifier=arrow_keys_modifier, axis=axis, - name=name + name=name, ) def get_selected_data( @@ -417,11 +417,7 @@ def make_ipywidget_slider(self, kind: str = "IntRangeSlider", **kwargs): return slider - def add_ipywidget_handler( - self, - widget, - step: Union[int, float] = None - ): + def add_ipywidget_handler(self, widget, step: Union[int, float] = None): """ Bidirectionally connect events with a ipywidget slider @@ -434,7 +430,9 @@ def add_ipywidget_handler( step size, if ``None`` 100 steps are created """ - if not isinstance(widget, (ipywidgets.IntRangeSlider, ipywidgets.FloatRangeSlider)): + if not isinstance( + widget, (ipywidgets.IntRangeSlider, ipywidgets.FloatRangeSlider) + ): raise TypeError( f"`widget` must be one of: ipywidgets.IntRangeSlider or ipywidgets.FloatRangeSlider\n" f"You have passed a: <{type(widget)}" diff --git a/fastplotlib/graphics/selectors/_mesh_positions.py b/fastplotlib/graphics/selectors/_mesh_positions.py index 07ff60498..a22b22b17 100644 --- a/fastplotlib/graphics/selectors/_mesh_positions.py +++ b/fastplotlib/graphics/selectors/_mesh_positions.py @@ -1,2 +1 @@ import numpy as np - diff --git a/fastplotlib/graphics/selectors/_polygon.py b/fastplotlib/graphics/selectors/_polygon.py index b347da0f4..44d378329 100644 --- a/fastplotlib/graphics/selectors/_polygon.py +++ b/fastplotlib/graphics/selectors/_polygon.py @@ -16,7 +16,6 @@ def __init__( parent: Graphic = None, name: str = None, ): - self.parent = parent group = pygfx.Group() @@ -47,7 +46,9 @@ def _add_plot_area_hook(self, plot_area): self._plot_area.renderer.add_event_handler(self._add_segment, "click") # pointer move to change endpoint of segment - self._plot_area.renderer.add_event_handler(self._move_segment_endpoint, "pointer_move") + self._plot_area.renderer.add_event_handler( + self._move_segment_endpoint, "pointer_move" + ) # click to finish existing segment self._plot_area.renderer.add_event_handler(self._finish_segment, "click") @@ -69,7 +70,9 @@ def _add_segment(self, ev): new_line = pygfx.Line( geometry=pygfx.Geometry(positions=data.astype(np.float32)), - material=pygfx.LineMaterial(thickness=self.edge_width, color=pygfx.Color(self.edge_color)) + material=pygfx.LineMaterial( + thickness=self.edge_width, color=pygfx.Color(self.edge_color) + ), ) self.world_object.add(new_line) @@ -86,7 +89,9 @@ def _move_segment_endpoint(self, ev): return # change endpoint - self.world_object.children[-1].geometry.positions.data[1] = np.array([world_pos]).astype(np.float32) + self.world_object.children[-1].geometry.positions.data[1] = np.array( + [world_pos] + ).astype(np.float32) self.world_object.children[-1].geometry.positions.update_range() def _finish_segment(self, ev): @@ -114,14 +119,15 @@ def _finish_polygon(self, ev): return # make new line to connect first and last vertices - data = np.vstack([ - world_pos, - self.world_object.children[0].geometry.positions.data[0] - ]) + data = np.vstack( + [world_pos, self.world_object.children[0].geometry.positions.data[0]] + ) new_line = pygfx.Line( geometry=pygfx.Geometry(positions=data.astype(np.float32)), - material=pygfx.LineMaterial(thickness=self.edge_width, color=pygfx.Color(self.edge_color)) + material=pygfx.LineMaterial( + thickness=self.edge_width, color=pygfx.Color(self.edge_color) + ), ) self.world_object.add(new_line) @@ -130,7 +136,7 @@ def _finish_polygon(self, ev): self._add_segment: "click", self._move_segment_endpoint: "pointer_move", self._finish_segment: "click", - self._finish_polygon: "double_click" + self._finish_polygon: "double_click", } for handler, event in handlers.items(): diff --git a/fastplotlib/graphics/selectors/_sync.py b/fastplotlib/graphics/selectors/_sync.py index 9414a2e20..ce903aab8 100644 --- a/fastplotlib/graphics/selectors/_sync.py +++ b/fastplotlib/graphics/selectors/_sync.py @@ -3,7 +3,9 @@ class Synchronizer: - def __init__(self, *selectors: LinearSelector, key_bind: Union[str, None] = "Shift"): + def __init__( + self, *selectors: LinearSelector, key_bind: Union[str, None] = "Shift" + ): """ Synchronize the movement of `Selectors`. Selectors will move in sync only when the selected `"key_bind"` is used during the mouse movement event. Valid key binds are: ``"Control"``, ``"Shift"`` and ``"Alt"``. diff --git a/fastplotlib/graphics/text.py b/fastplotlib/graphics/text.py index a159d9560..a8a873287 100644 --- a/fastplotlib/graphics/text.py +++ b/fastplotlib/graphics/text.py @@ -17,7 +17,7 @@ def __init__( screen_space: bool = True, anchor: str = "middle-center", *args, - **kwargs + **kwargs, ): """ Create a text Graphic @@ -144,4 +144,3 @@ def outline_color(self, color: Union[str, np.ndarray]): raise ValueError("Outline color must be of type str or np.ndarray") self.world_object.material.outline_color = color - diff --git a/fastplotlib/layouts/_defaults.py b/fastplotlib/layouts/_defaults.py index b28b04f64..8b1378917 100644 --- a/fastplotlib/layouts/_defaults.py +++ b/fastplotlib/layouts/_defaults.py @@ -1,3 +1 @@ - - diff --git a/fastplotlib/layouts/_frame/_frame.py b/fastplotlib/layouts/_frame/_frame.py index abd79759e..2b76b8124 100644 --- a/fastplotlib/layouts/_frame/_frame.py +++ b/fastplotlib/layouts/_frame/_frame.py @@ -27,15 +27,14 @@ def __call__(self, *args, **kwargs): JupyterOutputContext = UnavailableOutputContext( "Jupyter", "You must install fastplotlib using the `'notebook'` option to use this context:\n" - 'pip install "fastplotlib[notebook]"' + 'pip install "fastplotlib[notebook]"', ) if CANVAS_OPTIONS_AVAILABLE["qt"]: from ._qt_output import QOutputContext else: QtOutput = UnavailableOutputContext( - "Qt", - "You must install `PyQt6` to use this output context" + "Qt", "You must install `PyQt6` to use this output context" ) @@ -45,6 +44,7 @@ class Frame: Gives them their `show()` call that returns the appropriate output context. """ + def __init__(self): self._output = None @@ -83,13 +83,13 @@ def start_render(self): self.canvas.set_logical_size(*self._starting_size) def show( - self, - autoscale: bool = True, - maintain_aspect: bool = None, - toolbar: bool = True, - sidecar: bool = False, - sidecar_kwargs: dict = None, - add_widgets: list = None, + self, + autoscale: bool = True, + maintain_aspect: bool = None, + toolbar: bool = True, + sidecar: bool = False, + sidecar_kwargs: dict = None, + add_widgets: list = None, ): """ Begins the rendering event loop and shows the plot in the desired output context (jupyter, qt or glfw). @@ -168,9 +168,7 @@ def show( elif self.canvas.__class__.__name__ == "QWgpuCanvas": self._output = QOutputContext( - frame=self, - make_toolbar=toolbar, - add_widgets=add_widgets + frame=self, make_toolbar=toolbar, add_widgets=add_widgets ) else: # assume GLFW, the output context is just the canvas diff --git a/fastplotlib/layouts/_frame/_ipywidget_toolbar.py b/fastplotlib/layouts/_frame/_ipywidget_toolbar.py index 552ee9827..72976a445 100644 --- a/fastplotlib/layouts/_frame/_ipywidget_toolbar.py +++ b/fastplotlib/layouts/_frame/_ipywidget_toolbar.py @@ -28,6 +28,7 @@ class IpywidgetToolBar(HBox, ToolBar): """Basic toolbar using ipywidgets""" + def __init__(self, plot): ToolBar.__init__(self, plot) @@ -82,7 +83,7 @@ def __init__(self, plot): disabled=False, icon="draw-polygon", layout=Layout(width="auto"), - tooltip="add PolygonSelector" + tooltip="add PolygonSelector", ) widgets = [ @@ -109,7 +110,9 @@ def __init__(self, plot): widgets.append(image) if hasattr(self.plot, "_subplots"): - positions = list(product(range(self.plot.shape[0]), range(self.plot.shape[1]))) + positions = list( + product(range(self.plot.shape[0]), range(self.plot.shape[1])) + ) values = list() for pos in positions: if self.plot[pos].name is not None: @@ -150,7 +153,9 @@ def _get_subplot_dropdown_value(self) -> str: return self._dropdown.value def auto_scale_handler(self, obj): - self.current_subplot.auto_scale(maintain_aspect=self.current_subplot.camera.maintain_aspect) + self.current_subplot.auto_scale( + maintain_aspect=self.current_subplot.camera.maintain_aspect + ) def center_scene_handler(self, obj): self.current_subplot.center_scene() @@ -230,7 +235,7 @@ def __init__(self, iw): icon="adjust", description="reset", layout=Layout(width="auto"), - tooltip="reset vmin/vmax and reset histogram using current frame" + tooltip="reset vmin/vmax and reset histogram using current frame", ) self.sliders: Dict[str, IntSlider] = dict() @@ -250,7 +255,9 @@ def __init__(self, iw): orientation="horizontal", ) - slider.observe(partial(self.iw._slider_value_changed, dim), names="value") + slider.observe( + partial(self.iw._slider_value_changed, dim), names="value" + ) self.sliders[dim] = slider @@ -287,7 +294,7 @@ def __init__(self, iw): self.reset_vminvmax_hlut_button, self.play_button, self.step_size_setter, - self.speed_text + self.speed_text, ] self.play_button.interval = 10 diff --git a/fastplotlib/layouts/_frame/_jupyter_output.py b/fastplotlib/layouts/_frame/_jupyter_output.py index 25f5e2a2e..786041bcf 100644 --- a/fastplotlib/layouts/_frame/_jupyter_output.py +++ b/fastplotlib/layouts/_frame/_jupyter_output.py @@ -13,13 +13,14 @@ class JupyterOutputContext(VBox): Basically vstacks plot canvas, toolbar, and other widgets. Uses sidecar if desired. """ + def __init__( - self, - frame, - make_toolbar: bool, - use_sidecar: bool, - sidecar_kwargs: dict, - add_widgets: List[Widget], + self, + frame, + make_toolbar: bool, + use_sidecar: bool, + sidecar_kwargs: dict, + add_widgets: List[Widget], ): """ diff --git a/fastplotlib/layouts/_frame/_qt_output.py b/fastplotlib/layouts/_frame/_qt_output.py index b4c7cffd9..e8be2d050 100644 --- a/fastplotlib/layouts/_frame/_qt_output.py +++ b/fastplotlib/layouts/_frame/_qt_output.py @@ -9,11 +9,12 @@ class QOutputContext(QtWidgets.QWidget): Basically vstacks plot canvas, toolbar, and other widgets. """ + def __init__( - self, - frame, - make_toolbar, - add_widgets, + self, + frame, + make_toolbar, + add_widgets, ): """ diff --git a/fastplotlib/layouts/_frame/_qt_toolbar.py b/fastplotlib/layouts/_frame/_qt_toolbar.py index 9d4e0b48f..4ee073701 100644 --- a/fastplotlib/layouts/_frame/_qt_toolbar.py +++ b/fastplotlib/layouts/_frame/_qt_toolbar.py @@ -11,8 +11,11 @@ from ._qtoolbar_template import Ui_QToolbar -class QToolbar(ToolBar, QtWidgets.QWidget): # inheritance order MUST be Toolbar first, QWidget second! Else breaks +class QToolbar( + ToolBar, QtWidgets.QWidget +): # inheritance order MUST be Toolbar first, QWidget second! Else breaks """Toolbar for Qt context""" + def __init__(self, output_context, plot): QtWidgets.QWidget.__init__(self, parent=output_context) ToolBar.__init__(self, plot) @@ -49,7 +52,9 @@ def __init__(self, output_context, plot): self.setMaximumHeight(35) # set the initial values for buttons - self.ui.maintain_aspect_button.setChecked(self.current_subplot.camera.maintain_aspect) + self.ui.maintain_aspect_button.setChecked( + self.current_subplot.camera.maintain_aspect + ) self.ui.panzoom_button.setChecked(self.current_subplot.controller.enabled) if copysign(1, self.current_subplot.camera.local.scale_y) == -1: @@ -70,7 +75,9 @@ def update_current_subplot(self, ev): # set buttons w.r.t. current subplot self.ui.panzoom_button.setChecked(subplot.controller.enabled) - self.ui.maintain_aspect_button.setChecked(subplot.camera.maintain_aspect) + self.ui.maintain_aspect_button.setChecked( + subplot.camera.maintain_aspect + ) if copysign(1, subplot.camera.local.scale_y) == -1: self.ui.y_direction_button.setText("v") @@ -81,7 +88,9 @@ def _get_subplot_dropdown_value(self) -> str: return self.ui.current_subplot.text() def auto_scale_handler(self, *args): - self.current_subplot.auto_scale(maintain_aspect=self.current_subplot.camera.maintain_aspect) + self.current_subplot.auto_scale( + maintain_aspect=self.current_subplot.camera.maintain_aspect + ) def center_scene_handler(self, *args): self.current_subplot.center_scene() @@ -128,6 +137,7 @@ class SliderInterface: This interface makes a QSlider behave somewhat like a ipywidget IntSlider, enough for ImageWidget to function. """ + def __init__(self, qslider): self.qslider = qslider @@ -176,7 +186,9 @@ def __init__(self, image_widget): self.reset_vmin_vmax_hlut_button = QtWidgets.QPushButton(self) self.reset_vmin_vmax_hlut_button.setText("reset histogram-lut") - self.reset_vmin_vmax_hlut_button.clicked.connect(self.image_widget.reset_vmin_vmax_frame) + self.reset_vmin_vmax_hlut_button.clicked.connect( + self.image_widget.reset_vmin_vmax_frame + ) hlayout_buttons.addWidget(self.reset_vmin_vmax_hlut_button) self.vlayout.addLayout(hlayout_buttons) @@ -187,7 +199,9 @@ def __init__(self, image_widget): if self.image_widget.ndim > 2: # create a slider, spinbox and dimension label for each dimension in the ImageWidget for dim in self.image_widget.slider_dims: - hlayout = QtWidgets.QHBoxLayout() # horizontal stack for label, slider, spinbox + hlayout = ( + QtWidgets.QHBoxLayout() + ) # horizontal stack for label, slider, spinbox # max value for current dimension max_val = self.image_widget._dims_max_bounds[dim] - 1 @@ -213,7 +227,9 @@ def __init__(self, image_widget): spinbox.valueChanged.connect(slider.setValue) # connect slider to change the index within the dimension - slider.valueChanged.connect(partial(self.image_widget._slider_value_changed, dim)) + slider.valueChanged.connect( + partial(self.image_widget._slider_value_changed, dim) + ) # slider dimension label slider_label = QtWidgets.QLabel(self) diff --git a/fastplotlib/layouts/_gridplot.py b/fastplotlib/layouts/_gridplot.py index 459aca5fd..98e5643f3 100644 --- a/fastplotlib/layouts/_gridplot.py +++ b/fastplotlib/layouts/_gridplot.py @@ -84,7 +84,9 @@ def __init__( if names is not None: if len(list(chain(*names))) != self.shape[0] * self.shape[1]: - raise ValueError("must provide same number of subplot `names` as specified by gridplot shape") + raise ValueError( + "must provide same number of subplot `names` as specified by gridplot shape" + ) self.names = to_array(names).reshape(self.shape) else: @@ -111,7 +113,9 @@ def __init__( if controller_ids is None: # individual controller for each subplot - controller_ids = np.arange(self.shape[0] * self.shape[1]).reshape(self.shape) + controller_ids = np.arange(self.shape[0] * self.shape[1]).reshape( + self.shape + ) elif isinstance(controller_ids, str): if controller_ids == "sync": @@ -129,7 +133,9 @@ def __init__( # list of str of subplot names, convert this to integer ids if all([isinstance(item, str) for item in ids_flat]): if self.names is None: - raise ValueError("must specify subplot `names` to use list of str for `controller_ids`") + raise ValueError( + "must specify subplot `names` to use list of str for `controller_ids`" + ) # make sure each controller_id str is a subplot name if not all([n in self.names for n in ids_flat]): @@ -148,7 +154,9 @@ def __init__( # set id based on subplot position for each synced sublist for i, sublist in enumerate(controller_ids): for name in sublist: - ids_init[self.names == name] = -(i + 1) # use negative numbers because why not + ids_init[self.names == name] = -( + i + 1 + ) # use negative numbers because why not controller_ids = ids_init @@ -163,11 +171,15 @@ def __init__( ) if controller_ids.shape != self.shape: - raise ValueError("Number of controller_ids does not match the number of subplots") + raise ValueError( + "Number of controller_ids does not match the number of subplots" + ) if controller_types is None: # `create_controller()` will auto-determine controller for each subplot based on defaults - controller_types = np.array(["default"] * self.shape[0] * self.shape[1]).reshape(self.shape) + controller_types = np.array( + ["default"] * self.shape[0] * self.shape[1] + ).reshape(self.shape) # validate controller types types_flat = list(chain(*controller_types)) @@ -180,7 +192,9 @@ def __init__( if controller_type is None: continue - if (controller_type not in valid_str) and (not isinstance(controller_type, valid_instances)): + if (controller_type not in valid_str) and ( + not isinstance(controller_type, valid_instances) + ): raise ValueError( f"You have passed an invalid controller type, valid controller_types arguments are:\n" f"{valid_str} or instances of {[c.__name__ for c in valid_instances]}" diff --git a/fastplotlib/layouts/_plot_area.py b/fastplotlib/layouts/_plot_area.py index d4fc62e1f..08a09baa7 100644 --- a/fastplotlib/layouts/_plot_area.py +++ b/fastplotlib/layouts/_plot_area.py @@ -63,7 +63,7 @@ def __init__( name: str, optional name this ``subplot`` or ``plot`` - + """ self._parent: PlotArea = parent @@ -163,9 +163,13 @@ def camera(self, new_camera: Union[str, pygfx.PerspectiveCamera]): self._camera.fov = 50 else: - raise ValueError("camera must be one of '2d', '3d' or a pygfx.PerspectiveCamera instance") + raise ValueError( + "camera must be one of '2d', '3d' or a pygfx.PerspectiveCamera instance" + ) else: - raise ValueError("camera must be one of '2d', '3d' or a pygfx.PerspectiveCamera instance") + raise ValueError( + "camera must be one of '2d', '3d' or a pygfx.PerspectiveCamera instance" + ) # in the future we can think about how to allow changing the controller @property @@ -188,9 +192,7 @@ def controller(self, new_controller: Union[str, pygfx.Controller]): for camera in cameras_list: new_controller.add_camera(camera) - new_controller.register_events( - self.viewport - ) + new_controller.register_events(self.viewport) # TODO: monkeypatch until we figure out a better # pygfx plans on refactoring viewports anyways @@ -246,7 +248,7 @@ def name(self, name: str): if name is None: self._name = None return - + if not isinstance(name, str): raise TypeError("PlotArea `name` must be of type ") self._name = name @@ -473,9 +475,9 @@ def _add_or_insert_graphic( if isinstance(graphic, BaseSelector): # store in SELECTORS dict loc = graphic.loc - SELECTORS[ - loc - ] = graphic # add hex id string for referencing this graphic instance + SELECTORS[loc] = ( + graphic # add hex id string for referencing this graphic instance + ) # don't manage garbage collection of LineSliders for now if action == "insert": self._selectors.insert(index, loc) @@ -484,9 +486,9 @@ def _add_or_insert_graphic( else: # store in GRAPHICS dict loc = graphic.loc - GRAPHICS[ - loc - ] = graphic # add hex id string for referencing this graphic instance + GRAPHICS[loc] = ( + graphic # add hex id string for referencing this graphic instance + ) if action == "insert": self._graphics.insert(index, loc) @@ -566,10 +568,10 @@ def center_scene(self, *, zoom: float = 1.35): camera.zoom = zoom def auto_scale( - self, - *, # since this is often used as an event handler, don't want to coerce maintain_aspect = True - maintain_aspect: Union[None, bool] = None, - zoom: float = 0.8 + self, + *, # since this is often used as an event handler, don't want to coerce maintain_aspect = True + maintain_aspect: Union[None, bool] = None, + zoom: float = 0.8, ): """ Auto-scale the camera w.r.t to the scene @@ -737,7 +739,7 @@ def __contains__(self, item: Union[str, Graphic]): return False raise TypeError("PlotArea `in` operator accepts only `Graphic` or `str` types") - + def __str__(self): if self.name is None: name = "unnamed" diff --git a/fastplotlib/legends/legend.py b/fastplotlib/legends/legend.py index e29665e0f..291c25ff3 100644 --- a/fastplotlib/legends/legend.py +++ b/fastplotlib/legends/legend.py @@ -13,9 +13,9 @@ class LegendItem: def __init__( - self, - label: str, - color: pygfx.Color, + self, + label: str, + color: pygfx.Color, ): """ @@ -31,11 +31,7 @@ def __init__( class LineLegendItem(LegendItem): def __init__( - self, - parent, - graphic: LineGraphic, - label: str, - position: Tuple[int, int] + self, parent, graphic: LineGraphic, label: str, position: Tuple[int, int] ): """ @@ -55,7 +51,9 @@ def __init__( pass else: - raise ValueError("Must specify `label` or Graphic must have a `name` to auto-use as the label") + raise ValueError( + "Must specify `label` or Graphic must have a `name` to auto-use as the label" + ) # for now only support lines with a single color if np.unique(graphic.colors(), axis=0).shape[0] > 1: @@ -70,17 +68,13 @@ def __init__( graphic.colors.add_event_handler(self._update_color) # construct Line WorldObject - data = np.array( - [[0, 0, 0], - [3, 0, 0]], - dtype=np.float32 - ) + data = np.array([[0, 0, 0], [3, 0, 0]], dtype=np.float32) material = pygfx.LineMaterial self._line_world_object = pygfx.Line( geometry=pygfx.Geometry(positions=data), - material=material(thickness=8, color=self._color) + material=material(thickness=8, color=self._color), ) # self._line_world_object.world.x = position[0] @@ -96,7 +90,7 @@ def __init__( color="w", outline_color="w", outline_thickness=0, - ) + ), ) self.world_object = pygfx.Group() @@ -109,7 +103,9 @@ def __init__( self.world_object.world.y = position[1] self.world_object.world.z = 2 - self.world_object.add_event_handler(partial(self._highlight_graphic, graphic), "click") + self.world_object.add_event_handler( + partial(self._highlight_graphic, graphic), "click" + ) @property def label(self) -> str: @@ -123,7 +119,9 @@ def label(self, text: str): def _update_color(self, ev: FeatureEvent): new_color = ev.pick_info["new_data"] if np.unique(new_color, axis=0).shape[0] > 1: - raise ValueError("LegendError: LineGraphic colors no longer appropriate for legend") + raise ValueError( + "LegendError: LineGraphic colors no longer appropriate for legend" + ) self._color = new_color[0] self._line_world_object.material.color = pygfx.Color(self._color) @@ -142,12 +140,12 @@ def _highlight_graphic(self, graphic, ev): class Legend(Graphic): def __init__( - self, - plot_area, - highlight_color: Union[str, tuple, np.ndarray] = "w", - max_rows: int = 5, - *args, - **kwargs + self, + plot_area, + highlight_color: Union[str, tuple, np.ndarray] = "w", + max_rows: int = 5, + *args, + **kwargs, ): """ @@ -166,7 +164,7 @@ def __init__( self._graphics: List[Graphic] = list() # hex id of Graphic, i.e. graphic.loc are the keys - self._items: OrderedDict[str: LegendItem] = OrderedDict() + self._items: OrderedDict[str:LegendItem] = OrderedDict() super().__init__(*args, **kwargs) @@ -176,7 +174,9 @@ def __init__( self._mesh = pygfx.Mesh( pygfx.box_geometry(50, 10, 1), - pygfx.MeshBasicMaterial(color=pygfx.Color([0.1, 0.1, 0.1, 1]), wireframe_thickness=10) + pygfx.MeshBasicMaterial( + color=pygfx.Color([0.1, 0.1, 0.1, 1]), wireframe_thickness=10 + ), ) self.world_object.add(self._mesh) @@ -235,7 +235,9 @@ def add_graphic(self, graphic: Graphic, label: str = None): # get x position offset for this new column of LegendItems # start by getting the LegendItems in the previous column - prev_column_items: List[LegendItem] = list(self._items.values())[-self._max_rows:] + prev_column_items: List[LegendItem] = list(self._items.values())[ + -self._max_rows : + ] # x position of LegendItems in previous column x_pos = prev_column_items[-1].world_object.world.x max_width = 0 @@ -267,7 +269,7 @@ def add_graphic(self, graphic: Graphic, label: str = None): self._graphics.append(graphic) self._items[graphic.loc] = legend_item - + graphic.deleted.add_event_handler(partial(self.remove_graphic, graphic)) self._col_counter = new_col_ix diff --git a/fastplotlib/utils/__init__.py b/fastplotlib/utils/__init__.py index addea140d..305af90a8 100644 --- a/fastplotlib/utils/__init__.py +++ b/fastplotlib/utils/__init__.py @@ -10,6 +10,4 @@ class _Config: party_parrot: bool -config = _Config( - party_parrot=False -) +config = _Config(party_parrot=False) diff --git a/fastplotlib/utils/_gpu_info.py b/fastplotlib/utils/_gpu_info.py index 9387f3ece..93e95d281 100644 --- a/fastplotlib/utils/_gpu_info.py +++ b/fastplotlib/utils/_gpu_info.py @@ -11,6 +11,7 @@ NOTEBOOK = False else: from IPython.display import display + if ip.has_trait("kernel") and (JupyterWgpuCanvas is not False): NOTEBOOK = True else: @@ -21,17 +22,14 @@ def _notebook_print_banner(): if NOTEBOOK is False: return - logo_path = Path(__file__).parent.parent.joinpath("assets", "fastplotlib_face_logo.png") + logo_path = Path(__file__).parent.parent.joinpath( + "assets", "fastplotlib_face_logo.png" + ) with open(logo_path, "rb") as f: logo_data = f.read() - image = Image( - value=logo_data, - format="png", - width=300, - height=55 - ) + image = Image(value=logo_data, format="png", width=300, height=55) display(image) @@ -39,7 +37,9 @@ def _notebook_print_banner(): adapters = [a for a in enumerate_adapters()] adapters_info = [a.request_adapter_info() for a in adapters] - ix_default = adapters_info.index(get_default_device().adapter.request_adapter_info()) + ix_default = adapters_info.index( + get_default_device().adapter.request_adapter_info() + ) if len(adapters) > 0: print("Available devices:") diff --git a/fastplotlib/utils/generate_add_methods.py b/fastplotlib/utils/generate_add_methods.py index 3fe16260c..9cb87baab 100644 --- a/fastplotlib/utils/generate_add_methods.py +++ b/fastplotlib/utils/generate_add_methods.py @@ -5,11 +5,8 @@ # so that fastplotlib will import # hacky but it works current_module = pathlib.Path(__file__).parent.parent.resolve() -with open(current_module.joinpath('layouts/graphic_methods_mixin.py'), 'w') as f: - f.write( - f"class GraphicMethodsMixin:\n" - f" pass" - ) +with open(current_module.joinpath("layouts/graphic_methods_mixin.py"), "w") as f: + f.write(f"class GraphicMethodsMixin:\n" f" pass") from fastplotlib import graphics @@ -24,21 +21,23 @@ def generate_add_graphics_methods(): # clear file and regenerate from scratch - f = open(current_module.joinpath('layouts/graphic_methods_mixin.py'), 'w') + f = open(current_module.joinpath("layouts/graphic_methods_mixin.py"), "w") - f.write('# This is an auto-generated file and should not be modified directly\n\n') + f.write("# This is an auto-generated file and should not be modified directly\n\n") - f.write('from typing import *\n\n') - f.write('import numpy\n') - f.write('import weakref\n\n') - f.write('from ..graphics import *\n') - f.write('from ..graphics._base import Graphic\n\n') + f.write("from typing import *\n\n") + f.write("import numpy\n") + f.write("import weakref\n\n") + f.write("from ..graphics import *\n") + f.write("from ..graphics._base import Graphic\n\n") f.write("\nclass GraphicMethodsMixin:\n") f.write(" def __init__(self):\n") f.write(" pass\n\n") - f.write(" def _create_graphic(self, graphic_class, *args, **kwargs) -> Graphic:\n") + f.write( + " def _create_graphic(self, graphic_class, *args, **kwargs) -> Graphic:\n" + ) f.write(" if 'center' in kwargs.keys():\n") f.write(" center = kwargs.pop('center')\n") f.write(" else:\n") @@ -55,19 +54,23 @@ def generate_add_graphics_methods(): method_name = class_name.type class_args = inspect.getfullargspec(class_name)[0][1:] - class_args = [arg + ', ' for arg in class_args] + class_args = [arg + ", " for arg in class_args] s = "" for a in class_args: s += a - f.write(f" def add_{method_name}{inspect.signature(class_name.__init__)} -> {class_name.__name__}:\n") + f.write( + f" def add_{method_name}{inspect.signature(class_name.__init__)} -> {class_name.__name__}:\n" + ) f.write(' """\n') - f.write(f' {class_name.__init__.__doc__}\n') + f.write(f" {class_name.__init__.__doc__}\n") f.write(' """\n') - f.write(f" return self._create_graphic({class_name.__name__}, {s}*args, **kwargs)\n\n") + f.write( + f" return self._create_graphic({class_name.__name__}, {s}*args, **kwargs)\n\n" + ) f.close() -if __name__ == '__main__': +if __name__ == "__main__": generate_add_graphics_methods() diff --git a/fastplotlib/utils/mesh_masks.py b/fastplotlib/utils/mesh_masks.py index 600e5ab6d..c44588b6c 100644 --- a/fastplotlib/utils/mesh_masks.py +++ b/fastplotlib/utils/mesh_masks.py @@ -125,4 +125,4 @@ x_right = (x_right, 0) x_left = (x_left, 0) y_top = (y_top, 1) -y_bottom = (y_bottom, 1) \ No newline at end of file +y_bottom = (y_bottom, 1) diff --git a/fastplotlib/widgets/histogram_lut.py b/fastplotlib/widgets/histogram_lut.py index 64feb8df6..31f6ab8e9 100644 --- a/fastplotlib/widgets/histogram_lut.py +++ b/fastplotlib/widgets/histogram_lut.py @@ -13,12 +13,12 @@ # TODO: This is a widget, we can think about a BaseWidget class later if necessary class HistogramLUT(Graphic): def __init__( - self, - data: np.ndarray, - image_graphic: ImageGraphic, - nbins: int = 100, - flank_divisor: float = 5.0, - **kwargs + self, + data: np.ndarray, + image_graphic: ImageGraphic, + nbins: int = 100, + flank_divisor: float = 5.0, + **kwargs, ): """ @@ -58,11 +58,14 @@ def __init__( size=size, origin=origin, axis="y", - edge_thickness=8 + edge_thickness=8, ) # there will be a small difference with the histogram edges so this makes them both line up exactly - self.linear_region.selection = (image_graphic.cmap.vmin, image_graphic.cmap.vmax) + self.linear_region.selection = ( + image_graphic.cmap.vmin, + image_graphic.cmap.vmax, + ) self._vmin = self.image_graphic.cmap.vmin self._vmax = self.image_graphic.cmap.vmax @@ -105,9 +108,7 @@ def __init__( self._text_vmax.position_x = -120 self._text_vmax.position_y = self.linear_region.selection()[1] - self.linear_region.selection.add_event_handler( - self._linear_region_handler - ) + self.linear_region.selection.add_event_handler(self._linear_region_handler) self.image_graphic.cmap.add_event_handler(self._image_cmap_handler) @@ -168,12 +169,16 @@ def _calculate_histogram(self, data): flank_size = flank_nbins * bin_width flank_left = np.arange(edges[0] - flank_size, edges[0], bin_width) - flank_right = np.arange(edges[-1] + bin_width, edges[-1] + flank_size, bin_width) + flank_right = np.arange( + edges[-1] + bin_width, edges[-1] + flank_size, bin_width + ) edges_flanked = np.concatenate((flank_left, edges, flank_right)) np.unique(np.diff(edges_flanked)) - hist_flanked = np.concatenate((np.zeros(flank_nbins), hist, np.zeros(flank_nbins))) + hist_flanked = np.concatenate( + (np.zeros(flank_nbins), hist, np.zeros(flank_nbins)) + ) # scale 0-100 to make it easier to see # float32 data can produce unnecessarily high values @@ -181,7 +186,7 @@ def _calculate_histogram(self, data): if edges_flanked.size > hist_scaled.size: # we don't care about accuracy here so if it's off by 1-2 bins that's fine - edges_flanked = edges_flanked[:hist_scaled.size] + edges_flanked = edges_flanked[: hist_scaled.size] return hist, edges, hist_scaled, edges_flanked @@ -209,7 +214,10 @@ def vmin(self, value: float): # must use world coordinate values directly from selection() # otherwise the linear region bounds jump to the closest bin edges - self.linear_region.selection = (value * self._scale_factor, self.linear_region.selection()[1]) + self.linear_region.selection = ( + value * self._scale_factor, + self.linear_region.selection()[1], + ) self.image_graphic.cmap.vmin = value self._block_events(False) @@ -230,7 +238,10 @@ def vmax(self, value: float): # must use world coordinate values directly from selection() # otherwise the linear region bounds jump to the closest bin edges - self.linear_region.selection = (self.linear_region.selection()[0], value * self._scale_factor) + self.linear_region.selection = ( + self.linear_region.selection()[0], + value * self._scale_factor, + ) self.image_graphic.cmap.vmax = value self._block_events(False) @@ -280,9 +291,7 @@ def image_graphic(self, graphic): ) # cleanup events from current image graphic - self._image_graphic.cmap.remove_event_handler( - self._image_cmap_handler - ) + self._image_graphic.cmap.remove_event_handler(self._image_cmap_handler) self._image_graphic = graphic diff --git a/fastplotlib/widgets/image.py b/fastplotlib/widgets/image.py index a3f6335c9..0da1bb520 100644 --- a/fastplotlib/widgets/image.py +++ b/fastplotlib/widgets/image.py @@ -40,6 +40,7 @@ def _is_arraylike(obj) -> bool: class _WindowFunctions: """Stores window function and window size""" + def __init__(self, image_widget, func: callable, window_size: int): self._image_widget = image_widget self._func = None @@ -131,7 +132,9 @@ def cmap(self) -> List[str]: def cmap(self, names: Union[str, List[str]]): if isinstance(names, list): if not all([isinstance(n, str) for n in names]): - raise TypeError(f"Must pass cmap name as a `str` of list of `str`, you have passed:\n{names}") + raise TypeError( + f"Must pass cmap name as a `str` of list of `str`, you have passed:\n{names}" + ) if not len(names) == len(self.managed_graphics): raise IndexError( @@ -558,7 +561,9 @@ def __init__( # user specified kwargs will overwrite the defaults grid_plot_kwargs_default.update(grid_plot_kwargs) - self._gridplot: GridPlot = GridPlot(shape=grid_shape, **grid_plot_kwargs_default) + self._gridplot: GridPlot = GridPlot( + shape=grid_shape, **grid_plot_kwargs_default + ) for data_ix, (d, subplot) in enumerate(zip(self.data, self.gridplot)): if self._names is not None: @@ -574,11 +579,7 @@ def __init__( subplot.set_title(name) if histogram_widget: - hlut = HistogramLUT( - data=d, - image_graphic=ig, - name="histogram_lut" - ) + hlut = HistogramLUT(data=d, image_graphic=ig, name="histogram_lut") subplot.docks["right"].add_graphic(hlut) subplot.docks["right"].size = 80 @@ -596,7 +597,7 @@ def frame_apply(self) -> Union[dict, None]: def frame_apply(self, frame_apply: Dict[int, callable]): if frame_apply is None: frame_apply = dict() - + self._frame_apply = frame_apply # force update image graphic self.current_index = self.current_index @@ -901,7 +902,9 @@ def set_data( max_lengths["z"] = min(max_lengths["z"], new_array.shape[1] - 1) # set histogram widget - subplot.docks["right"]["histogram_lut"].set_data(new_array, reset_vmin_vmax=reset_vmin_vmax) + subplot.docks["right"]["histogram_lut"].set_data( + new_array, reset_vmin_vmax=reset_vmin_vmax + ) # set slider maxes # TODO: maybe make this stuff a property, like ndims, n_frames etc. and have it set the sliders @@ -912,7 +915,9 @@ def set_data( # force graphics to update self.current_index = self.current_index - def show(self, toolbar: bool = True, sidecar: bool = False, sidecar_kwargs: dict = None): + def show( + self, toolbar: bool = True, sidecar: bool = False, sidecar_kwargs: dict = None + ): """ Show the widget. @@ -931,7 +936,7 @@ def show(self, toolbar: bool = True, sidecar: bool = False, sidecar_kwargs: dict toolbar=toolbar, sidecar=sidecar, sidecar_kwargs=sidecar_kwargs, - add_widgets=[self._image_widget_toolbar] + add_widgets=[self._image_widget_toolbar], ) return self._output From c9ce4958eea542209fa5e7a541a8a50fd54d4928 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Mon, 4 Mar 2024 22:36:26 -0500 Subject: [PATCH 039/185] add zmq example (#422) --- .../notebooks/multiprocessing_zmq/README.md | 3 + .../multiprocessing_zmq_compute.ipynb | 73 +++++++++++ .../multiprocessing_zmq_plot.ipynb | 114 ++++++++++++++++++ 3 files changed, 190 insertions(+) create mode 100644 examples/notebooks/multiprocessing_zmq/README.md create mode 100644 examples/notebooks/multiprocessing_zmq/multiprocessing_zmq_compute.ipynb create mode 100644 examples/notebooks/multiprocessing_zmq/multiprocessing_zmq_plot.ipynb diff --git a/examples/notebooks/multiprocessing_zmq/README.md b/examples/notebooks/multiprocessing_zmq/README.md new file mode 100644 index 000000000..184453d0c --- /dev/null +++ b/examples/notebooks/multiprocessing_zmq/README.md @@ -0,0 +1,3 @@ +This example shows how to use a zmq publisher-subscriber pattern to perform a computation in one process and visualize results in another process. First, run all cells in `multiprocessing_zmq_plot.ipynb`, and then run cells in `multiprocessing_zmq_compute.ipynb`. The raw bytes for the numpy array are sent using zmq in the compute notebook and received in the plot notebook and displayed. + +For more information on zmq see: https://zeromq.org/languages/python/ diff --git a/examples/notebooks/multiprocessing_zmq/multiprocessing_zmq_compute.ipynb b/examples/notebooks/multiprocessing_zmq/multiprocessing_zmq_compute.ipynb new file mode 100644 index 000000000..7f24f6411 --- /dev/null +++ b/examples/notebooks/multiprocessing_zmq/multiprocessing_zmq_compute.ipynb @@ -0,0 +1,73 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "ca2817a3-869c-4cc6-901b-c34509518175", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import zmq" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dd0b9780-b507-4ea2-af09-134abd76f45b", + "metadata": {}, + "outputs": [], + "source": [ + "context = zmq.Context()\n", + "\n", + "# create publisher\n", + "socket = context.socket(zmq.PUB)\n", + "socket.bind(\"tcp://127.0.0.1:5555\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4729bfe8-3474-4bc9-a489-a57d02e5a287", + "metadata": {}, + "outputs": [], + "source": [ + "for i in range(2_000):\n", + " # make some data, make note of the dtype\n", + " data = np.random.rand(512, 512).astype(np.float32)\n", + "\n", + " # sent bytes over the socket\n", + " socket.send(data.tobytes())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d47dab72-1061-439f-bf6e-a88b9ee8e5aa", + "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.11.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/notebooks/multiprocessing_zmq/multiprocessing_zmq_plot.ipynb b/examples/notebooks/multiprocessing_zmq/multiprocessing_zmq_plot.ipynb new file mode 100644 index 000000000..2c6e93d8f --- /dev/null +++ b/examples/notebooks/multiprocessing_zmq/multiprocessing_zmq_plot.ipynb @@ -0,0 +1,114 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "491e6050-64ae-4bfc-a480-5805cd684710", + "metadata": {}, + "outputs": [], + "source": [ + "import fastplotlib as fpl\n", + "import numpy as np\n", + "import zmq" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "97135f98-6810-49b6-a8de-d0e114720d6c", + "metadata": {}, + "outputs": [], + "source": [ + "context = zmq.Context()\n", + "\n", + "# create subscriber\n", + "sub = context.socket(zmq.SUB)\n", + "sub.setsockopt(zmq.SUBSCRIBE, b\"\")\n", + "\n", + "# keep only the most recent message\n", + "sub.setsockopt(zmq.CONFLATE, 1)\n", + "\n", + "# publisher address and port\n", + "sub.connect(\"tcp://127.0.0.1:5555\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2d4420f2-364a-445a-9658-63e9ffa586c3", + "metadata": {}, + "outputs": [], + "source": [ + "def get_bytes():\n", + " \"\"\"\n", + " Gets the bytes from the publisher\n", + " \"\"\"\n", + " try:\n", + " b = sub.recv(zmq.NOBLOCK)\n", + " except zmq.Again:\n", + " pass\n", + " else:\n", + " return b\n", + " \n", + " return None" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "42d20d77-b884-4379-80e4-e08738506eeb", + "metadata": {}, + "outputs": [], + "source": [ + "plot = fpl.Plot()\n", + "\n", + "# initialize some data, must be of same dtype and shape as data sent by publisher\n", + "data = np.random.rand(512, 512).astype(np.float32)\n", + "plot.add_image(data, name=\"image\")\n", + "\n", + "def update_frame(p):\n", + " # recieve bytes\n", + " b = get_bytes()\n", + " \n", + " if b is not None:\n", + " # numpy array from bytes, MUST specify dtype and make sure it matches what you sent\n", + " a = np.frombuffer(b, dtype=np.float32).reshape(512, 512)\n", + " \n", + " # set graphic data\n", + " p[\"image\"].data = a\n", + "\n", + "plot.add_animations(update_frame)\n", + "plot.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0f8ac188-9359-4d3c-b8f1-384be84d1585", + "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.11.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From 5978e7d9f2727eb953a243780af92592dd6ad416 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Wed, 6 Mar 2024 05:30:28 -0500 Subject: [PATCH 040/185] fix fastplotlib.__init__ (#424) --- fastplotlib/__init__.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/fastplotlib/__init__.py b/fastplotlib/__init__.py index f0f3cc7b1..98dc17e26 100644 --- a/fastplotlib/__init__.py +++ b/fastplotlib/__init__.py @@ -4,6 +4,7 @@ from .graphics import * from .graphics.selectors import * from .legends import * +from .utils import _notebook_print_banner, config from wgpu.gui.auto import run @@ -16,13 +17,15 @@ from wgpu.backends.wgpu_native import enumerate_adapters +with open(Path(__file__).parent.joinpath("VERSION"), "r") as f: + __version__ = f.read().split("\n")[0] + adapters = [a.request_adapter_info() for a in enumerate_adapters()] if len(adapters) < 1: raise IndexError("No WGPU adapters found, fastplotlib will not work.") -with open(Path(__file__).parent.joinpath("VERSION"), "r") as f: - __version__ = f.read().split("\n")[0] +_notebook_print_banner() __all__ = [ "Plot", From 463cd15d7e3de4b7ff51836c64b4cd90b0bed30a Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Fri, 8 Mar 2024 17:01:49 +0100 Subject: [PATCH 041/185] Fix typo (#423) --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 84114c61f..ad484db5d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,7 +15,7 @@ git clone https://github.com/YOUR_ACCOUNT/fastplotlib.git cd fastplotlib # install all extras in place -pip install -e ".[notebook,docs,tests] +pip install -e ".[notebook,docs,tests]" ``` > If you cloned before installing `git-lfs`, you can run `git lfs pull` at any From 97dd7581598e08d2320c1a208737164be2cdbdb7 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Fri, 8 Mar 2024 17:56:50 +0100 Subject: [PATCH 042/185] Just use asarray (#426) --- fastplotlib/layouts/_gridplot.py | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/fastplotlib/layouts/_gridplot.py b/fastplotlib/layouts/_gridplot.py index 98e5643f3..04046cd01 100644 --- a/fastplotlib/layouts/_gridplot.py +++ b/fastplotlib/layouts/_gridplot.py @@ -15,16 +15,6 @@ from ._record_mixin import RecordMixin -def to_array(a) -> np.ndarray: - if isinstance(a, np.ndarray): - return a - - if not isinstance(a, list): - raise TypeError("must pass list or numpy array") - - return np.array(a) - - class GridPlot(Frame, RecordMixin): def __init__( self, @@ -88,7 +78,7 @@ def __init__( "must provide same number of subplot `names` as specified by gridplot shape" ) - self.names = to_array(names).reshape(self.shape) + self.names = np.asarray(names).reshape(self.shape) else: self.names = None @@ -101,7 +91,7 @@ def __init__( ) # list -> array if necessary - cameras = to_array(cameras).reshape(self.shape) + cameras = np.asarray(cameras).reshape(self.shape) if cameras.shape != self.shape: raise ValueError("Number of cameras does not match the number of subplots") @@ -162,7 +152,7 @@ def __init__( # integer ids elif all([isinstance(item, (int, np.integer)) for item in ids_flat]): - controller_ids = to_array(controller_ids).reshape(self.shape) + controller_ids = np.asarray(controller_ids).reshape(self.shape) else: raise TypeError( @@ -200,7 +190,7 @@ def __init__( f"{valid_str} or instances of {[c.__name__ for c in valid_instances]}" ) - controller_types = to_array(controller_types).reshape(self.shape) + controller_types = np.asarray(controller_types).reshape(self.shape) # make the real controllers for each subplot self._controllers = np.empty(shape=self.shape, dtype=object) From d45c72301200923b4c0e6f20793ed7d7c3c11917 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Tue, 12 Mar 2024 15:02:00 +0100 Subject: [PATCH 043/185] Image widget glfw example (#429) * Add destkop image widget example * black (to examples/desktop/image only) * Add not * Always import ImageWidget --- examples/desktop/image/image_cmap.py | 1 + examples/desktop/image/image_rgb.py | 1 + examples/desktop/image/image_rgbvminvmax.py | 1 + examples/desktop/image/image_vminvmax.py | 1 + examples/desktop/image/image_widget.py | 20 ++++++++++++++++++++ fastplotlib/__init__.py | 10 ++-------- 6 files changed, 26 insertions(+), 8 deletions(-) create mode 100644 examples/desktop/image/image_widget.py diff --git a/examples/desktop/image/image_cmap.py b/examples/desktop/image/image_cmap.py index 9a9f0d497..b7f7b39af 100644 --- a/examples/desktop/image/image_cmap.py +++ b/examples/desktop/image/image_cmap.py @@ -3,6 +3,7 @@ ============ Example showing simple plot creation and subsequent cmap change with Standard image from imageio. """ + # test_example = true import fastplotlib as fpl diff --git a/examples/desktop/image/image_rgb.py b/examples/desktop/image/image_rgb.py index f73077acf..2642962fd 100644 --- a/examples/desktop/image/image_rgb.py +++ b/examples/desktop/image/image_rgb.py @@ -3,6 +3,7 @@ ============ Example showing the simple plot creation with 512 x 512 2D RGB image. """ + # test_example = true import fastplotlib as fpl diff --git a/examples/desktop/image/image_rgbvminvmax.py b/examples/desktop/image/image_rgbvminvmax.py index 4891c5614..e5c4af531 100644 --- a/examples/desktop/image/image_rgbvminvmax.py +++ b/examples/desktop/image/image_rgbvminvmax.py @@ -3,6 +3,7 @@ ============ Example showing the simple plot followed by changing the vmin/vmax with 512 x 512 2D RGB image. """ + # test_example = true import fastplotlib as fpl diff --git a/examples/desktop/image/image_vminvmax.py b/examples/desktop/image/image_vminvmax.py index ae5d102fa..e764f6775 100644 --- a/examples/desktop/image/image_vminvmax.py +++ b/examples/desktop/image/image_vminvmax.py @@ -3,6 +3,7 @@ ============ Example showing the simple plot creation followed by changing the vmin/vmax with Standard imageio image. """ + # test_example = true import fastplotlib as fpl diff --git a/examples/desktop/image/image_widget.py b/examples/desktop/image/image_widget.py new file mode 100644 index 000000000..c50d914d3 --- /dev/null +++ b/examples/desktop/image/image_widget.py @@ -0,0 +1,20 @@ +""" +Image widget +============ +Example showing the image widget in action. +When run in a notebook, or with the Qt GUI backend, sliders are also shown. +""" + +import numpy as np +import fastplotlib as fpl +import imageio.v3 as iio # not a fastplotlib dependency, only used for examples + + +a = iio.imread("imageio:camera.png") +iw = fpl.widgets.ImageWidget(data=a, cmap="viridis") +iw.show() + + +if __name__ == "__main__": + print(__doc__) + fpl.run() diff --git a/fastplotlib/__init__.py b/fastplotlib/__init__.py index 98dc17e26..27545f0ad 100644 --- a/fastplotlib/__init__.py +++ b/fastplotlib/__init__.py @@ -4,19 +4,13 @@ from .graphics import * from .graphics.selectors import * from .legends import * +from .widgets import ImageWidget from .utils import _notebook_print_banner, config from wgpu.gui.auto import run - -try: - import ipywidgets -except (ModuleNotFoundError, ImportError): - pass -else: - from .widgets import ImageWidget - from wgpu.backends.wgpu_native import enumerate_adapters + with open(Path(__file__).parent.joinpath("VERSION"), "r") as f: __version__ = f.read().split("\n")[0] From 775556ed4426dc4d861e9db862ce9a6d86c1f391 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Tue, 12 Mar 2024 15:08:14 +0100 Subject: [PATCH 044/185] Remove duplicate code (mesh_masks) (#430) * Remove duplicate code * Ah, even better --- .../graphics/_features/_selection_features.py | 131 +----------------- fastplotlib/graphics/_features/_sizes.py | 12 +- .../graphics/selectors/_mesh_positions.py | 1 - .../graphics/selectors/_rectangle_region.py | 10 +- 4 files changed, 18 insertions(+), 136 deletions(-) delete mode 100644 fastplotlib/graphics/selectors/_mesh_positions.py diff --git a/fastplotlib/graphics/_features/_selection_features.py b/fastplotlib/graphics/_features/_selection_features.py index 9a2696f7c..294bb15d6 100644 --- a/fastplotlib/graphics/_features/_selection_features.py +++ b/fastplotlib/graphics/_features/_selection_features.py @@ -2,131 +2,10 @@ import numpy as np +from ...utils import mesh_masks from ._base import GraphicFeature, FeatureEvent -""" -positions for indexing the BoxGeometry to set the "width" and "size" of the box -hacky, but I don't think we can morph meshes in pygfx yet: https://github.com/pygfx/pygfx/issues/346 -""" - -x_right = np.array( - [ - True, - True, - True, - True, - False, - False, - False, - False, - False, - True, - False, - True, - True, - False, - True, - False, - False, - True, - False, - True, - True, - False, - True, - False, - ] -) - -x_left = np.array( - [ - False, - False, - False, - False, - True, - True, - True, - True, - True, - False, - True, - False, - False, - True, - False, - True, - True, - False, - True, - False, - False, - True, - False, - True, - ] -) - -y_top = np.array( - [ - False, - True, - False, - True, - False, - True, - False, - True, - True, - True, - True, - True, - False, - False, - False, - False, - False, - False, - True, - True, - False, - False, - True, - True, - ] -) - -y_bottom = np.array( - [ - True, - False, - True, - False, - True, - False, - True, - False, - False, - False, - False, - False, - True, - True, - True, - True, - True, - True, - False, - False, - True, - True, - False, - False, - ] -) - - class LinearSelectionFeature(GraphicFeature): # A bit much to have a class for this but this allows it to integrate with the fastplotlib callback system """ @@ -253,10 +132,10 @@ def _set(self, value: Tuple[float, float]): if self.axis == "x": # change left x position of the fill mesh - self._parent.fill.geometry.positions.data[x_left, 0] = value[0] + self._parent.fill.geometry.positions.data[mesh_masks.x_left] = value[0] # change right x position of the fill mesh - self._parent.fill.geometry.positions.data[x_right, 0] = value[1] + self._parent.fill.geometry.positions.data[mesh_masks.x_right] = value[1] # change x position of the left edge line self._parent.edges[0].geometry.positions.data[:, 0] = value[0] @@ -266,10 +145,10 @@ def _set(self, value: Tuple[float, float]): elif self.axis == "y": # change bottom y position of the fill mesh - self._parent.fill.geometry.positions.data[y_bottom, 1] = value[0] + self._parent.fill.geometry.positions.data[mesh_masks.y_bottom] = value[0] # change top position of the fill mesh - self._parent.fill.geometry.positions.data[y_top, 1] = value[1] + self._parent.fill.geometry.positions.data[mesh_masks.y_top] = value[1] # change y position of the bottom edge line self._parent.edges[0].geometry.positions.data[:, 1] = value[0] diff --git a/fastplotlib/graphics/_features/_sizes.py b/fastplotlib/graphics/_features/_sizes.py index 8fceb8df3..403760508 100644 --- a/fastplotlib/graphics/_features/_sizes.py +++ b/fastplotlib/graphics/_features/_sizes.py @@ -61,8 +61,10 @@ def _fix_sizes(self, sizes, parent): if graphic_type == "ScatterGraphic": sizes = np.array(sizes) else: - raise ValueError(f"Sizes must be an array of shape (n,) where n == the number of data points provided.\ - Received shape={sizes.shape}.") + raise ValueError( + f"Sizes must be an array of shape (n,) where n == the number of data points provided.\ + Received shape={sizes.shape}." + ) return np.array(sizes) @@ -78,8 +80,10 @@ def __setitem__(self, key, value): # numpy will throw errors if it can't broadcast if value.size != self.buffer.data[key].size: - raise ValueError(f"{value.size} is not equal to buffer size {self.buffer.data[key].size}.\ - If you want to set size to a non-scalar value, make sure it's the right length!") + raise ValueError( + f"{value.size} is not equal to buffer size {self.buffer.data[key].size}.\ + If you want to set size to a non-scalar value, make sure it's the right length!" + ) self.buffer.data[key] = value self._update_range(key) diff --git a/fastplotlib/graphics/selectors/_mesh_positions.py b/fastplotlib/graphics/selectors/_mesh_positions.py deleted file mode 100644 index a22b22b17..000000000 --- a/fastplotlib/graphics/selectors/_mesh_positions.py +++ /dev/null @@ -1 +0,0 @@ -import numpy as np diff --git a/fastplotlib/graphics/selectors/_rectangle_region.py b/fastplotlib/graphics/selectors/_rectangle_region.py index a5a9a31cb..0d7dd3661 100644 --- a/fastplotlib/graphics/selectors/_rectangle_region.py +++ b/fastplotlib/graphics/selectors/_rectangle_region.py @@ -3,10 +3,10 @@ import pygfx +from ...utils import mesh_masks from .._base import Graphic from .._features import GraphicFeature from ._base_selector import BaseSelector -from ._mesh_positions import x_right, x_left, y_top, y_bottom class RectangleBoundsFeature(GraphicFeature): @@ -58,16 +58,16 @@ def _set(self, value: Tuple[float, float, float, float]): # change fill mesh # change left x position of the fill mesh - self._parent.fill.geometry.positions.data[x_left, 0] = xmin + self._parent.fill.geometry.positions.data[mesh_masks.x_left] = xmin # change right x position of the fill mesh - self._parent.fill.geometry.positions.data[x_right, 0] = xmax + self._parent.fill.geometry.positions.data[mesh_masks.x_right] = xmax # change bottom y position of the fill mesh - self._parent.fill.geometry.positions.data[y_bottom, 1] = ymin + self._parent.fill.geometry.positions.data[mesh_masks.y_bottom] = ymin # change top position of the fill mesh - self._parent.fill.geometry.positions.data[y_top, 1] = ymax + self._parent.fill.geometry.positions.data[mesh_masks.y_top] = ymax # change the edge lines From fe7417980a83145466ebf7398d75ca71eb28bdcc Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Thu, 14 Mar 2024 09:36:22 +0100 Subject: [PATCH 045/185] Lazy import av (#432) --- fastplotlib/layouts/_frame/_toolbar.py | 2 +- fastplotlib/layouts/_record_mixin.py | 25 +++++++++++++------------ 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/fastplotlib/layouts/_frame/_toolbar.py b/fastplotlib/layouts/_frame/_toolbar.py index 94410b8ea..6a0485655 100644 --- a/fastplotlib/layouts/_frame/_toolbar.py +++ b/fastplotlib/layouts/_frame/_toolbar.py @@ -1,4 +1,4 @@ -from fastplotlib.layouts._subplot import Subplot +from .._subplot import Subplot class ToolBar: diff --git a/fastplotlib/layouts/_record_mixin.py b/fastplotlib/layouts/_record_mixin.py index e3a491915..e3bfdeba5 100644 --- a/fastplotlib/layouts/_record_mixin.py +++ b/fastplotlib/layouts/_record_mixin.py @@ -3,12 +3,17 @@ from multiprocessing import Queue, Process from time import time -try: - import av -except ImportError: - HAS_AV = False -else: - HAS_AV = True + +def _get_av(): + try: + import av + except ImportError: + raise ModuleNotFoundError( + "Recording to video file requires `av`:\n" + "https://github.com/PyAV-Org/PyAV" + ) from None + else: + return av class VideoWriterAV(Process): @@ -28,6 +33,7 @@ def __init__( super().__init__() self.queue = queue + av = _get_av() self.container = av.open(path, mode="w") self.stream = self.container.add_stream(codec, rate=fps, options=options) @@ -45,6 +51,7 @@ def __init__( self.stream.pix_fmt = pixel_format def run(self): + av = _get_av() while True: if self.queue.empty(): # no frame to write continue @@ -177,12 +184,6 @@ def record_start( """ - if not HAS_AV: - raise ModuleNotFoundError( - "Recording to video file requires `av`:\n" - "https://github.com/PyAV-Org/PyAV" - ) - if Path(path).exists(): raise FileExistsError(f"File already exists at given path: {path}") From 8ebbe5cdce7fe0fdab6c0cacf492620ae0d7219a Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Thu, 14 Mar 2024 21:03:28 +0100 Subject: [PATCH 046/185] A bit of black (#433) * Script that generates methods also blackens the code. * Also blacken docs subdir --- docs/source/conf.py | 20 +- docs/source/generate_api.py | 56 ++--- fastplotlib/layouts/graphic_methods_mixin.py | 239 ++++++++++++++++--- fastplotlib/utils/generate_add_methods.py | 23 +- setup.py | 37 ++- 5 files changed, 262 insertions(+), 113 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 7b33a309e..a6a9fd1f6 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -7,9 +7,9 @@ # -- Project information ----------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information -project = 'fastplotlib' -copyright = '2023, Kushal Kolar, Caitlin Lewis' -author = 'Kushal Kolar, Caitlin Lewis' +project = "fastplotlib" +copyright = "2023, Kushal Kolar, Caitlin Lewis" +author = "Kushal Kolar, Caitlin Lewis" release = fastplotlib.__version__ # -- General configuration --------------------------------------------------- @@ -28,21 +28,21 @@ autosummary_generate = True -templates_path = ['_templates'] +templates_path = ["_templates"] exclude_patterns = [] -napoleon_custom_sections = ['Features'] +napoleon_custom_sections = ["Features"] # -- Options for HTML output ------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output html_theme = "furo" -html_static_path = ['_static'] +html_static_path = ["_static"] html_logo = "_static/logo.png" html_title = f"v{release}" -autodoc_member_order = 'groupwise' +autodoc_member_order = "groupwise" autoclass_content = "both" add_module_names = False @@ -50,9 +50,9 @@ autodoc_typehints_description_target = "documented_params" intersphinx_mapping = { - 'python': ('https://docs.python.org/3', None), - 'numpy': ('https://numpy.org/doc/stable/', None), - 'pygfx': ('https://pygfx.readthedocs.io/en/latest', None) + "python": ("https://docs.python.org/3", None), + "numpy": ("https://numpy.org/doc/stable/", None), + "pygfx": ("https://pygfx.readthedocs.io/en/latest", None), } html_theme_options = { diff --git a/docs/source/generate_api.py b/docs/source/generate_api.py index 05e8b0f1c..19b739d1b 100644 --- a/docs/source/generate_api.py +++ b/docs/source/generate_api.py @@ -26,7 +26,7 @@ GRAPHICS_DIR, GRAPHIC_FEATURES_DIR, SELECTORS_DIR, - WIDGETS_DIR + WIDGETS_DIR, ] for source_dir in doc_sources: @@ -66,13 +66,9 @@ def generate_class( ): name = cls.__name__ methods, properties = get_public_members(cls) - methods = [ - f"{name}.{m}" for m in methods - ] + methods = [f"{name}.{m}" for m in methods] - properties = [ - f"{name}.{p}" for p in properties - ] + properties = [f"{name}.{p}" for p in properties] underline = "=" * len(name) @@ -149,32 +145,28 @@ def main(): page_name="Plot", classes=[fastplotlib.Plot], modules=["fastplotlib"], - source_path=LAYOUTS_DIR.joinpath("plot.rst") + source_path=LAYOUTS_DIR.joinpath("plot.rst"), ) generate_page( page_name="GridPlot", classes=[fastplotlib.GridPlot], modules=["fastplotlib"], - source_path=LAYOUTS_DIR.joinpath("gridplot.rst") + source_path=LAYOUTS_DIR.joinpath("gridplot.rst"), ) generate_page( page_name="Subplot", classes=[Subplot], modules=["fastplotlib.layouts._subplot"], - source_path=LAYOUTS_DIR.joinpath("subplot.rst") + source_path=LAYOUTS_DIR.joinpath("subplot.rst"), ) # the rest of this is a mess and can be refactored later - graphic_classes = [ - getattr(graphics, g) for g in graphics.__all__ - ] + graphic_classes = [getattr(graphics, g) for g in graphics.__all__] - graphic_class_names = [ - g.__name__ for g in graphic_classes - ] + graphic_class_names = [g.__name__ for g in graphic_classes] graphic_class_names_str = "\n ".join([""] + graphic_class_names) @@ -194,17 +186,13 @@ def main(): page_name=graphic_cls.__name__, classes=[graphic_cls], modules=["fastplotlib"], - source_path=GRAPHICS_DIR.joinpath(f"{graphic_cls.__name__}.rst") + source_path=GRAPHICS_DIR.joinpath(f"{graphic_cls.__name__}.rst"), ) ############################################################################## - feature_classes = [ - getattr(_features, f) for f in _features.__all__ - ] + feature_classes = [getattr(_features, f) for f in _features.__all__] - feature_class_names = [ - f.__name__ for f in feature_classes - ] + feature_class_names = [f.__name__ for f in feature_classes] feature_class_names_str = "\n ".join([""] + feature_class_names) @@ -223,17 +211,13 @@ def main(): page_name=feature_cls.__name__, classes=[feature_cls], modules=["fastplotlib.graphics._features"], - source_path=GRAPHIC_FEATURES_DIR.joinpath(f"{feature_cls.__name__}.rst") + source_path=GRAPHIC_FEATURES_DIR.joinpath(f"{feature_cls.__name__}.rst"), ) ############################################################################## - selector_classes = [ - getattr(selectors, s) for s in selectors.__all__ - ] + selector_classes = [getattr(selectors, s) for s in selectors.__all__] - selector_class_names = [ - s.__name__ for s in selector_classes - ] + selector_class_names = [s.__name__ for s in selector_classes] selector_class_names_str = "\n ".join([""] + selector_class_names) @@ -252,17 +236,13 @@ def main(): page_name=selector_cls.__name__, classes=[selector_cls], modules=["fastplotlib"], - source_path=SELECTORS_DIR.joinpath(f"{selector_cls.__name__}.rst") + source_path=SELECTORS_DIR.joinpath(f"{selector_cls.__name__}.rst"), ) ############################################################################## - widget_classes = [ - getattr(widgets, w) for w in widgets.__all__ - ] + widget_classes = [getattr(widgets, w) for w in widgets.__all__] - widget_class_names = [ - w.__name__ for w in widget_classes - ] + widget_class_names = [w.__name__ for w in widget_classes] widget_class_names_str = "\n ".join([""] + widget_class_names) @@ -281,7 +261,7 @@ def main(): page_name=widget_cls.__name__, classes=[widget_cls], modules=["fastplotlib"], - source_path=WIDGETS_DIR.joinpath(f"{widget_cls.__name__}.rst") + source_path=WIDGETS_DIR.joinpath(f"{widget_cls.__name__}.rst"), ) ############################################################################## diff --git a/fastplotlib/layouts/graphic_methods_mixin.py b/fastplotlib/layouts/graphic_methods_mixin.py index b00187df7..0376fd777 100644 --- a/fastplotlib/layouts/graphic_methods_mixin.py +++ b/fastplotlib/layouts/graphic_methods_mixin.py @@ -14,13 +14,13 @@ def __init__(self): pass def _create_graphic(self, graphic_class, *args, **kwargs) -> Graphic: - if 'center' in kwargs.keys(): - center = kwargs.pop('center') + if "center" in kwargs.keys(): + center = kwargs.pop("center") else: center = False - if 'name' in kwargs.keys(): - self._check_graphic_name_exists(kwargs['name']) + if "name" in kwargs.keys(): + self._check_graphic_name_exists(kwargs["name"]) graphic = graphic_class(*args, **kwargs) self.add_graphic(graphic, center=center) @@ -28,9 +28,20 @@ def _create_graphic(self, graphic_class, *args, **kwargs) -> Graphic: # only return a proxy to the real graphic return weakref.proxy(graphic) - def add_heatmap(self, data: Any, vmin: int = None, vmax: int = None, cmap: str = 'plasma', filter: str = 'nearest', chunk_size: int = 8192, isolated_buffer: bool = True, *args, **kwargs) -> HeatmapGraphic: + def add_heatmap( + self, + data: Any, + vmin: int = None, + vmax: int = None, + cmap: str = "plasma", + filter: str = "nearest", + chunk_size: int = 8192, + isolated_buffer: bool = True, + *args, + **kwargs + ) -> HeatmapGraphic: """ - + Create an Image Graphic Parameters @@ -78,13 +89,34 @@ def add_heatmap(self, data: Any, vmin: int = None, vmax: int = None, cmap: str = **present**: :class:`.PresentFeature` Control the presence of the Graphic in the scene - - """ - return self._create_graphic(HeatmapGraphic, data, vmin, vmax, cmap, filter, chunk_size, isolated_buffer, *args, **kwargs) - def add_image(self, data: Any, vmin: int = None, vmax: int = None, cmap: str = 'plasma', filter: str = 'nearest', isolated_buffer: bool = True, *args, **kwargs) -> ImageGraphic: """ - + return self._create_graphic( + HeatmapGraphic, + data, + vmin, + vmax, + cmap, + filter, + chunk_size, + isolated_buffer, + *args, + **kwargs + ) + + def add_image( + self, + data: Any, + vmin: int = None, + vmax: int = None, + cmap: str = "plasma", + filter: str = "nearest", + isolated_buffer: bool = True, + *args, + **kwargs + ) -> ImageGraphic: + """ + Create an Image Graphic Parameters @@ -129,13 +161,36 @@ def add_image(self, data: Any, vmin: int = None, vmax: int = None, cmap: str = ' **present**: :class:`.PresentFeature` Control the presence of the Graphic in the scene - - """ - return self._create_graphic(ImageGraphic, data, vmin, vmax, cmap, filter, isolated_buffer, *args, **kwargs) - def add_line_collection(self, data: List[numpy.ndarray], z_position: Union[List[float], float] = None, thickness: Union[float, List[float]] = 2.0, colors: Union[List[numpy.ndarray], numpy.ndarray] = 'w', alpha: float = 1.0, cmap: Union[List[str], str] = None, cmap_values: Union[numpy.ndarray, List] = None, name: str = None, metadata: Union[list, tuple, numpy.ndarray] = None, *args, **kwargs) -> LineCollection: """ - + return self._create_graphic( + ImageGraphic, + data, + vmin, + vmax, + cmap, + filter, + isolated_buffer, + *args, + **kwargs + ) + + def add_line_collection( + self, + data: List[numpy.ndarray], + z_position: Union[List[float], float] = None, + thickness: Union[float, List[float]] = 2.0, + colors: Union[List[numpy.ndarray], numpy.ndarray] = "w", + alpha: float = 1.0, + cmap: Union[List[str], str] = None, + cmap_values: Union[numpy.ndarray, List] = None, + name: str = None, + metadata: Union[list, tuple, numpy.ndarray] = None, + *args, + **kwargs + ) -> LineCollection: + """ + Create a collection of :class:`.LineGraphic` Parameters @@ -189,13 +244,38 @@ def add_line_collection(self, data: List[numpy.ndarray], z_position: Union[List[ See :class:`LineGraphic` details on the features. - - """ - return self._create_graphic(LineCollection, data, z_position, thickness, colors, alpha, cmap, cmap_values, name, metadata, *args, **kwargs) - def add_line(self, data: Any, thickness: float = 2.0, colors: Union[str, numpy.ndarray, Iterable] = 'w', alpha: float = 1.0, cmap: str = None, cmap_values: Union[numpy.ndarray, List] = None, z_position: float = None, collection_index: int = None, *args, **kwargs) -> LineGraphic: """ - + return self._create_graphic( + LineCollection, + data, + z_position, + thickness, + colors, + alpha, + cmap, + cmap_values, + name, + metadata, + *args, + **kwargs + ) + + def add_line( + self, + data: Any, + thickness: float = 2.0, + colors: Union[str, numpy.ndarray, Iterable] = "w", + alpha: float = 1.0, + cmap: str = None, + cmap_values: Union[numpy.ndarray, List] = None, + z_position: float = None, + collection_index: int = None, + *args, + **kwargs + ) -> LineGraphic: + """ + Create a line Graphic, 2d or 3d Parameters @@ -247,13 +327,37 @@ def add_line(self, data: Any, thickness: float = 2.0, colors: Union[str, numpy.n **present**: :class:`.PresentFeature` Control the presence of the Graphic in the scene, set to ``True`` or ``False`` - - """ - return self._create_graphic(LineGraphic, data, thickness, colors, alpha, cmap, cmap_values, z_position, collection_index, *args, **kwargs) - def add_line_stack(self, data: List[numpy.ndarray], z_position: Union[List[float], float] = None, thickness: Union[float, List[float]] = 2.0, colors: Union[List[numpy.ndarray], numpy.ndarray] = 'w', cmap: Union[List[str], str] = None, separation: float = 10, separation_axis: str = 'y', name: str = None, *args, **kwargs) -> LineStack: """ - + return self._create_graphic( + LineGraphic, + data, + thickness, + colors, + alpha, + cmap, + cmap_values, + z_position, + collection_index, + *args, + **kwargs + ) + + def add_line_stack( + self, + data: List[numpy.ndarray], + z_position: Union[List[float], float] = None, + thickness: Union[float, List[float]] = 2.0, + colors: Union[List[numpy.ndarray], numpy.ndarray] = "w", + cmap: Union[List[str], str] = None, + separation: float = 10, + separation_axis: str = "y", + name: str = None, + *args, + **kwargs + ) -> LineStack: + """ + Create a stack of :class:`.LineGraphic` that are separated along the "x" or "y" axis. Parameters @@ -309,13 +413,36 @@ def add_line_stack(self, data: List[numpy.ndarray], z_position: Union[List[float See :class:`LineGraphic` details on the features. - - """ - return self._create_graphic(LineStack, data, z_position, thickness, colors, cmap, separation, separation_axis, name, *args, **kwargs) - def add_scatter(self, data: numpy.ndarray, sizes: Union[int, float, numpy.ndarray, list] = 1, colors: numpy.ndarray = 'w', alpha: float = 1.0, cmap: str = None, cmap_values: Union[numpy.ndarray, List] = None, z_position: float = 0.0, *args, **kwargs) -> ScatterGraphic: """ - + return self._create_graphic( + LineStack, + data, + z_position, + thickness, + colors, + cmap, + separation, + separation_axis, + name, + *args, + **kwargs + ) + + def add_scatter( + self, + data: numpy.ndarray, + sizes: Union[int, float, numpy.ndarray, list] = 1, + colors: numpy.ndarray = "w", + alpha: float = 1.0, + cmap: str = None, + cmap_values: Union[numpy.ndarray, List] = None, + z_position: float = 0.0, + *args, + **kwargs + ) -> ScatterGraphic: + """ + Create a Scatter Graphic, 2d or 3d Parameters @@ -364,13 +491,36 @@ def add_scatter(self, data: numpy.ndarray, sizes: Union[int, float, numpy.ndarra **present**: :class:`.PresentFeature` Control the presence of the Graphic in the scene, set to ``True`` or ``False`` - - """ - return self._create_graphic(ScatterGraphic, data, sizes, colors, alpha, cmap, cmap_values, z_position, *args, **kwargs) - def add_text(self, text: str, position: Tuple[int] = (0, 0, 0), size: int = 14, face_color: Union[str, numpy.ndarray] = 'w', outline_color: Union[str, numpy.ndarray] = 'w', outline_thickness=0, screen_space: bool = True, anchor: str = 'middle-center', *args, **kwargs) -> TextGraphic: """ - + return self._create_graphic( + ScatterGraphic, + data, + sizes, + colors, + alpha, + cmap, + cmap_values, + z_position, + *args, + **kwargs + ) + + def add_text( + self, + text: str, + position: Tuple[int] = (0, 0, 0), + size: int = 14, + face_color: Union[str, numpy.ndarray] = "w", + outline_color: Union[str, numpy.ndarray] = "w", + outline_thickness=0, + screen_space: bool = True, + anchor: str = "middle-center", + *args, + **kwargs + ) -> TextGraphic: + """ + Create a text Graphic Parameters @@ -405,7 +555,18 @@ def add_text(self, text: str, position: Tuple[int] = (0, 0, 0), size: int = 14, * Vertical values: "top", "middle", "baseline", "bottom" * Horizontal values: "left", "center", "right" - - """ - return self._create_graphic(TextGraphic, text, position, size, face_color, outline_color, outline_thickness, screen_space, anchor, *args, **kwargs) + """ + return self._create_graphic( + TextGraphic, + text, + position, + size, + face_color, + outline_color, + outline_thickness, + screen_space, + anchor, + *args, + **kwargs + ) diff --git a/fastplotlib/utils/generate_add_methods.py b/fastplotlib/utils/generate_add_methods.py index 9cb87baab..100ad7757 100644 --- a/fastplotlib/utils/generate_add_methods.py +++ b/fastplotlib/utils/generate_add_methods.py @@ -1,11 +1,15 @@ import inspect import pathlib +import black + +root = pathlib.Path(__file__).parent.parent.resolve() +filename = root.joinpath("layouts/graphic_methods_mixin.py") + # if there is an existing mixin class, replace it with an empty class # so that fastplotlib will import # hacky but it works -current_module = pathlib.Path(__file__).parent.parent.resolve() -with open(current_module.joinpath("layouts/graphic_methods_mixin.py"), "w") as f: +with open(filename, "w") as f: f.write(f"class GraphicMethodsMixin:\n" f" pass") from fastplotlib import graphics @@ -20,8 +24,7 @@ def generate_add_graphics_methods(): # clear file and regenerate from scratch - - f = open(current_module.joinpath("layouts/graphic_methods_mixin.py"), "w") + f = open(filename, "w", encoding="utf-8") f.write("# This is an auto-generated file and should not be modified directly\n\n") @@ -72,5 +75,17 @@ def generate_add_graphics_methods(): f.close() +def blacken(): + with open(filename, "r", encoding="utf-8") as f: + text = f.read() + + mode = black.FileMode(line_length=88) + text = black.format_str(text, mode=mode) + + with open(filename, "w", encoding="utf-8") as f: + f.write(text) + + if __name__ == "__main__": generate_add_graphics_methods() + blacken() diff --git a/setup.py b/setup.py index 1d274fc0a..2622b1406 100644 --- a/setup.py +++ b/setup.py @@ -20,19 +20,15 @@ "nbsphinx", "pandoc", "jupyterlab", - "sidecar" + "sidecar", ], - - "notebook": - [ + "notebook": [ "jupyterlab", "jupyter-rfb>=0.4.1", "ipywidgets>=8.0.0,<9", - "sidecar" + "sidecar", ], - - "tests": - [ + "tests": [ "pytest<8.0.0", "nbmake", "scipy", @@ -42,17 +38,15 @@ "ipywidgets>=8.0.0,<9", "scikit-learn", "tqdm", - "sidecar" + "sidecar", ], - - "tests-desktop": - [ + "tests-desktop": [ "pytest<8.0.0", "scipy", "imageio", "scikit-learn", "tqdm", - ] + ], } @@ -72,19 +66,18 @@ setup( - name='fastplotlib', + name="fastplotlib", version=ver, long_description=readme, - long_description_content_type='text/markdown', + long_description_content_type="text/markdown", packages=find_packages(), - url='https://github.com/fastplotlib/fastplotlib', - license='Apache 2.0', - author='Kushal Kolar, Caitlin Lewis', - author_email='', - python_requires='>=3.9', + url="https://github.com/fastplotlib/fastplotlib", + license="Apache 2.0", + author="Kushal Kolar, Caitlin Lewis", + author_email="", + python_requires=">=3.9", install_requires=install_requires, extras_require=extras_require, include_package_data=True, - description='A fast plotting library built using the pygfx render engine' + description="A fast plotting library built using the pygfx render engine", ) - From 576ccbf29844b3bd3222ba69ae669f51e220605e Mon Sep 17 00:00:00 2001 From: Eric Thomson Date: Tue, 19 Mar 2024 22:56:39 -0400 Subject: [PATCH 047/185] Add black to setup tests reqs and add black CI action (#436) --- .github/workflows/black.yml | 12 ++++++++++++ .github/workflows/ci.yml | 1 - .gitignore | 3 +++ setup.py | 1 + 4 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/black.yml diff --git a/.github/workflows/black.yml b/.github/workflows/black.yml new file mode 100644 index 000000000..bcb2d2b33 --- /dev/null +++ b/.github/workflows/black.yml @@ -0,0 +1,12 @@ +name: Lint + +on: [push, pull_request] + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: psf/black@stable + with: + src: "./fastplotlib" \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fddfae5f4..40d39c4f6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,6 @@ on: - ready_for_review jobs: - docs-build: name: Docs runs-on: bigmem diff --git a/.gitignore b/.gitignore index 9857fe4b2..c599d5f8c 100644 --- a/.gitignore +++ b/.gitignore @@ -131,4 +131,7 @@ dmypy.json # Pycharm .idea/ +# vs code +.vscode/ + examples/desktop/diffs/*.png diff --git a/setup.py b/setup.py index 2622b1406..a06a879a5 100644 --- a/setup.py +++ b/setup.py @@ -31,6 +31,7 @@ "tests": [ "pytest<8.0.0", "nbmake", + "black", "scipy", "imageio[pyav]", "jupyterlab", From fb8aedc7e4a1d6ecc1e2bd1b7613f8353280439d Mon Sep 17 00:00:00 2001 From: Caitlin Lewis <69729525+clewis7@users.noreply.github.com> Date: Fri, 22 Mar 2024 21:17:33 -0400 Subject: [PATCH 048/185] update for changes in pygfx@main (#443) * update points material kwarg * update scatter examples & screenshots --- examples/desktop/scatter/scatter_cmap.py | 6 +----- examples/desktop/scatter/scatter_dataslice.py | 2 +- examples/desktop/scatter/scatter_size.py | 21 +++++++------------ examples/desktop/screenshots/scatter.png | 4 ++-- examples/desktop/screenshots/scatter_cmap.png | 4 ++-- .../screenshots/scatter_colorslice.png | 4 ++-- .../desktop/screenshots/scatter_dataslice.png | 4 ++-- .../desktop/screenshots/scatter_present.png | 4 ++-- examples/desktop/screenshots/scatter_size.png | 4 ++-- fastplotlib/graphics/scatter.py | 2 +- 10 files changed, 23 insertions(+), 32 deletions(-) diff --git a/examples/desktop/scatter/scatter_cmap.py b/examples/desktop/scatter/scatter_cmap.py index edc55a4b1..3e986d5d5 100644 --- a/examples/desktop/scatter/scatter_cmap.py +++ b/examples/desktop/scatter/scatter_cmap.py @@ -26,11 +26,7 @@ scatter_graphic = plot.add_scatter( - data=data[:, :-1], - sizes=15, - alpha=0.7, - cmap="Set1", - cmap_values=agg.labels_ + data=data[:, :-1], sizes=15, alpha=0.7, cmap="Set1", cmap_values=agg.labels_ ) plot.show() diff --git a/examples/desktop/scatter/scatter_dataslice.py b/examples/desktop/scatter/scatter_dataslice.py index 22c495bff..3008aab61 100644 --- a/examples/desktop/scatter/scatter_dataslice.py +++ b/examples/desktop/scatter/scatter_dataslice.py @@ -34,7 +34,7 @@ scatter_graphic.data[2] = np.array([[5.2, 2.7, 1.7]]) scatter_graphic.data[10:15] = scatter_graphic.data[0:5] + np.array([1, 1, 1]) -scatter_graphic.data[50:100:2] = scatter_graphic.data[100:150:2] + np.array([1,1,0]) +scatter_graphic.data[50:100:2] = scatter_graphic.data[100:150:2] + np.array([1, 1, 0]) if __name__ == "__main__": diff --git a/examples/desktop/scatter/scatter_size.py b/examples/desktop/scatter/scatter_size.py index 2ad995584..5c1f97703 100644 --- a/examples/desktop/scatter/scatter_size.py +++ b/examples/desktop/scatter/scatter_size.py @@ -12,28 +12,23 @@ grid_shape = (2, 1) # you can give string names for each subplot within the gridplot -names = [ - ["scalar_size"], - ["array_size"] -] +names = [["scalar_size"], ["array_size"]] # Create the grid plot -plot = fpl.GridPlot( - shape=grid_shape, - names=names, - size=(1000, 1000) -) +plot = fpl.GridPlot(shape=grid_shape, names=names, size=(1000, 1000)) # get y_values using sin function -angles = np.arange(0, 20*np.pi+0.001, np.pi / 20) -y_values = 30*np.sin(angles) # 1 thousand points +angles = np.arange(0, 20 * np.pi + 0.001, np.pi / 20) +y_values = 30 * np.sin(angles) # 1 thousand points x_values = np.array([x for x in range(len(y_values))], dtype=np.float32) data = np.column_stack([x_values, y_values]) -plot["scalar_size"].add_scatter(data=data, sizes=5, colors="blue") # add a set of scalar sizes +plot["scalar_size"].add_scatter( + data=data, sizes=5, colors="blue" +) # add a set of scalar sizes -non_scalar_sizes = np.abs((y_values / np.pi)) # ensure minimum size of 5 +non_scalar_sizes = np.abs((y_values / np.pi)) # ensure minimum size of 5 plot["array_size"].add_scatter(data=data, sizes=non_scalar_sizes, colors="red") for graph in plot: diff --git a/examples/desktop/screenshots/scatter.png b/examples/desktop/screenshots/scatter.png index bf5e8c92a..d01d36707 100644 --- a/examples/desktop/screenshots/scatter.png +++ b/examples/desktop/screenshots/scatter.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bd38399b77e09d915c5bb1e7ee022f936ae90682f598357bc774a95c372dc78f -size 25231 +oid sha256:8d840f02d1c4be5ea11adfc224481a5b8b306cbc904e099af4d3fdd5ab7f383f +size 26683 diff --git a/examples/desktop/screenshots/scatter_cmap.png b/examples/desktop/screenshots/scatter_cmap.png index eec22566a..7f0bba38a 100644 --- a/examples/desktop/screenshots/scatter_cmap.png +++ b/examples/desktop/screenshots/scatter_cmap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e712693b403166909dcaa65256131eacba0a15892cd144ad97fdecb6b9835e93 -size 57273 +oid sha256:e9aba2f37c7682d68569e1bac7afac5f243afb98ab94d9957de4b59f9d3dd1c0 +size 57257 diff --git a/examples/desktop/screenshots/scatter_colorslice.png b/examples/desktop/screenshots/scatter_colorslice.png index 0da0fcd9f..27249e63e 100644 --- a/examples/desktop/screenshots/scatter_colorslice.png +++ b/examples/desktop/screenshots/scatter_colorslice.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c620cad9976f390e44a5b037f3ff61fb80e6487e17f4be8118be5df55f276a35 -size 23664 +oid sha256:39c49529552d6ace3d67b37f0c660e9734fcb763bdc165484f356ad8cffc908e +size 25218 diff --git a/examples/desktop/screenshots/scatter_dataslice.png b/examples/desktop/screenshots/scatter_dataslice.png index 32f56ad11..155510885 100644 --- a/examples/desktop/screenshots/scatter_dataslice.png +++ b/examples/desktop/screenshots/scatter_dataslice.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:69d2f0999b0bb334e48320702095fc76444f4d89d43a51ac6c5c8f49e1df96ac -size 25999 +oid sha256:20757e215c4c208e08027f8e2b798691f421ce7662dc86be3615dc41084686f3 +size 27392 diff --git a/examples/desktop/screenshots/scatter_present.png b/examples/desktop/screenshots/scatter_present.png index 8c1e5eed4..87685fe90 100644 --- a/examples/desktop/screenshots/scatter_present.png +++ b/examples/desktop/screenshots/scatter_present.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e45c1a936771e569e562ed3496421e498e725325093e84243ab494c0718ead3a -size 23639 +oid sha256:7a8ceb8f3d7203f0569374993784f2229448ace08b0abee94910e6ae71ceca29 +size 24587 diff --git a/examples/desktop/screenshots/scatter_size.png b/examples/desktop/screenshots/scatter_size.png index da211cde1..afe8a0b35 100644 --- a/examples/desktop/screenshots/scatter_size.png +++ b/examples/desktop/screenshots/scatter_size.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:10533aa5831a50a0f9b38c0a60b89a9b6c33311ecb3a569c5e0b4c82379dc20a -size 66037 +oid sha256:a184ce0a202bc03fa61b3f4149109e44160bfd326c26f46db3b83ce0cf1699a6 +size 67710 diff --git a/fastplotlib/graphics/scatter.py b/fastplotlib/graphics/scatter.py index f6104aeb7..1c579eaa5 100644 --- a/fastplotlib/graphics/scatter.py +++ b/fastplotlib/graphics/scatter.py @@ -93,7 +93,7 @@ def __init__( pygfx.Geometry( positions=self.data(), sizes=self.sizes(), colors=self.colors() ), - material=pygfx.PointsMaterial(color_mode="vertex", vertex_sizes=True), + material=pygfx.PointsMaterial(color_mode="vertex", size_mode="vertex"), ) self._set_world_object(world_object) From 758d3ed45253195aab09528beca2494b2a128151 Mon Sep 17 00:00:00 2001 From: Caitlin Lewis <69729525+clewis7@users.noreply.github.com> Date: Fri, 22 Mar 2024 21:35:51 -0400 Subject: [PATCH 049/185] add codeowners file (#442) --- .github/CODEOWNERS | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 000000000..a3e89ef76 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,2 @@ +* @kushalkolar +* @clewis7 \ No newline at end of file From 97168ff2bef8c6d732da4266afd8fd8f10e983e6 Mon Sep 17 00:00:00 2001 From: Edoardo Balzani Date: Wed, 27 Mar 2024 19:40:39 -0400 Subject: [PATCH 050/185] Rotation (#439) * added rotation property * added a rotate method to rotate along axis --- fastplotlib/graphics/_base.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/fastplotlib/graphics/_base.py b/fastplotlib/graphics/_base.py index 91ccb143e..f7a0ec102 100644 --- a/fastplotlib/graphics/_base.py +++ b/fastplotlib/graphics/_base.py @@ -5,6 +5,7 @@ from dataclasses import dataclass import numpy as np +import pylinalg as la from pygfx import WorldObject @@ -142,6 +143,14 @@ def position_y(self, val): def position_z(self, val): self.world_object.world.z = val + @property + def rotation(self): + return self.world_object.local.rotation + + @rotation.setter + def rotation(self, val): + self.world_object.local.rotation = val + @property def visible(self) -> bool: """Access or change the visibility.""" @@ -196,6 +205,28 @@ def __del__(self): self.deleted = True del WORLD_OBJECTS[self.loc] + def rotate(self, alpha: float, axis: Literal["x", "y", "z"] = "y"): + """Rotate the Graphic with respect to the world. + + Parameters + ---------- + alpha : + Rotation angle in radians. + axis : + Rotation axis label. + """ + if axis == "x": + rot = la.quat_from_euler((alpha, 0), order="XY") + elif axis == "y": + rot = la.quat_from_euler((0, alpha), order="XY") + elif axis == "z": + rot = la.quat_from_euler((0, alpha), order="XZ") + else: + raise ValueError( + f"`axis` must be either `x`, `y`, or `z`. `{axis}` provided instead!" + ) + self.rotation = la.quat_mul(rot, self.rotation) + class Interaction(ABC): """Mixin class that makes graphics interactive""" From 99e8a0b1036ba7a8e635c358869c3b1ee6a3c50d Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Wed, 27 Mar 2024 19:44:06 -0400 Subject: [PATCH 051/185] require only one reviewer (#455) --- .github/CODEOWNERS | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index a3e89ef76..f33328dce 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,2 +1 @@ -* @kushalkolar -* @clewis7 \ No newline at end of file +* @kushalkolar @clewis7 From 002165259d0a83eec93e37174e8a1b59e07a1a11 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Fri, 29 Mar 2024 02:20:19 +0100 Subject: [PATCH 052/185] Use simpler form of super() where possible (#453) --- fastplotlib/graphics/_base.py | 2 +- fastplotlib/graphics/_features/_colors.py | 11 +++++------ fastplotlib/graphics/_features/_data.py | 6 ++---- fastplotlib/graphics/_features/_deleted.py | 2 +- fastplotlib/graphics/_features/_present.py | 2 +- fastplotlib/graphics/_features/_selection_features.py | 4 ++-- fastplotlib/graphics/_features/_sizes.py | 4 +--- fastplotlib/graphics/_features/_thickness.py | 2 +- fastplotlib/graphics/histogram.py | 6 ++---- fastplotlib/graphics/line.py | 2 +- fastplotlib/graphics/line_collection.py | 4 ++-- fastplotlib/graphics/scatter.py | 2 +- fastplotlib/graphics/selectors/_rectangle_region.py | 2 +- fastplotlib/graphics/text.py | 2 +- fastplotlib/layouts/_plot.py | 4 ++-- fastplotlib/layouts/_subplot.py | 4 ++-- 16 files changed, 26 insertions(+), 33 deletions(-) diff --git a/fastplotlib/graphics/_base.py b/fastplotlib/graphics/_base.py index f7a0ec102..fdf120268 100644 --- a/fastplotlib/graphics/_base.py +++ b/fastplotlib/graphics/_base.py @@ -439,7 +439,7 @@ class GraphicCollection(Graphic): """Graphic Collection base class""" def __init__(self, name: str = None): - super(GraphicCollection, self).__init__(name) + super().__init__(name) self._graphics: List[str] = list() self._graphics_changed: bool = True diff --git a/fastplotlib/graphics/_features/_colors.py b/fastplotlib/graphics/_features/_colors.py index ad9e673ef..48405e74c 100644 --- a/fastplotlib/graphics/_features/_colors.py +++ b/fastplotlib/graphics/_features/_colors.py @@ -124,9 +124,7 @@ def __init__( if alpha != 1.0: data[:, -1] = alpha - super(ColorFeature, self).__init__( - parent, data, collection_index=collection_index - ) + super().__init__(parent, data, collection_index=collection_index) def __setitem__(self, key, value): # parse numerical slice indices @@ -253,6 +251,7 @@ class CmapFeature(ColorFeature): """ def __init__(self, parent, colors, cmap_name: str, cmap_values: np.ndarray): + # Skip the ColorFeature's __init__ super(ColorFeature, self).__init__(parent, colors) self._cmap_name = cmap_name @@ -278,7 +277,7 @@ def __setitem__(self, key, cmap_name): ) self._cmap_name = cmap_name - super(CmapFeature, self).__setitem__(key, colors) + super().__setitem__(key, colors) @property def name(self) -> str: @@ -299,7 +298,7 @@ def values(self, values: np.ndarray): self._cmap_values = values - super(CmapFeature, self).__setitem__(slice(None), colors) + super().__setitem__(slice(None), colors) def __repr__(self) -> str: s = f"CmapFeature for {self._parent}, to get name or values: `.cmap.name`, `.cmap.values`" @@ -330,7 +329,7 @@ class ImageCmapFeature(GraphicFeature): def __init__(self, parent, cmap: str): cmap_texture_view = get_cmap_texture(cmap) - super(ImageCmapFeature, self).__init__(parent, cmap_texture_view) + super().__init__(parent, cmap_texture_view) self._name = cmap def _set(self, cmap_name: str): diff --git a/fastplotlib/graphics/_features/_data.py b/fastplotlib/graphics/_features/_data.py index 23e80b470..bcfe9446a 100644 --- a/fastplotlib/graphics/_features/_data.py +++ b/fastplotlib/graphics/_features/_data.py @@ -21,9 +21,7 @@ class PointsDataFeature(GraphicFeatureIndexable): def __init__(self, parent, data: Any, collection_index: int = None): data = self._fix_data(data, parent) - super(PointsDataFeature, self).__init__( - parent, data, collection_index=collection_index - ) + super().__init__(parent, data, collection_index=collection_index) @property def buffer(self) -> pygfx.Buffer: @@ -117,7 +115,7 @@ def __init__(self, parent, data: Any): "``[x_dim, y_dim]`` or ``[x_dim, y_dim, rgb]``" ) - super(ImageDataFeature, self).__init__(parent, data) + super().__init__(parent, data) @property def buffer(self) -> pygfx.Texture: diff --git a/fastplotlib/graphics/_features/_deleted.py b/fastplotlib/graphics/_features/_deleted.py index 2fca1c719..7900385eb 100644 --- a/fastplotlib/graphics/_features/_deleted.py +++ b/fastplotlib/graphics/_features/_deleted.py @@ -16,7 +16,7 @@ class Deleted(GraphicFeature): """ def __init__(self, parent, value: bool): - super(Deleted, self).__init__(parent, value) + super().__init__(parent, value) def _set(self, value: bool): value = self._parse_set_value(value) diff --git a/fastplotlib/graphics/_features/_present.py b/fastplotlib/graphics/_features/_present.py index 6fbf93b48..a73d66523 100644 --- a/fastplotlib/graphics/_features/_present.py +++ b/fastplotlib/graphics/_features/_present.py @@ -23,7 +23,7 @@ class PresentFeature(GraphicFeature): def __init__(self, parent, present: bool = True, collection_index: int = False): self._scene = None - super(PresentFeature, self).__init__(parent, present, collection_index) + super().__init__(parent, present, collection_index) def _set(self, present: bool): present = self._parse_set_value(present) diff --git a/fastplotlib/graphics/_features/_selection_features.py b/fastplotlib/graphics/_features/_selection_features.py index 294bb15d6..21e5d0a09 100644 --- a/fastplotlib/graphics/_features/_selection_features.py +++ b/fastplotlib/graphics/_features/_selection_features.py @@ -27,7 +27,7 @@ class LinearSelectionFeature(GraphicFeature): """ def __init__(self, parent, axis: str, value: float, limits: Tuple[int, int]): - super(LinearSelectionFeature, self).__init__(parent, data=value) + super().__init__(parent, data=value) self._axis = axis self._limits = limits @@ -99,7 +99,7 @@ class LinearRegionSelectionFeature(GraphicFeature): def __init__( self, parent, selection: Tuple[int, int], axis: str, limits: Tuple[int, int] ): - super(LinearRegionSelectionFeature, self).__init__(parent, data=selection) + super().__init__(parent, data=selection) self._axis = axis self._limits = limits diff --git a/fastplotlib/graphics/_features/_sizes.py b/fastplotlib/graphics/_features/_sizes.py index 403760508..2ceeb7862 100644 --- a/fastplotlib/graphics/_features/_sizes.py +++ b/fastplotlib/graphics/_features/_sizes.py @@ -21,9 +21,7 @@ class PointsSizesFeature(GraphicFeatureIndexable): def __init__(self, parent, sizes: Any, collection_index: int = None): sizes = self._fix_sizes(sizes, parent) - super(PointsSizesFeature, self).__init__( - parent, sizes, collection_index=collection_index - ) + super().__init__(parent, sizes, collection_index=collection_index) @property def buffer(self) -> pygfx.Buffer: diff --git a/fastplotlib/graphics/_features/_thickness.py b/fastplotlib/graphics/_features/_thickness.py index f9190f0b1..fc90ef96f 100644 --- a/fastplotlib/graphics/_features/_thickness.py +++ b/fastplotlib/graphics/_features/_thickness.py @@ -19,7 +19,7 @@ class ThicknessFeature(GraphicFeature): def __init__(self, parent, thickness: float): self._scene = None - super(ThicknessFeature, self).__init__(parent, thickness) + super().__init__(parent, thickness) def _set(self, value: float): value = self._parse_set_value(value) diff --git a/fastplotlib/graphics/histogram.py b/fastplotlib/graphics/histogram.py index 6efd83a96..b78be39d3 100644 --- a/fastplotlib/graphics/histogram.py +++ b/fastplotlib/graphics/histogram.py @@ -10,7 +10,7 @@ class _HistogramBin(pygfx.Mesh): def __int__(self, *args, **kwargs): - super(_HistogramBin, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.bin_center: float = None self.frequency: Union[int, float] = None @@ -93,9 +93,7 @@ def __init__( data = np.vstack([x_positions_bins, self.hist]) - super(HistogramGraphic, self).__init__( - data=data, colors=colors, n_colors=n_bins, **kwargs - ) + super().__init__(data=data, colors=colors, n_colors=n_bins, **kwargs) self._world_object: pygfx.Group = pygfx.Group() diff --git a/fastplotlib/graphics/line.py b/fastplotlib/graphics/line.py index 9ac7568a7..d76c8e704 100644 --- a/fastplotlib/graphics/line.py +++ b/fastplotlib/graphics/line.py @@ -102,7 +102,7 @@ def __init__( self, self.colors(), cmap_name=cmap, cmap_values=cmap_values ) - super(LineGraphic, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) if thickness < 1.1: material = pygfx.LineThinMaterial diff --git a/fastplotlib/graphics/line_collection.py b/fastplotlib/graphics/line_collection.py index a5c398130..bb7bb2444 100644 --- a/fastplotlib/graphics/line_collection.py +++ b/fastplotlib/graphics/line_collection.py @@ -87,7 +87,7 @@ def __init__( """ - super(LineCollection, self).__init__(name) + super().__init__(name) if not isinstance(z_position, float) and z_position is not None: if len(data) != len(z_position): @@ -544,7 +544,7 @@ def __init__( See :class:`LineGraphic` details on the features. """ - super(LineStack, self).__init__( + super().__init__( data=data, z_position=z_position, thickness=thickness, diff --git a/fastplotlib/graphics/scatter.py b/fastplotlib/graphics/scatter.py index 1c579eaa5..3f04f644e 100644 --- a/fastplotlib/graphics/scatter.py +++ b/fastplotlib/graphics/scatter.py @@ -87,7 +87,7 @@ def __init__( ) self.sizes = PointsSizesFeature(self, sizes) - super(ScatterGraphic, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) world_object = pygfx.Points( pygfx.Geometry( diff --git a/fastplotlib/graphics/selectors/_rectangle_region.py b/fastplotlib/graphics/selectors/_rectangle_region.py index 0d7dd3661..1081a49a9 100644 --- a/fastplotlib/graphics/selectors/_rectangle_region.py +++ b/fastplotlib/graphics/selectors/_rectangle_region.py @@ -28,7 +28,7 @@ class RectangleBoundsFeature(GraphicFeature): def __init__( self, parent, bounds: Tuple[int, int], axis: str, limits: Tuple[int, int] ): - super(RectangleBoundsFeature, self).__init__(parent, data=bounds) + super().__init__(parent, data=bounds) self._axis = axis self.limits = limits diff --git a/fastplotlib/graphics/text.py b/fastplotlib/graphics/text.py index a8a873287..a486b1bd2 100644 --- a/fastplotlib/graphics/text.py +++ b/fastplotlib/graphics/text.py @@ -55,7 +55,7 @@ def __init__( * Vertical values: "top", "middle", "baseline", "bottom" * Horizontal values: "left", "center", "right" """ - super(TextGraphic, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self._text = text diff --git a/fastplotlib/layouts/_plot.py b/fastplotlib/layouts/_plot.py index 34027a276..77aba0adc 100644 --- a/fastplotlib/layouts/_plot.py +++ b/fastplotlib/layouts/_plot.py @@ -45,7 +45,7 @@ def __init__( passed to Subplot, for example ``name`` """ - super(Plot, self).__init__( + super().__init__( parent=None, position=(0, 0), parent_dims=(1, 1), @@ -62,7 +62,7 @@ def __init__( def render(self): """performs a single render of the plot, not for the user""" - super(Plot, self).render() + super().render() self.renderer.flush() self.canvas.request_draw() diff --git a/fastplotlib/layouts/_subplot.py b/fastplotlib/layouts/_subplot.py index e776fddb6..6fa5f890e 100644 --- a/fastplotlib/layouts/_subplot.py +++ b/fastplotlib/layouts/_subplot.py @@ -214,7 +214,7 @@ def __init__( self._size = size - super(Dock, self).__init__( + super().__init__( parent=parent, position=position, camera=pygfx.OrthographicCamera(), @@ -349,4 +349,4 @@ def render(self): if self.size == 0: return - super(Dock, self).render() + super().render() From 60cf919cd5c38dd9d500437cbb0dbb7bfef8a8af Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Sat, 30 Mar 2024 01:48:49 -0400 Subject: [PATCH 053/185] FASTPLOTLIB_NB_TESTS required for nb test funcs to run (#460) --- .github/workflows/ci.yml | 2 +- examples/notebooks/nb_test_utils.py | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 40d39c4f6..8f92bafb8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -93,7 +93,7 @@ jobs: PYGFX_EXPECT_LAVAPIPE: true run: | pytest -v examples - pytest --nbmake examples/notebooks/ + FASTPLOTLIB_NB_TESTS=1 pytest --nbmake examples/notebooks/ - uses: actions/upload-artifact@v3 if: ${{ failure() }} with: diff --git a/examples/notebooks/nb_test_utils.py b/examples/notebooks/nb_test_utils.py index e16ed2eaf..71d2d5114 100644 --- a/examples/notebooks/nb_test_utils.py +++ b/examples/notebooks/nb_test_utils.py @@ -21,7 +21,19 @@ FAILURES = list() +def _run_tests(): + if "FASTPLOTLIB_NB_TESTS" not in os.environ.keys(): + return False + + if os.environ["FASTPLOTLIB_NB_TESTS"] == "1": + return True + + return False + + def plot_test(name, plot: Union[Plot, GridPlot]): + if not _run_tests(): + return snapshot = plot.canvas.snapshot() if "REGENERATE_SCREENSHOTS" in os.environ.keys(): @@ -81,6 +93,9 @@ def get_diffs_rgba(slicer): def notebook_finished(): + if not _run_tests(): + return + if len(FAILURES) > 0: raise AssertionError( f"Failures for plots:\n{FAILURES}" From af23b584ba50d0a5b9fc7848dc166cc3aa702c37 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Sun, 31 Mar 2024 01:22:30 +0100 Subject: [PATCH 054/185] Improvements to GUI selection and Qt support (#431) * Improved GUI selection wip * Support all modern qt libs * comment * make the ipywidgets imports conditional * Adjust for new wgpu * set min wgpu version * fixes * top might not necessarily the default --- docs/source/user_guide/gpu.rst | 39 ++----- fastplotlib/__init__.py | 9 +- fastplotlib/graphics/selectors/_linear.py | 16 ++- .../graphics/selectors/_linear_region.py | 18 ++- fastplotlib/layouts/_frame/_frame.py | 40 +------ fastplotlib/layouts/_frame/_qt_output.py | 3 +- fastplotlib/layouts/_frame/_qt_toolbar.py | 3 +- .../layouts/_frame/_qtoolbar_template.py | 5 +- fastplotlib/layouts/_gridplot.py | 12 +- fastplotlib/layouts/_plot.py | 4 +- fastplotlib/layouts/_plot_area.py | 6 +- fastplotlib/layouts/_subplot.py | 4 +- fastplotlib/layouts/_utils.py | 74 ++---------- fastplotlib/utils/__init__.py | 1 - fastplotlib/utils/_gpu_info.py | 66 ----------- fastplotlib/utils/gui.py | 108 ++++++++++++++++++ fastplotlib/widgets/image.py | 16 +-- setup.py | 1 + 18 files changed, 172 insertions(+), 253 deletions(-) delete mode 100644 fastplotlib/utils/_gpu_info.py create mode 100644 fastplotlib/utils/gui.py diff --git a/docs/source/user_guide/gpu.rst b/docs/source/user_guide/gpu.rst index 006f78872..18fb81cdf 100644 --- a/docs/source/user_guide/gpu.rst +++ b/docs/source/user_guide/gpu.rst @@ -33,39 +33,18 @@ View available GPU You can view all GPUs that are available to ``WGPU`` like this:: - from wgpu.backends.wgpu_native import enumerate_adapters - from pprint import pprint + import wgpu - for adapter in enumerate_adapters(): - pprint(adapter.request_adapter_info()) + for adapter in wgpu.gpu.enumerate_adapters(): + print(adapter.summary) For example, on a Thinkpad AMD laptop with a dedicated nvidia GPU this returns:: - {'adapter_type': 'IntegratedGPU', - 'architecture': '', - 'backend_type': 'Vulkan', - 'description': 'Mesa 22.3.6', - 'device': 'AMD Radeon Graphics (RADV REMBRANDT)', - 'vendor': 'radv'} - {'adapter_type': 'DiscreteGPU', - 'architecture': '', - 'backend_type': 'Vulkan', - 'description': '535.129.03', - 'device': 'NVIDIA T1200 Laptop GPU', - 'vendor': 'NVIDIA'} - {'adapter_type': 'CPU', - 'architecture': '', - 'backend_type': 'Vulkan', - 'description': 'Mesa 22.3.6 (LLVM 15.0.6)', - 'device': 'llvmpipe (LLVM 15.0.6, 256 bits)', - 'vendor': 'llvmpipe'} - {'adapter_type': 'Unknown', - 'architecture': '', - 'backend_type': 'OpenGL', - 'description': '', - 'device': 'AMD Radeon Graphics (rembrandt, LLVM 15.0.6, DRM 3.52, ' - '6.4.0-0.deb12.2-amd64)', - 'vendor': ''} + AMD Radeon Graphics (RADV REMBRANDT) (IntegratedGPU) on Vulkan + NVIDIA T1200 Laptop GPU (DiscreteGPU) on Vulkan + llvmpipe (LLVM 15.0.6, 256 bits) (CPU) on Vulkan + AMD Radeon Graphics (rembrandt, LLVM 15.0.6, DRM 3.52, 6.4.0-0.deb12.2-amd64) (Unknown) on OpenGL + GPU currently in use -------------------- @@ -78,5 +57,5 @@ If you want to know the GPU that a current plot is using you can check the adapt plot.show() # GPU that is currently in use by the renderer - plot.renderer.device.adapter.request_adapter_info() + print(plot.renderer.device.adapter.summary) diff --git a/fastplotlib/__init__.py b/fastplotlib/__init__.py index 27545f0ad..33db8c79d 100644 --- a/fastplotlib/__init__.py +++ b/fastplotlib/__init__.py @@ -5,21 +5,20 @@ from .graphics.selectors import * from .legends import * from .widgets import ImageWidget -from .utils import _notebook_print_banner, config +from .utils import config +from .utils.gui import run -from wgpu.gui.auto import run -from wgpu.backends.wgpu_native import enumerate_adapters +import wgpu with open(Path(__file__).parent.joinpath("VERSION"), "r") as f: __version__ = f.read().split("\n")[0] -adapters = [a.request_adapter_info() for a in enumerate_adapters()] +adapters = [a.summary for a in wgpu.gpu.enumerate_adapters()] if len(adapters) < 1: raise IndexError("No WGPU adapters found, fastplotlib will not work.") -_notebook_print_banner() __all__ = [ "Plot", diff --git a/fastplotlib/graphics/selectors/_linear.py b/fastplotlib/graphics/selectors/_linear.py index ff617c5e3..886ccbaaf 100644 --- a/fastplotlib/graphics/selectors/_linear.py +++ b/fastplotlib/graphics/selectors/_linear.py @@ -3,21 +3,19 @@ from numbers import Real import numpy as np - import pygfx -try: - import ipywidgets - - HAS_IPYWIDGETS = True -except (ImportError, ModuleNotFoundError): - HAS_IPYWIDGETS = False - +from ...utils.gui import IS_JUPYTER from .._base import Graphic, GraphicCollection from .._features._selection_features import LinearSelectionFeature from ._base_selector import BaseSelector +if IS_JUPYTER: + # If using the jupyter backend, user has jupyter_rfb, and thus also ipywidgets + import ipywidgets + + class LinearSelector(BaseSelector): @property def limits(self) -> Tuple[float, float]: @@ -240,7 +238,7 @@ def make_ipywidget_slider(self, kind: str = "IntSlider", **kwargs): """ - if not HAS_IPYWIDGETS: + if not IS_JUPYTER: raise ImportError( "Must installed `ipywidgets` to use `make_ipywidget_slider()`" ) diff --git a/fastplotlib/graphics/selectors/_linear_region.py b/fastplotlib/graphics/selectors/_linear_region.py index 4ffbd2cc2..b88174ddb 100644 --- a/fastplotlib/graphics/selectors/_linear_region.py +++ b/fastplotlib/graphics/selectors/_linear_region.py @@ -1,20 +1,18 @@ from typing import * from numbers import Real -try: - import ipywidgets - - HAS_IPYWIDGETS = True -except (ImportError, ModuleNotFoundError): - HAS_IPYWIDGETS = False - import numpy as np - import pygfx +from ...utils.gui import IS_JUPYTER from .._base import Graphic, GraphicCollection -from ._base_selector import BaseSelector from .._features._selection_features import LinearRegionSelectionFeature +from ._base_selector import BaseSelector + + +if IS_JUPYTER: + # If using the jupyter backend, user has jupyter_rfb, and thus also ipywidgets + import ipywidgets class LinearRegionSelector(BaseSelector): @@ -390,7 +388,7 @@ def make_ipywidget_slider(self, kind: str = "IntRangeSlider", **kwargs): """ - if not HAS_IPYWIDGETS: + if not IS_JUPYTER: raise ImportError( "Must installed `ipywidgets` to use `make_ipywidget_slider()`" ) diff --git a/fastplotlib/layouts/_frame/_frame.py b/fastplotlib/layouts/_frame/_frame.py index 2b76b8124..219a59082 100644 --- a/fastplotlib/layouts/_frame/_frame.py +++ b/fastplotlib/layouts/_frame/_frame.py @@ -1,41 +1,7 @@ import os -from ._toolbar import ToolBar - from ...graphics import ImageGraphic - -from .._utils import CANVAS_OPTIONS_AVAILABLE - - -class UnavailableOutputContext: - # called when a requested output context is not available - # ex: if trying to force jupyter_rfb canvas but jupyter_rfb is not installed - def __init__(self, context_name, msg): - self.context_name = context_name - self.msg = msg - - def __call__(self, *args, **kwargs): - raise ModuleNotFoundError( - f"The following output context is not available: {self.context_name}\n{self.msg}" - ) - - -# TODO: potentially put all output context and toolbars in their own module and have this determination done at import -if CANVAS_OPTIONS_AVAILABLE["jupyter"]: - from ._jupyter_output import JupyterOutputContext -else: - JupyterOutputContext = UnavailableOutputContext( - "Jupyter", - "You must install fastplotlib using the `'notebook'` option to use this context:\n" - 'pip install "fastplotlib[notebook]"', - ) - -if CANVAS_OPTIONS_AVAILABLE["qt"]: - from ._qt_output import QOutputContext -else: - QtOutput = UnavailableOutputContext( - "Qt", "You must install `PyQt6` to use this output context" - ) +from ._toolbar import ToolBar class Frame: @@ -158,6 +124,8 @@ def show( # return the appropriate OutputContext based on the current canvas if self.canvas.__class__.__name__ == "JupyterWgpuCanvas": + from ._jupyter_output import JupyterOutputContext # noqa - inline import + self._output = JupyterOutputContext( frame=self, make_toolbar=toolbar, @@ -167,6 +135,8 @@ def show( ) elif self.canvas.__class__.__name__ == "QWgpuCanvas": + from ._qt_output import QOutputContext # noqa - inline import + self._output = QOutputContext( frame=self, make_toolbar=toolbar, add_widgets=add_widgets ) diff --git a/fastplotlib/layouts/_frame/_qt_output.py b/fastplotlib/layouts/_frame/_qt_output.py index e8be2d050..d7e7f2612 100644 --- a/fastplotlib/layouts/_frame/_qt_output.py +++ b/fastplotlib/layouts/_frame/_qt_output.py @@ -1,5 +1,4 @@ -from PyQt6 import QtWidgets - +from ...utils.gui import QtWidgets from ._qt_toolbar import QToolbar diff --git a/fastplotlib/layouts/_frame/_qt_toolbar.py b/fastplotlib/layouts/_frame/_qt_toolbar.py index 4ee073701..d62994c2d 100644 --- a/fastplotlib/layouts/_frame/_qt_toolbar.py +++ b/fastplotlib/layouts/_frame/_qt_toolbar.py @@ -4,8 +4,7 @@ import traceback from typing import * -from PyQt6 import QtWidgets, QtCore - +from ...utils.gui import QtCore, QtWidgets from ...graphics.selectors import PolygonSelector from ._toolbar import ToolBar from ._qtoolbar_template import Ui_QToolbar diff --git a/fastplotlib/layouts/_frame/_qtoolbar_template.py b/fastplotlib/layouts/_frame/_qtoolbar_template.py index a8a1c6f86..d2311c595 100644 --- a/fastplotlib/layouts/_frame/_qtoolbar_template.py +++ b/fastplotlib/layouts/_frame/_qtoolbar_template.py @@ -5,8 +5,7 @@ # WARNING: Any manual changes made to this file will be lost when pyuic6 is # run again. Do not edit this file unless you know what you are doing. - -from PyQt6 import QtCore, QtGui, QtWidgets +from ...utils.gui import QtGui, QtCore, QtWidgets class Ui_QToolbar(object): @@ -30,7 +29,7 @@ def setupUi(self, QToolbar): self.maintain_aspect_button = QtWidgets.QPushButton(parent=QToolbar) font = QtGui.QFont() font.setBold(True) - font.setWeight(75) + font.setWeight(QtGui.QFont.Weight.Bold) self.maintain_aspect_button.setFont(font) self.maintain_aspect_button.setCheckable(True) self.maintain_aspect_button.setObjectName("maintain_aspect_button") diff --git a/fastplotlib/layouts/_gridplot.py b/fastplotlib/layouts/_gridplot.py index 04046cd01..fa987b661 100644 --- a/fastplotlib/layouts/_gridplot.py +++ b/fastplotlib/layouts/_gridplot.py @@ -6,7 +6,7 @@ import pygfx -from wgpu.gui.auto import WgpuCanvas +from wgpu.gui import WgpuCanvasBase from ._frame import Frame from ._utils import make_canvas_and_renderer, create_controller, create_camera @@ -22,7 +22,7 @@ def __init__( cameras: Union[str, list, np.ndarray] = "2d", controller_types: Union[str, list, np.ndarray] = None, controller_ids: Union[str, list, np.ndarray] = None, - canvas: Union[str, WgpuCanvas, pygfx.Texture] = None, + canvas: Union[str, WgpuCanvasBase, pygfx.Texture] = None, renderer: pygfx.WgpuRenderer = None, size: Tuple[int, int] = (500, 300), names: Union[list, np.ndarray] = None, @@ -219,12 +219,6 @@ def __init__( for cam in cams[1:]: _controller.add_camera(cam) - if canvas is None: - canvas = WgpuCanvas() - - if renderer is None: - renderer = pygfx.renderers.WgpuRenderer(canvas) - self._canvas = canvas self._renderer = renderer @@ -266,7 +260,7 @@ def __init__( Frame.__init__(self) @property - def canvas(self) -> WgpuCanvas: + def canvas(self) -> WgpuCanvasBase: """The canvas associated to this GridPlot""" return self._canvas diff --git a/fastplotlib/layouts/_plot.py b/fastplotlib/layouts/_plot.py index 77aba0adc..4656649e6 100644 --- a/fastplotlib/layouts/_plot.py +++ b/fastplotlib/layouts/_plot.py @@ -1,7 +1,7 @@ from typing import * import pygfx -from wgpu.gui.auto import WgpuCanvas +from wgpu.gui import WgpuCanvasBase from ._subplot import Subplot from ._frame import Frame @@ -11,7 +11,7 @@ class Plot(Subplot, Frame, RecordMixin): def __init__( self, - canvas: Union[str, WgpuCanvas] = None, + canvas: Union[str, WgpuCanvasBase] = None, renderer: pygfx.WgpuRenderer = None, camera: Union[str, pygfx.PerspectiveCamera] = "2d", controller: Union[str, pygfx.Controller] = None, diff --git a/fastplotlib/layouts/_plot_area.py b/fastplotlib/layouts/_plot_area.py index 08a09baa7..2c93d7e9e 100644 --- a/fastplotlib/layouts/_plot_area.py +++ b/fastplotlib/layouts/_plot_area.py @@ -7,7 +7,7 @@ import pygfx from pylinalg import vec_transform, vec_unproject -from wgpu.gui.auto import WgpuCanvas +from wgpu.gui import WgpuCanvasBase from ._utils import create_camera, create_controller from ..graphics._base import Graphic @@ -29,7 +29,7 @@ def __init__( camera: Union[pygfx.PerspectiveCamera], controller: Union[pygfx.Controller], scene: pygfx.Scene, - canvas: WgpuCanvas, + canvas: WgpuCanvasBase, renderer: pygfx.WgpuRenderer, name: str = None, ): @@ -122,7 +122,7 @@ def scene(self) -> pygfx.Scene: return self._scene @property - def canvas(self) -> WgpuCanvas: + def canvas(self) -> WgpuCanvasBase: """Canvas associated to the plot area""" return self._canvas diff --git a/fastplotlib/layouts/_subplot.py b/fastplotlib/layouts/_subplot.py index 6fa5f890e..509840fa7 100644 --- a/fastplotlib/layouts/_subplot.py +++ b/fastplotlib/layouts/_subplot.py @@ -4,7 +4,7 @@ import pygfx -from wgpu.gui.auto import WgpuCanvas +from wgpu.gui import WgpuCanvasBase from ..graphics import TextGraphic from ._utils import make_canvas_and_renderer, create_camera, create_controller @@ -20,7 +20,7 @@ def __init__( parent_dims: Tuple[int, int] = None, camera: Union[str, pygfx.PerspectiveCamera] = "2d", controller: Union[str, pygfx.Controller] = None, - canvas: Union[str, WgpuCanvas, pygfx.Texture] = None, + canvas: Union[str, WgpuCanvasBase, pygfx.Texture] = None, renderer: pygfx.WgpuRenderer = None, name: str = None, ): diff --git a/fastplotlib/layouts/_utils.py b/fastplotlib/layouts/_utils.py index 1662f00c5..5ee930b67 100644 --- a/fastplotlib/layouts/_utils.py +++ b/fastplotlib/layouts/_utils.py @@ -1,64 +1,15 @@ from typing import * +import importlib import pygfx from pygfx import WgpuRenderer, Texture +from wgpu.gui import WgpuCanvasBase -# default auto-determined canvas -from wgpu.gui.auto import WgpuCanvas -from wgpu.gui.base import WgpuCanvasBase - - -# TODO: this determination can be better -try: - from wgpu.gui.jupyter import JupyterWgpuCanvas -except ImportError: - JupyterWgpuCanvas = False - -try: - import PyQt6 - from wgpu.gui.qt import QWgpuCanvas -except ImportError: - QWgpuCanvas = False - -try: - from wgpu.gui.glfw import GlfwWgpuCanvas -except ImportError: - GlfwWgpuCanvas = False - - -CANVAS_OPTIONS = ["jupyter", "glfw", "qt"] -CANVAS_OPTIONS_AVAILABLE = { - "jupyter": JupyterWgpuCanvas, - "glfw": GlfwWgpuCanvas, - "qt": QWgpuCanvas, -} - - -def auto_determine_canvas(): - try: - ip = get_ipython() - if ip.has_trait("kernel"): - if hasattr(ip.kernel, "app"): - if ip.kernel.app.__class__.__name__ == "QApplication": - return QWgpuCanvas - else: - return JupyterWgpuCanvas - except NameError: - pass - - else: - if CANVAS_OPTIONS_AVAILABLE["qt"]: - return QWgpuCanvas - elif CANVAS_OPTIONS_AVAILABLE["glfw"]: - return GlfwWgpuCanvas - - # We go with the wgpu auto guess - # for example, offscreen canvas etc. - return WgpuCanvas +from ..utils import gui def make_canvas_and_renderer( - canvas: Union[str, WgpuCanvas, Texture, None], renderer: [WgpuRenderer, None] + canvas: Union[str, WgpuCanvasBase, Texture, None], renderer: [WgpuRenderer, None] ): """ Parses arguments and returns the appropriate canvas and renderer instances @@ -66,23 +17,14 @@ def make_canvas_and_renderer( """ if canvas is None: - Canvas = auto_determine_canvas() - canvas = Canvas(max_fps=60) - + canvas = gui.WgpuCanvas(max_fps=60) elif isinstance(canvas, str): - if canvas not in CANVAS_OPTIONS: - raise ValueError(f"str canvas argument must be one of: {CANVAS_OPTIONS}") - elif not CANVAS_OPTIONS_AVAILABLE[canvas]: - raise ImportError( - f"The {canvas} framework is not installed for using this canvas" - ) - else: - canvas = CANVAS_OPTIONS_AVAILABLE[canvas](max_fps=60) - + m = importlib.import_module("wgpu.gui." + canvas) + canvas = m.WgpuCanvas(max_fps=60) elif not isinstance(canvas, (WgpuCanvasBase, Texture)): raise ValueError( f"canvas option must either be a valid WgpuCanvas implementation, a pygfx Texture" - f" or a str from the following options: {CANVAS_OPTIONS}" + f" or a str with the wgpu gui backend name." ) if renderer is None: diff --git a/fastplotlib/utils/__init__.py b/fastplotlib/utils/__init__.py index 305af90a8..6759b2497 100644 --- a/fastplotlib/utils/__init__.py +++ b/fastplotlib/utils/__init__.py @@ -2,7 +2,6 @@ from .functions import * -from ._gpu_info import _notebook_print_banner @dataclass diff --git a/fastplotlib/utils/_gpu_info.py b/fastplotlib/utils/_gpu_info.py deleted file mode 100644 index 93e95d281..000000000 --- a/fastplotlib/utils/_gpu_info.py +++ /dev/null @@ -1,66 +0,0 @@ -from pathlib import Path - -from wgpu.backends.wgpu_native import enumerate_adapters -from wgpu.utils import get_default_device - -try: - ip = get_ipython() - from ipywidgets import Image - from wgpu.gui.jupyter import JupyterWgpuCanvas -except (NameError, ModuleNotFoundError, ImportError): - NOTEBOOK = False -else: - from IPython.display import display - - if ip.has_trait("kernel") and (JupyterWgpuCanvas is not False): - NOTEBOOK = True - else: - NOTEBOOK = False - - -def _notebook_print_banner(): - if NOTEBOOK is False: - return - - logo_path = Path(__file__).parent.parent.joinpath( - "assets", "fastplotlib_face_logo.png" - ) - - with open(logo_path, "rb") as f: - logo_data = f.read() - - image = Image(value=logo_data, format="png", width=300, height=55) - - display(image) - - # print logo and adapter info - adapters = [a for a in enumerate_adapters()] - adapters_info = [a.request_adapter_info() for a in adapters] - - ix_default = adapters_info.index( - get_default_device().adapter.request_adapter_info() - ) - - if len(adapters) > 0: - print("Available devices:") - - for ix, adapter in enumerate(adapters_info): - atype = adapter["adapter_type"] - backend = adapter["backend_type"] - driver = adapter["description"] - device = adapter["device"] - - if atype == "DiscreteGPU" and backend != "OpenGL": - charactor = chr(0x2705) - elif atype == "IntegratedGPU" and backend != "OpenGL": - charactor = chr(0x0001FBC4) - else: - charactor = chr(0x2757) - - if ix == ix_default: - default = " (default) " - else: - default = " " - - output_str = f"{charactor}{default}| {device} | {atype} | {backend} | {driver}" - print(output_str) diff --git a/fastplotlib/utils/gui.py b/fastplotlib/utils/gui.py new file mode 100644 index 000000000..b59c7799b --- /dev/null +++ b/fastplotlib/utils/gui.py @@ -0,0 +1,108 @@ +import sys +import importlib +from pathlib import Path + +import wgpu + + +# --- Prepare + + +# Ultimately, we let wgpu-py decide, but we can prime things a bit to create our +# own preferred order, by importing a Qt lib. But we only do this if no GUI has +# been imported yet. + +# Qt libs that we will try to import +qt_libs = ["PySide6", "PyQt6", "PySide2", "PyQt5"] + +# Other known libs that, if imported, we should probably not try to force qt +other_libs = ["glfw", "wx", "ipykernel"] + +already_imported = [name for name in (qt_libs + other_libs) if name in sys.modules] +if not already_imported: + for name in qt_libs: + try: + importlib.import_module(name) + except Exception: + pass + else: + break + + +# --- Triage + + +# Let wgpu do the auto gui selection +from wgpu.gui.auto import WgpuCanvas, run + +# Get the name of the backend ('qt', 'glfw', 'jupyter') +GUI_BACKEND = WgpuCanvas.__module__.split(".")[-1] +IS_JUPYTER = GUI_BACKEND == "jupyter" + + +# --- Some backend-specific preparations + + +def _notebook_print_banner(): + + from ipywidgets import Image + from IPython.display import display + + logo_path = Path(__file__).parent.parent.joinpath( + "assets", "fastplotlib_face_logo.png" + ) + + with open(logo_path, "rb") as f: + logo_data = f.read() + + image = Image(value=logo_data, format="png", width=300, height=55) + + display(image) + + # print logo and adapter info + adapters = [a for a in wgpu.gpu.enumerate_adapters()] + adapters_info = [a.request_adapter_info() for a in adapters] + + default_adapter_info = wgpu.gpu.request_adapter().request_adapter_info() + default_ix = adapters_info.index(default_adapter_info) + + if len(adapters) > 0: + print("Available devices:") + + for ix, adapter in enumerate(adapters_info): + atype = adapter["adapter_type"] + backend = adapter["backend_type"] + driver = adapter["description"] + device = adapter["device"] + + if atype == "DiscreteGPU" and backend != "OpenGL": + charactor = chr(0x2705) + elif atype == "IntegratedGPU" and backend != "OpenGL": + charactor = chr(0x0001FBC4) + else: + charactor = chr(0x2757) + + if ix == default_ix: + default = " (default) " + else: + default = " " + + output_str = f"{charactor}{default}| {device} | {atype} | {backend} | {driver}" + print(output_str) + + +if GUI_BACKEND == "jupyter": + _notebook_print_banner() + +elif GUI_BACKEND == "qt": + from wgpu.gui.qt import get_app, libname + + # create and store ref to qt app + _qt_app = get_app() + + # Import submodules of PySide6/PyQt6/PySid2/PyQt5 + # For the way that fpl uses Qt, the supported Qt libs seems compatible enough. + # If necessary we can do some qtpy-like monkey-patching here. + QtCore = importlib.import_module(".QtCore", libname) + QtGui = importlib.import_module(".QtGui", libname) + QtWidgets = importlib.import_module(".QtWidgets", libname) diff --git a/fastplotlib/widgets/image.py b/fastplotlib/widgets/image.py index 0da1bb520..9412f7cc5 100644 --- a/fastplotlib/widgets/image.py +++ b/fastplotlib/widgets/image.py @@ -8,14 +8,6 @@ from ..graphics import ImageGraphic from ..utils import calculate_gridshape from .histogram_lut import HistogramLUT -from ..layouts._utils import CANVAS_OPTIONS_AVAILABLE - - -if CANVAS_OPTIONS_AVAILABLE["jupyter"]: - from ..layouts._frame._ipywidget_toolbar import IpywidgetImageWidgetToolbar - -if CANVAS_OPTIONS_AVAILABLE["qt"]: - from ..layouts._frame._qt_toolbar import QToolbarImageWidget DEFAULT_DIMS_ORDER = { @@ -927,9 +919,17 @@ def show( ImageWidget just uses the Gridplot output context """ if self.gridplot.canvas.__class__.__name__ == "JupyterWgpuCanvas": + from ..layouts._frame._ipywidget_toolbar import ( + IpywidgetImageWidgetToolbar, + ) # noqa - inline import + self._image_widget_toolbar = IpywidgetImageWidgetToolbar(self) elif self.gridplot.canvas.__class__.__name__ == "QWgpuCanvas": + from ..layouts._frame._qt_toolbar import ( + QToolbarImageWidget, + ) # noqa - inline import + self._image_widget_toolbar = QToolbarImageWidget(self) self._output = self.gridplot.show( diff --git a/setup.py b/setup.py index a06a879a5..e8f2613d9 100644 --- a/setup.py +++ b/setup.py @@ -4,6 +4,7 @@ install_requires = [ "numpy>=1.23.0", + "wgpu>=0.15.1", "pygfx>=0.1.14", ] From 9a1bfbee6ceda2e8b0a7cdc9210ef1450201ba0f Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Sun, 31 Mar 2024 00:52:54 -0400 Subject: [PATCH 055/185] simplify screenshot testing (#447) * rgb and rmse funcs work * increase screenshot rmse threshold to 0.025 * drop py3.9, add mac actions * workflow name * skip mac CI for now * just use numpy to convert RGBA -> RGB * rgb for nb tests * forgot to remove rgba screenshot saving for nb * forgot to add rmse to nb test utils * more tweaks * allow setting rmse tolerance for nb tests * use slightly higher tolerance for iw nb tests * proper way to set tolerance for nb --- .github/workflows/ci.yml | 92 ++++++++- .github/workflows/screenshots.yml | 2 +- examples/desktop/screenshots/gridplot.png | 4 +- .../screenshots/gridplot_non_square.png | 4 +- examples/desktop/screenshots/heatmap.png | 4 +- examples/desktop/screenshots/heatmap_cmap.png | 4 +- examples/desktop/screenshots/heatmap_data.png | 4 +- .../desktop/screenshots/heatmap_vmin_vmax.png | 4 +- examples/desktop/screenshots/image_cmap.png | 4 +- examples/desktop/screenshots/image_rgb.png | 4 +- .../desktop/screenshots/image_rgbvminvmax.png | 4 +- examples/desktop/screenshots/image_simple.png | 4 +- .../desktop/screenshots/image_vminvmax.png | 4 +- examples/desktop/screenshots/line.png | 4 +- examples/desktop/screenshots/line_cmap.png | 4 +- .../desktop/screenshots/line_collection.png | 4 +- .../line_collection_cmap_values.png | 4 +- ...ine_collection_cmap_values_qualitative.png | 4 +- .../screenshots/line_collection_colors.png | 4 +- .../desktop/screenshots/line_colorslice.png | 4 +- .../desktop/screenshots/line_dataslice.png | 4 +- .../screenshots/line_present_scaling.png | 4 +- examples/desktop/screenshots/line_stack.png | 4 +- examples/desktop/screenshots/scatter.png | 4 +- examples/desktop/screenshots/scatter_cmap.png | 4 +- .../screenshots/scatter_colorslice.png | 4 +- .../desktop/screenshots/scatter_dataslice.png | 4 +- .../desktop/screenshots/scatter_present.png | 4 +- examples/desktop/screenshots/scatter_size.png | 4 +- examples/notebooks/image_widget_test.ipynb | 25 ++- examples/notebooks/nb_test_utils.py | 86 +++++++- .../notebooks/screenshots/nb-astronaut.png | 4 +- .../screenshots/nb-astronaut_RGB.png | 4 +- examples/notebooks/screenshots/nb-camera.png | 4 +- .../nb-image-widget-movie-set_data.png | 4 +- .../nb-image-widget-movie-single-0-reset.png | 4 +- .../nb-image-widget-movie-single-0.png | 4 +- .../nb-image-widget-movie-single-279.png | 4 +- ...e-widget-movie-single-50-window-max-33.png | 4 +- ...-widget-movie-single-50-window-mean-13.png | 4 +- ...-widget-movie-single-50-window-mean-33.png | 4 +- ...ge-widget-movie-single-50-window-reset.png | 4 +- .../nb-image-widget-movie-single-50.png | 4 +- .../nb-image-widget-single-gnuplot2.png | 4 +- .../screenshots/nb-image-widget-single.png | 4 +- ...et-zfish-frame-50-frame-apply-gaussian.png | 4 +- ...idget-zfish-frame-50-frame-apply-reset.png | 4 +- ...ge-widget-zfish-frame-50-max-window-13.png | 4 +- ...e-widget-zfish-frame-50-mean-window-13.png | 4 +- ...ge-widget-zfish-frame-50-mean-window-5.png | 4 +- .../nb-image-widget-zfish-frame-50.png | 4 +- .../nb-image-widget-zfish-frame-99.png | 4 +- ...ish-grid-frame-50-frame-apply-gaussian.png | 4 +- ...-zfish-grid-frame-50-frame-apply-reset.png | 4 +- ...dget-zfish-grid-frame-50-max-window-13.png | 4 +- ...get-zfish-grid-frame-50-mean-window-13.png | 4 +- ...dget-zfish-grid-frame-50-mean-window-5.png | 4 +- .../nb-image-widget-zfish-grid-frame-50.png | 4 +- .../nb-image-widget-zfish-grid-frame-99.png | 4 +- ...e-widget-zfish-grid-init-mean-window-5.png | 4 +- ...fish-grid-set_data-reset-indices-false.png | 4 +- ...zfish-grid-set_data-reset-indices-true.png | 4 +- ...-image-widget-zfish-init-mean-window-5.png | 4 +- .../notebooks/screenshots/nb-imagewidget.png | 3 + .../notebooks/screenshots/nb-lines-3d.png | 4 +- .../nb-lines-cmap-jet-values-cosine.png | 4 +- .../screenshots/nb-lines-cmap-jet-values.png | 4 +- .../screenshots/nb-lines-cmap-jet.png | 4 +- .../screenshots/nb-lines-cmap-tab-10.png | 4 +- .../nb-lines-cmap-viridis-values.png | 4 +- .../screenshots/nb-lines-cmap-viridis.png | 4 +- .../screenshots/nb-lines-cmap-white.png | 4 +- .../notebooks/screenshots/nb-lines-colors.png | 4 +- .../notebooks/screenshots/nb-lines-data.png | 4 +- .../screenshots/nb-lines-underlay.png | 4 +- examples/notebooks/screenshots/nb-lines.png | 4 +- examples/tests/test_examples.py | 47 ++++- examples/tests/testutils.py | 188 ++++++++++++++++++ 78 files changed, 552 insertions(+), 175 deletions(-) create mode 100644 examples/notebooks/screenshots/nb-imagewidget.png diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8f92bafb8..53e88bdf8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,15 +46,13 @@ jobs: make html SPHINXOPTS="-W --keep-going" test-build-full: - name: Test examples, env with notebook and glfw + name: Test Linux, notebook + glfw runs-on: bigmem if: ${{ !github.event.pull_request.draft }} strategy: fail-fast: false matrix: include: - - name: Test py39 - pyversion: '3.9' - name: Test py310 pyversion: '3.10' - name: Test py311 @@ -103,15 +101,13 @@ jobs: examples/notebooks/diffs test-build-desktop: - name: Test examples, env with only glfw + name: Test Linux, only glfw runs-on: bigmem if: ${{ !github.event.pull_request.draft }} strategy: fail-fast: false matrix: include: - - name: Test py39 - pyversion: '3.9' - name: Test py310 pyversion: '3.10' - name: Test py311 @@ -156,3 +152,87 @@ jobs: name: screenshot-diffs path: | examples/desktop/diffs + +# test-build-full-mac: +# name: Test Mac, notebook + glfw +# runs-on: macos-14 +# if: ${{ !github.event.pull_request.draft }} +# strategy: +# fail-fast: false +# matrix: +# include: +# - name: Test py310 +# pyversion: '3.10' +# - name: Test py311 +# pyversion: '3.11' +# - name: Test py312 +# pyversion: '3.12' +# steps: +# - uses: actions/checkout@v3 +# with: +# lfs: true +# - name: Set up Python +# uses: actions/setup-python@v3 +# with: +# python-version: ${{ matrix.pyversion }} +# - name: Install dev dependencies +# run: | +# python -m pip install --upgrade pip setuptools +# # remove pygfx from install_requires, we install using pygfx@main +# pip install -e ".["tests"]" +# pip install git+https://github.com/pygfx/pygfx.git@main +# - name: Show wgpu backend +# run: +# python -c "from examples.tests.testutils import wgpu_backend; print(wgpu_backend)" +# - name: Test examples +# run: | +# pytest -v examples +# pytest --nbmake examples/notebooks/ +# - uses: actions/upload-artifact@v3 +# if: ${{ failure() }} +# with: +# name: screenshot-diffs +# path: | +# examples/desktop/diffs +# examples/notebooks/diffs +# +# test-build-glfw-mac: +# name: Test Mac, glfw +# runs-on: macos-14 +# if: ${{ !github.event.pull_request.draft }} +# strategy: +# fail-fast: false +# matrix: +# include: +# - name: Test py310 +# pyversion: '3.10' +# - name: Test py311 +# pyversion: '3.11' +# - name: Test py312 +# pyversion: '3.12' +# steps: +# - uses: actions/checkout@v3 +# with: +# lfs: true +# - name: Set up Python +# uses: actions/setup-python@v3 +# with: +# python-version: ${{ matrix.pyversion }} +# - name: Install dev dependencies +# run: | +# python -m pip install --upgrade pip setuptools +# # remove pygfx from install_requires, we install using pygfx@main +# pip install -e ".["tests-desktop"]" +# pip install git+https://github.com/pygfx/pygfx.git@main +# - name: Show wgpu backend +# run: +# python -c "from examples.tests.testutils import wgpu_backend; print(wgpu_backend)" +# - name: Test examples +# run: | +# pytest -v examples +# - uses: actions/upload-artifact@v3 +# if: ${{ failure() }} +# with: +# name: screenshot-diffs +# path: | +# examples/desktop/diffs diff --git a/.github/workflows/screenshots.yml b/.github/workflows/screenshots.yml index d3cdb919b..baad8b655 100644 --- a/.github/workflows/screenshots.yml +++ b/.github/workflows/screenshots.yml @@ -48,7 +48,7 @@ jobs: run: | # regenerate screenshots REGENERATE_SCREENSHOTS=1 pytest -v examples - REGENERATE_SCREENSHOTS=1 pytest --nbmake examples/notebooks/ + FASTPLOTLIB_NB_TESTS=1 REGENERATE_SCREENSHOTS=1 pytest --nbmake examples/notebooks/ - uses: actions/upload-artifact@v3 if: always() with: diff --git a/examples/desktop/screenshots/gridplot.png b/examples/desktop/screenshots/gridplot.png index bc35ccf8c..ebf2d3a97 100644 --- a/examples/desktop/screenshots/gridplot.png +++ b/examples/desktop/screenshots/gridplot.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e416fc968edd3788513e369f4d265b0abd7216a7ef19ec8b84659c30ca7c8ca1 -size 307384 +oid sha256:f972f67b8830657ab14899f749fb385a080280304377d8868e6cd39c766a0afd +size 267084 diff --git a/examples/desktop/screenshots/gridplot_non_square.png b/examples/desktop/screenshots/gridplot_non_square.png index 82b2b0eb4..bc642b729 100644 --- a/examples/desktop/screenshots/gridplot_non_square.png +++ b/examples/desktop/screenshots/gridplot_non_square.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ab5598b67b80efce0d2559e400e77098e734c91608a3f49b691ddaa030d47edb -size 203434 +oid sha256:352bf94c68444a330b000d7b6b3ec51b5b694ff3a0ce810299b325315923d9af +size 175938 diff --git a/examples/desktop/screenshots/heatmap.png b/examples/desktop/screenshots/heatmap.png index a0655cf3a..a8c8b73fe 100644 --- a/examples/desktop/screenshots/heatmap.png +++ b/examples/desktop/screenshots/heatmap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6872c3cc3e35ec918b054fb2d76525bbd3d82d8b49916aca1046aa1be65ff923 -size 111825 +oid sha256:5620e4dcb964dbf3318ac77e566af395a35b9762e0687dec2e1a2864eb291fd3 +size 102994 diff --git a/examples/desktop/screenshots/heatmap_cmap.png b/examples/desktop/screenshots/heatmap_cmap.png index 2eb769c14..cee81dd30 100644 --- a/examples/desktop/screenshots/heatmap_cmap.png +++ b/examples/desktop/screenshots/heatmap_cmap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f2eba96c2bfb1d07365810a69e99c79b068741f5dcf74fc745c13d5ff21f16f2 -size 106671 +oid sha256:8863461569f5b89d1443e3051a5512f3987487fcb9e057215d2f030a180fa09f +size 97996 diff --git a/examples/desktop/screenshots/heatmap_data.png b/examples/desktop/screenshots/heatmap_data.png index 50a8ae79e..316a73753 100644 --- a/examples/desktop/screenshots/heatmap_data.png +++ b/examples/desktop/screenshots/heatmap_data.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f0576063658e05e19b7723b4c88dc4d55a8178b090b4a88e33251fc92408b4a1 -size 18051 +oid sha256:a975179e82893dbb04e4674310761e7b02bb62ae6abb1b89397720bddf96ae5f +size 19084 diff --git a/examples/desktop/screenshots/heatmap_vmin_vmax.png b/examples/desktop/screenshots/heatmap_vmin_vmax.png index f10382e87..357683d82 100644 --- a/examples/desktop/screenshots/heatmap_vmin_vmax.png +++ b/examples/desktop/screenshots/heatmap_vmin_vmax.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:639d50f2f5fb07dba461e7a38de3886092f8754277eadbb5e305e32023289abd -size 124403 +oid sha256:9592f3724016db1b7431bc100b16bec175e197c111e7b442dc2255d51da3f5e8 +size 114957 diff --git a/examples/desktop/screenshots/image_cmap.png b/examples/desktop/screenshots/image_cmap.png index bed07a41a..bbf51ab18 100644 --- a/examples/desktop/screenshots/image_cmap.png +++ b/examples/desktop/screenshots/image_cmap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e1d78cc0681079a5c43d9fdb4142f5fee75d477d9f9a1469fca8bc8933c244fc -size 216210 +oid sha256:555fd969606d0cb231ac152724f7c9717a2220ce22db663c5e7d5793f828ed34 +size 189654 diff --git a/examples/desktop/screenshots/image_rgb.png b/examples/desktop/screenshots/image_rgb.png index a21c0658b..9a5082b12 100644 --- a/examples/desktop/screenshots/image_rgb.png +++ b/examples/desktop/screenshots/image_rgb.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:820a73b9b2e5bbaed84fb11438e2c5672b76c8b57a33823f4840a71be03d7dd1 -size 251438 +oid sha256:95f3cae6caf8d64d1a6b4799df52dc61cc05bd6b6ea465edbec06a9678f32435 +size 218089 diff --git a/examples/desktop/screenshots/image_rgbvminvmax.png b/examples/desktop/screenshots/image_rgbvminvmax.png index 88acfadc5..00bbdc0c5 100644 --- a/examples/desktop/screenshots/image_rgbvminvmax.png +++ b/examples/desktop/screenshots/image_rgbvminvmax.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5f2f562573f8104342ae01b5852f71c960341bdd70ae0bc7967df663166edbd3 -size 39604 +oid sha256:4fc06b8cdd72040cf2ffc44cde80d5ae21ca392daac25d79fe175b5865b13552 +size 34894 diff --git a/examples/desktop/screenshots/image_simple.png b/examples/desktop/screenshots/image_simple.png index 098d5a055..94fcd3061 100644 --- a/examples/desktop/screenshots/image_simple.png +++ b/examples/desktop/screenshots/image_simple.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a4ed42d042d8bb7e35f31b5ad0a3e3a495cf9c3164516eb457d8b41d7fae6bab -size 213075 +oid sha256:3dcfb5d48d0e4db920c33ee725e2c66f3c8e04a66e03d283a6481f42a4121a16 +size 190178 diff --git a/examples/desktop/screenshots/image_vminvmax.png b/examples/desktop/screenshots/image_vminvmax.png index 88acfadc5..00bbdc0c5 100644 --- a/examples/desktop/screenshots/image_vminvmax.png +++ b/examples/desktop/screenshots/image_vminvmax.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5f2f562573f8104342ae01b5852f71c960341bdd70ae0bc7967df663166edbd3 -size 39604 +oid sha256:4fc06b8cdd72040cf2ffc44cde80d5ae21ca392daac25d79fe175b5865b13552 +size 34894 diff --git a/examples/desktop/screenshots/line.png b/examples/desktop/screenshots/line.png index 8e3e6ae64..74cbae39a 100644 --- a/examples/desktop/screenshots/line.png +++ b/examples/desktop/screenshots/line.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:81038ebca5d41f22f5dde3fd152d94760ef51cc055ff248be18298bc7537b569 -size 44312 +oid sha256:c8b4f4a08d1791b80d226c8c3099e37d33d8cdd7a400e4f85fb7072ee2aa3c2e +size 29121 diff --git a/examples/desktop/screenshots/line_cmap.png b/examples/desktop/screenshots/line_cmap.png index b96c9a1dd..9cd93f05d 100644 --- a/examples/desktop/screenshots/line_cmap.png +++ b/examples/desktop/screenshots/line_cmap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e7a9cf65bbd19290ed96e418930e896fd0ec463dc2c6797f8b407d56e9e4444d -size 43730 +oid sha256:d60b4ff117298f973be892773dbfc620ac855c35ca7dea42437e20bf7fcef804 +size 31050 diff --git a/examples/desktop/screenshots/line_collection.png b/examples/desktop/screenshots/line_collection.png index 89d613c2c..bcfe85309 100644 --- a/examples/desktop/screenshots/line_collection.png +++ b/examples/desktop/screenshots/line_collection.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:09e8c2be9815edf5c29a98dc61758fac3aeb2603c8547aa8c3c05b01538886e3 -size 147244 +oid sha256:7ca99b8d74fdf7f87b0f2fc5637c59c9090b91bef868e85ddd75dbcb1264f699 +size 95146 diff --git a/examples/desktop/screenshots/line_collection_cmap_values.png b/examples/desktop/screenshots/line_collection_cmap_values.png index 38d9adc6e..b7fcdbcae 100644 --- a/examples/desktop/screenshots/line_collection_cmap_values.png +++ b/examples/desktop/screenshots/line_collection_cmap_values.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:98d7468dc1701d3523c779a0e857cf185201766b751731e1db44e3c2bc753335 -size 93839 +oid sha256:68090603856eb5b961092cf2ad2d89a1e9cfd7e31f6d089b3abad101874f65d4 +size 61032 diff --git a/examples/desktop/screenshots/line_collection_cmap_values_qualitative.png b/examples/desktop/screenshots/line_collection_cmap_values_qualitative.png index 4f14e49b9..9f89a24cc 100644 --- a/examples/desktop/screenshots/line_collection_cmap_values_qualitative.png +++ b/examples/desktop/screenshots/line_collection_cmap_values_qualitative.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:31d79a2aeb2e43b569a9045edd21fb59e7bddf2b5cc18133f4b346e0d6be7fd1 -size 95696 +oid sha256:9cff99e5f9faf319909571778631453c043237f5c94eece6680b028a5d7a5ac2 +size 64149 diff --git a/examples/desktop/screenshots/line_collection_colors.png b/examples/desktop/screenshots/line_collection_colors.png index c71c623c6..7bb4152fd 100644 --- a/examples/desktop/screenshots/line_collection_colors.png +++ b/examples/desktop/screenshots/line_collection_colors.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:381fc320bda54f5aa235b4cd0183ff511e3a0484c00ab37f76c89df959d48626 -size 82806 +oid sha256:a4aa17a65806300da65f4bbbfccb970d6a7207c4ca4d48b25615f627630fb484 +size 51174 diff --git a/examples/desktop/screenshots/line_colorslice.png b/examples/desktop/screenshots/line_colorslice.png index 7775c1918..3d04c473f 100644 --- a/examples/desktop/screenshots/line_colorslice.png +++ b/examples/desktop/screenshots/line_colorslice.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3ade0416355a05de01e8f4a55485aacc40bd4c3d57a1a9fcff43317e13a43856 -size 50560 +oid sha256:aa941eaf5b940b4eebab89ed836cbd092e16b4758abafa3722c296db65c0c4b5 +size 33233 diff --git a/examples/desktop/screenshots/line_dataslice.png b/examples/desktop/screenshots/line_dataslice.png index 2907dd64a..0863751bf 100644 --- a/examples/desktop/screenshots/line_dataslice.png +++ b/examples/desktop/screenshots/line_dataslice.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8fc4265c0ac6b4cee0476acbc5362968f60a965a387c7a0a3b66e89e522eb21c -size 69917 +oid sha256:78ccd51d1891fb6a345cb2885a341f276d8ad7a6fa506deda6cae6ef14c64094 +size 45843 diff --git a/examples/desktop/screenshots/line_present_scaling.png b/examples/desktop/screenshots/line_present_scaling.png index b4b883855..ba7142106 100644 --- a/examples/desktop/screenshots/line_present_scaling.png +++ b/examples/desktop/screenshots/line_present_scaling.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f37f2a227136af0cfc112bb1e5c9ba01fb362f33bad0971f5253adb61e89785e -size 30264 +oid sha256:06f7dd45eb495fecfcf46478c6430a658640ceb2855c4797bc184cf4134571e3 +size 20180 diff --git a/examples/desktop/screenshots/line_stack.png b/examples/desktop/screenshots/line_stack.png index 47e59ba8c..c13f05f04 100644 --- a/examples/desktop/screenshots/line_stack.png +++ b/examples/desktop/screenshots/line_stack.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0081d587956056934c4feb0c0f695f69ea19a253243f1af1dc7de80c6406a642 -size 365226 +oid sha256:5480aefe6e723863b919a4eeb4755310fe7036b27beb8e2e2402e04943ee8c1e +size 201102 diff --git a/examples/desktop/screenshots/scatter.png b/examples/desktop/screenshots/scatter.png index d01d36707..94fb858e1 100644 --- a/examples/desktop/screenshots/scatter.png +++ b/examples/desktop/screenshots/scatter.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8d840f02d1c4be5ea11adfc224481a5b8b306cbc904e099af4d3fdd5ab7f383f -size 26683 +oid sha256:4fc16a1ba74a8eca99a2fc7937f8896ca93207b99e231bc4f53845b0d2bdaed7 +size 15283 diff --git a/examples/desktop/screenshots/scatter_cmap.png b/examples/desktop/screenshots/scatter_cmap.png index 7f0bba38a..87a6e0ded 100644 --- a/examples/desktop/screenshots/scatter_cmap.png +++ b/examples/desktop/screenshots/scatter_cmap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e9aba2f37c7682d68569e1bac7afac5f243afb98ab94d9957de4b59f9d3dd1c0 -size 57257 +oid sha256:a02d2b5d4735d656d1b754ac3681a7700d961d7e4a43dfaf3a7dd0d4f6516ba6 +size 37808 diff --git a/examples/desktop/screenshots/scatter_colorslice.png b/examples/desktop/screenshots/scatter_colorslice.png index 27249e63e..cede76dfd 100644 --- a/examples/desktop/screenshots/scatter_colorslice.png +++ b/examples/desktop/screenshots/scatter_colorslice.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:39c49529552d6ace3d67b37f0c660e9734fcb763bdc165484f356ad8cffc908e -size 25218 +oid sha256:f7956e02d6c231bab091adb4ce9102ad4943050ccf171a0594a899a381880771 +size 14712 diff --git a/examples/desktop/screenshots/scatter_dataslice.png b/examples/desktop/screenshots/scatter_dataslice.png index 155510885..7a1429663 100644 --- a/examples/desktop/screenshots/scatter_dataslice.png +++ b/examples/desktop/screenshots/scatter_dataslice.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:20757e215c4c208e08027f8e2b798691f421ce7662dc86be3615dc41084686f3 -size 27392 +oid sha256:0ecc6454dd197e6a3f146d0a04881db91b099673b9d74903536ca103b2418c89 +size 15657 diff --git a/examples/desktop/screenshots/scatter_present.png b/examples/desktop/screenshots/scatter_present.png index 87685fe90..08bc610b3 100644 --- a/examples/desktop/screenshots/scatter_present.png +++ b/examples/desktop/screenshots/scatter_present.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7a8ceb8f3d7203f0569374993784f2229448ace08b0abee94910e6ae71ceca29 -size 24587 +oid sha256:bd072918f21ed0ce4ea4e1f4499ec1ff66d867cfdc0ecd6b3ed8092141cd348e +size 14195 diff --git a/examples/desktop/screenshots/scatter_size.png b/examples/desktop/screenshots/scatter_size.png index afe8a0b35..056d2a531 100644 --- a/examples/desktop/screenshots/scatter_size.png +++ b/examples/desktop/screenshots/scatter_size.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a184ce0a202bc03fa61b3f4149109e44160bfd326c26f46db3b83ce0cf1699a6 -size 67710 +oid sha256:5ccfbac94de6ba122ea420dce58e4a576b2d58d9282aaf8d64de399278df57b3 +size 38076 diff --git a/examples/notebooks/image_widget_test.ipynb b/examples/notebooks/image_widget_test.ipynb index 90747757c..c236ce9b7 100644 --- a/examples/notebooks/image_widget_test.ipynb +++ b/examples/notebooks/image_widget_test.ipynb @@ -23,7 +23,9 @@ "metadata": {}, "outputs": [], "source": [ - "from nb_test_utils import plot_test, notebook_finished " + "from nb_test_utils import plot_test, notebook_finished\n", + "import nb_test_utils\n", + "nb_test_utils.TOLERANCE = 0.035" ] }, { @@ -57,7 +59,8 @@ "source": [ "iw = ImageWidget(\n", " data=a,\n", - " cmap=\"viridis\"\n", + " cmap=\"viridis\",\n", + " grid_plot_kwargs={\"size\": (900, 600)},\n", ")" ] }, @@ -128,7 +131,8 @@ "iw_movie = ImageWidget(\n", " data=gray_movie, \n", " slider_dims=[\"t\"],\n", - " cmap=\"gray\"\n", + " cmap=\"gray\",\n", + " grid_plot_kwargs={\"size\": (900, 600)},\n", ")" ] }, @@ -271,6 +275,9 @@ "execution_count": null, "id": "76535d56-e514-4c16-aa48-a6359f8019d5", "metadata": { + "jupyter": { + "source_hidden": true + }, "tags": [] }, "outputs": [], @@ -280,6 +287,7 @@ " window_funcs={\"t\": (np.mean, 5)},\n", " names=[f\"plane-{i}\" for i in range(n_planes)],\n", " cmap=\"gnuplot2\", \n", + " grid_plot_kwargs={\"size\": (900, 600)},\n", ")" ] }, @@ -379,6 +387,7 @@ " data=zfish_data, # you can also provide a list of tzxy arrays\n", " window_funcs={\"t\": (np.mean, 5)},\n", " cmap=\"gnuplot2\", \n", + " grid_plot_kwargs={\"size\": (900, 600)},\n", ")" ] }, @@ -444,6 +453,14 @@ "source": [ "notebook_finished()" ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b8fff1a6-119e-4f03-ba3a-4c7b9e8c212b", + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { @@ -462,7 +479,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.2" + "version": "3.11.3" } }, "nbformat": 4, diff --git a/examples/notebooks/nb_test_utils.py b/examples/notebooks/nb_test_utils.py index 71d2d5114..90b7158ad 100644 --- a/examples/notebooks/nb_test_utils.py +++ b/examples/notebooks/nb_test_utils.py @@ -16,11 +16,73 @@ os.makedirs(SCREENSHOTS_DIR, exist_ok=True) os.makedirs(DIFFS_DIR, exist_ok=True) +TOLERANCE = 0.025 # store all the failures to allow the nb to proceed to test other examples FAILURES = list() +# TODO: consolidate testing functions into one module so we don't have this separate one for notebooks + +def rgba_to_rgb(img: np.ndarray) -> np.ndarray: + black = np.zeros(img.shape).astype(np.uint8) + black[:, :, -1] = 255 + + img_alpha = img[..., -1] / 255 + + rgb = img[..., :-1] * img_alpha[..., None] + black[..., :-1] * np.ones( + img_alpha.shape + )[..., None] * (1 - img_alpha[..., None]) + + return rgb.round().astype(np.uint8) + + +# image comparison functions from: https://github.com/pygfx/image-comparison +def image_similarity(src, target, threshold=0.2): + """Compute normalized RMSE 0..1 and decide if similar based on threshold. + + For every pixel, the euclidian distance between RGB values is computed, + and normalized by the maximum possible distance (between black and white). + The RMSE is then computed from those errors. + + The normalized RMSE is used to compute the + similarity metric, so larger errors (euclidian distance + between two RGB colors) will have a disproportionately + larger effect on the score than smaller errors. + + In other words, lots of small errors will lead to a good score + (closer to 0) whereas a few large errors will lead to a bad score + (closer to 1). + """ + float_type = np.float64 + src = np.asarray(src, dtype=float_type) + target = np.asarray(target, dtype=float_type) + denom = np.sqrt(np.mean(src * src)) + mse = np.mean((src - target) ** 2) + rmse = np.sqrt(mse) / denom + + similar = bool(rmse < threshold) + return similar, rmse + + +def normalize_image(img): + """Discard the alpha channel and convert from 0..255 uint8 to 0..1 float.""" + assert len(img.shape) == 3 + + # normalize to 0..1 range + if img.dtype == "u1" or np.max(img) > 1: + img = img / 255 + assert np.min(img) >= 0 and np.max(img) <= 1 + + # discard alpha channel + # unsupported if it's not fully opaque + if img.shape[-1] == 4: + assert np.max(img[..., 3]) == 1 + img = img[..., :-1] + + return img + + def _run_tests(): if "FASTPLOTLIB_NB_TESTS" not in os.environ.keys(): return False @@ -34,16 +96,15 @@ def _run_tests(): def plot_test(name, plot: Union[Plot, GridPlot]): if not _run_tests(): return + snapshot = plot.canvas.snapshot() + rgb_img = rgba_to_rgb(snapshot.data) if "REGENERATE_SCREENSHOTS" in os.environ.keys(): if os.environ["REGENERATE_SCREENSHOTS"] == "1": - regenerate_screenshot(name, snapshot.data) + regenerate_screenshot(name, rgb_img) - try: - assert_screenshot_equal(name, snapshot.data) - except AssertionError: - FAILURES.append(name) + assert_screenshot_equal(name, rgb_img) def regenerate_screenshot(name, data): @@ -53,13 +114,17 @@ def regenerate_screenshot(name, data): def assert_screenshot_equal(name, data): ground_truth = iio.imread(SCREENSHOTS_DIR.joinpath(f"nb-{name}.png")) - is_similar = np.allclose(data, ground_truth) + img = normalize_image(data) + ref_img = normalize_image(ground_truth) - update_diffs(name, is_similar, data, ground_truth) + similar, rmse = image_similarity(img, ref_img, threshold=TOLERANCE) - assert is_similar, ( - f"notebook snapshot for {name} has changed" - ) + update_diffs(name, similar, data, ground_truth) + + if not similar: + FAILURES.append( + (name, rmse) + ) def update_diffs(name, is_similar, img, ground_truth): @@ -81,7 +146,6 @@ def get_diffs_rgba(slicer): # split into an rgb and an alpha diff diffs = { DIFFS_DIR.joinpath(f"nb-diff-{name}-rgb.png"): slice(0, 3), - DIFFS_DIR.joinpath(f"nb-diff-{name}-alpha.png"): 3, } for path, slicer in diffs.items(): diff --git a/examples/notebooks/screenshots/nb-astronaut.png b/examples/notebooks/screenshots/nb-astronaut.png index 2faf79def..378260288 100644 --- a/examples/notebooks/screenshots/nb-astronaut.png +++ b/examples/notebooks/screenshots/nb-astronaut.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4845a61b99f7a489ac82a8688cc3350ce66e4771bb1399354591fd39688a58a2 -size 127977 +oid sha256:e584533ea04b9758634ba62dceeb72991861c509d01dc082436c54c272686409 +size 112104 diff --git a/examples/notebooks/screenshots/nb-astronaut_RGB.png b/examples/notebooks/screenshots/nb-astronaut_RGB.png index 22b2627cb..bf11bf667 100644 --- a/examples/notebooks/screenshots/nb-astronaut_RGB.png +++ b/examples/notebooks/screenshots/nb-astronaut_RGB.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2c34bd21fd7bf98bab25431019e8fee30b0f4912b6b4495ad963fb9e107b1f21 -size 125479 +oid sha256:db9602a610f258803d74ac03cd46447dd5a7ad62241ec26a4c3df30c1d6de299 +size 110408 diff --git a/examples/notebooks/screenshots/nb-camera.png b/examples/notebooks/screenshots/nb-camera.png index 32e83f3ba..9db4005bc 100644 --- a/examples/notebooks/screenshots/nb-camera.png +++ b/examples/notebooks/screenshots/nb-camera.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8ce695e954332a9b9122d418645d785900506fc30a897844bdf7fdce0bffb316 -size 89342 +oid sha256:4bb9080b99c2717e093bf6ae4986bf0689a8d377e137a7022c9c6929b9a335d3 +size 77965 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-set_data.png b/examples/notebooks/screenshots/nb-image-widget-movie-set_data.png index a728df223..5be8f55a3 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-set_data.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-set_data.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:794d4ba4e31884a95c5a98596d142aa2d2af75647c5ad73dc573ff18493c1a07 -size 31256 +oid sha256:66a310e312add59a310ff0a50335db97ac557d7f2967d8251a7d811c25a4de28 +size 40517 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-0-reset.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-0-reset.png index 8e624128e..8572f6472 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-0-reset.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-0-reset.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:edd99517550a035ee55fb81301fb1c3e08e3e90bc4345e781449d83fa307a8a9 -size 62630 +oid sha256:ff83d6bab26b9bbccf66ed100764ffdfc7556f4cb04f0b85f50c2497ba0ab257 +size 134419 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-0.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-0.png index 8e624128e..8572f6472 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-0.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:edd99517550a035ee55fb81301fb1c3e08e3e90bc4345e781449d83fa307a8a9 -size 62630 +oid sha256:ff83d6bab26b9bbccf66ed100764ffdfc7556f4cb04f0b85f50c2497ba0ab257 +size 134419 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-279.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-279.png index d27e49568..e241ce5a6 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-279.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-279.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3d5f5889af4490cdc81a47fd8235493bdf0384a015edcb5177ffea274efb64b1 -size 72626 +oid sha256:337a22f11649b350f7f47d68d82be165633caeb7f8cef581e50f981d6ec0c52c +size 169615 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-max-33.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-max-33.png index 12dce091c..b827fc536 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-max-33.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-max-33.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ec662212f6299cd27c3e26bb6b9fa1a99b4dd2808934536640e2f54d8bf8f699 -size 63968 +oid sha256:22ff3ed815fcbe8bc95c321c806a4b42536e7014209cd43ac597a5ccefd8b9c6 +size 149261 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-13.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-13.png index 44ccb8020..d37f44a3a 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ffe73fa414fa1af8e2daa0bdb7fa75fd47d9d575b60c729a9426deb42225bb5a -size 54513 +oid sha256:11143f73a297d0b59c92db1b115ceac1bc1304135a9925302e616a4fd3669b25 +size 125012 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-33.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-33.png index f0354cbb2..46d3fa9b3 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-33.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-33.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:10d82b8d15a5d1099f8a909401d78cc6a87d22708a7cd4c32d96bb316d570da3 -size 50431 +oid sha256:7438018c3b55d423f57c42a9d03c1af6d5de168a2dfa5df9d535ef2ae1f1c8e9 +size 113981 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-reset.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-reset.png index 25b65bd48..6146a7985 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-reset.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-reset.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:79776dadbe6947e9f20c96e5dc2d6ee718100f24fc09579609e7b25614869b74 -size 65150 +oid sha256:9b57a1e6640de9471540fa4d86faadb706d6de8cc1de6a29fb65d709b91eef1e +size 146429 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50.png index 25b65bd48..6146a7985 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:79776dadbe6947e9f20c96e5dc2d6ee718100f24fc09579609e7b25614869b74 -size 65150 +oid sha256:9b57a1e6640de9471540fa4d86faadb706d6de8cc1de6a29fb65d709b91eef1e +size 146429 diff --git a/examples/notebooks/screenshots/nb-image-widget-single-gnuplot2.png b/examples/notebooks/screenshots/nb-image-widget-single-gnuplot2.png index 75fa3ef5a..b8bf7adeb 100644 --- a/examples/notebooks/screenshots/nb-image-widget-single-gnuplot2.png +++ b/examples/notebooks/screenshots/nb-image-widget-single-gnuplot2.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7e292d6e43e7a98cb72ca077e62bbc9ad3acde4753098ea2248c66ee07a9fc46 -size 143675 +oid sha256:dbfa1e7aeb7f0a068a33f2f11023a06f834332f7b3d8e4cf97b51222536fd6cb +size 434782 diff --git a/examples/notebooks/screenshots/nb-image-widget-single.png b/examples/notebooks/screenshots/nb-image-widget-single.png index 1321d01bb..86119e247 100644 --- a/examples/notebooks/screenshots/nb-image-widget-single.png +++ b/examples/notebooks/screenshots/nb-image-widget-single.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:50f17ab342efff0d1d3bc06f36d2b7b372eed4426e282ffa2b12e3e7dc913b68 -size 134516 +oid sha256:7ceee2cdd73092cb84b4b0f2876fc08d838b8a47bb94d431a6c19c8a4793a153 +size 403521 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-gaussian.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-gaussian.png index 2543f9a5d..82cee281f 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-gaussian.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-gaussian.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5834c92427c08b210a0971667018b4a63fd4e8d916bb02a582ba352cb0d5aad7 -size 64281 +oid sha256:47e3e6cea0e738b2731060488886606f05595cfdfb0d81e6db1aa099dc8e3a84 +size 148181 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-reset.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-reset.png index 1a062cdf7..0b7832eee 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-reset.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-reset.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:00f8173e7476a781826617bbaf03241348cece3c25d355e13832961cb022145f -size 50155 +oid sha256:0af4ceb50ed269aa80667c3012a871c87f73777cd8cb497ebb243b53932b9bad +size 72377 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-max-window-13.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-max-window-13.png index 00af23a9a..2bc2db3a5 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-max-window-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-max-window-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:676217864c9f5ca4792c08a4b51574acdceb8c02b186e467633583b358bdb0f8 -size 121420 +oid sha256:9053c70da35fd42fe44a76e0ace8788ba79667b33c596409ca1e1f2f6d6ba3ad +size 195906 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-13.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-13.png index bd31b760d..d5999dd0f 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4bb119d1e38ec69285092c551da7ff12541a15c4ef144689e6535c520e0e988b -size 76596 +oid sha256:77d4a8542a5507e3eda1203a6da29a2f533bbbe2988ad297948c74e44a4337ec +size 177152 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-5.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-5.png index ccbbd13d6..29af0398d 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-5.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:57202d813c24f93fc106a1e66edd04af5b877ebf291380b3894b583ddccbda19 -size 72288 +oid sha256:3422923039d45b20ea6150f0ad545bdf876596ba60b156df5ec4004590a29a3e +size 139029 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50.png index d4020153c..bb07b8fbb 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cc156be2788a1d82f587a1ae0a7b29f4b6d75f07bfd83055ecefe92b213c01fd -size 56749 +oid sha256:ba9e055298372238ce0cd0c5ac4d75db8cd53f3f4acffbcc22bf7d503b40ec57 +size 79174 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-99.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-99.png index 368c1b9f8..6e8274659 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-99.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-99.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:03da34d72c3437de1bebea5a545e1a75fb7b6a7ea309cbd88340031acc5cb852 -size 45223 +oid sha256:c6aed15f9f1b6bae442687613c5b04621f42e78f1dbda1e3560b000d652ba0b3 +size 61523 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-gaussian.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-gaussian.png index c722c8ec1..28704bd2d 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-gaussian.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-gaussian.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:309384b0062003c9af321a005ddb5e0a78db382cf2b40285dd8ca43423cb13dd -size 74975 +oid sha256:499fab9183f2528297fdfa3c96a25eb58b2376a44556d790ef06928e0379af3a +size 174612 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-reset.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-reset.png index 9be3a311f..d163fd22a 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-reset.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-reset.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:28291e3913c28a2933affa12810254627f352fd4059a77d3637e2756737d11c0 -size 75617 +oid sha256:72832b86f802ee90b4eb54cb64d64aff59527fe0c7dcb87a4d8ab281ad15726b +size 142136 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-max-window-13.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-max-window-13.png index 912374687..52ebd8591 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-max-window-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-max-window-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c248700a71fb8284d039714d9b11672f6788bcd0df716c26aa936436c5035c28 -size 116784 +oid sha256:b414fbb8f6935901b65851de9c7cb37df628d7b24759dc7f407ee130389216a3 +size 371687 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-13.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-13.png index 66971bfb1..6c406a621 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a5fe9fce8c8e63e497b558370d0d3edd8f86b0ad27939feec02715dd84fe7353 -size 75550 +oid sha256:e0ef52156509f308d972533fb45509ba7451b4d6149400d519aae28274609e41 +size 212053 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-5.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-5.png index 76f6c71a2..aaed804b8 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-5.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b7d563033c432006b88adce07aabf0ab6a2c6559b519f6f820ebb58066f4d1b2 -size 79214 +oid sha256:8fd94e597074094dc3718153c8bb94fb0b1bf58e58e34601e5c7281f938f52bd +size 200278 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50.png index 90639b14f..3110fa7cf 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8408e6e841d09b4c2f1704803515c878cdc46e4f7d4cd67b1634efb4efba8e98 -size 82327 +oid sha256:76ab21314fbd7846c1b94aeeed9ef7b97be99d2f2f7f09c13c0474a889c21179 +size 159319 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-99.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-99.png index 97e3787ea..0cfad54e7 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-99.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-99.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4c21bbd9a9d1548d5480f9a0f0f6d4ace0abfd80e4d09e295557163310c7095d -size 79733 +oid sha256:77f247de5374a8cb7ce27a338ab8be880d53d9586b52f646b400901ba70be3aa +size 146217 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-init-mean-window-5.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-init-mean-window-5.png index 24eda7928..c74807939 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-init-mean-window-5.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-init-mean-window-5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2b6222d1a7291609f5d2637e97e9455a4e734b6dca4d6ebf9246ea46f32ce4de -size 81628 +oid sha256:33c3dfa77bbc558493634ab83fd1539ef76022a2e20d10e1353d2bd0a0e94a2c +size 183739 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-false.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-false.png index 3285e7875..a2841b1d5 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-false.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-false.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b6f5d03a9929a12232628da3a0c89673a650c94737ead73bf74e0daa587e75f9 -size 65977 +oid sha256:715f7909db0d374c2e618bb41f7a6341a8cc8891b1e3e8678a6f934fd71159a4 +size 127129 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-true.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-true.png index 63bd3d070..9064f2323 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-true.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-true.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:182410c74215dc70140561200cdb414555e98a1ea7767205764a19cc9b72bf6d -size 66489 +oid sha256:76708e8e6e6865d700aa5286fca4d58ba4eb91f21ab3b0243bb128e9a84f063c +size 131192 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-init-mean-window-5.png b/examples/notebooks/screenshots/nb-image-widget-zfish-init-mean-window-5.png index e82e34241..1fbaec974 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-init-mean-window-5.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-init-mean-window-5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:59eaa00b1ba39188f664719964036857df5cb143e2f70171d86e822958e98fa7 -size 62617 +oid sha256:51c62474b9ebee76242ef82a710a83b90e0183792790f6a2cd00213642b76755 +size 99519 diff --git a/examples/notebooks/screenshots/nb-imagewidget.png b/examples/notebooks/screenshots/nb-imagewidget.png new file mode 100644 index 000000000..9acfdb0f9 --- /dev/null +++ b/examples/notebooks/screenshots/nb-imagewidget.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0f7a4a260ef4f9f2bdee9adab4ed376147cff39fcdd2f07eaf6e87e8b899f7d3 +size 89842 diff --git a/examples/notebooks/screenshots/nb-lines-3d.png b/examples/notebooks/screenshots/nb-lines-3d.png index 4ac6b8b92..a3b75de58 100644 --- a/examples/notebooks/screenshots/nb-lines-3d.png +++ b/examples/notebooks/screenshots/nb-lines-3d.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:467cf0f08f6861c9556a412ed57e0bc6ff4499e62d576fa10dcdc2ca9ef4de6a -size 23693 +oid sha256:677f544d93cf7a733003c38666f555f0b598d41b92681dd2f09e6c11faa4aed0 +size 14186 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-jet-values-cosine.png b/examples/notebooks/screenshots/nb-lines-cmap-jet-values-cosine.png index 0c875da24..0d9ff5729 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-jet-values-cosine.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-jet-values-cosine.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c870f3010432310ba6d1937410a23836de6ddb656eb83bfb1b928d7d1211bd09 -size 17263 +oid sha256:0d981d57d7905879ab68af95da84d2cf530a9a40dc4d0ffb138119a11f4966be +size 11808 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-jet-values.png b/examples/notebooks/screenshots/nb-lines-cmap-jet-values.png index e193c85b3..dbcbf1e7f 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-jet-values.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-jet-values.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5c98af9372102a7fef96fe45650ca7e0b4f6e3589abd5fc3c1205130f3613c08 -size 18859 +oid sha256:706bffa485dd7994a70602ef2076aee79e1206dd508fbac903549fce526087b6 +size 13095 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-jet.png b/examples/notebooks/screenshots/nb-lines-cmap-jet.png index acc52de78..6a3ae0c1c 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-jet.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-jet.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bf3494a4cc2d4ea061fbd3465bd2133120b63bab6b248681307687e4e8e395a2 -size 16322 +oid sha256:cfd3f55e1671ac1fa45a8eb26aeb425ccb8d1ac033f5766f4002fee4380b2a77 +size 11174 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-tab-10.png b/examples/notebooks/screenshots/nb-lines-cmap-tab-10.png index d18cadb51..9bb368e0e 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-tab-10.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-tab-10.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b66364f0b7ba1da27b713e48cc757215f84143f6646451e8dfe7938d90a69102 -size 14863 +oid sha256:338dbcdf1a87266eee33367bfa08bcaec7eac42ef2dd928bbc699b3a0412ebaf +size 9889 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-viridis-values.png b/examples/notebooks/screenshots/nb-lines-cmap-viridis-values.png index ecdbb0db7..23137bdf3 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-viridis-values.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-viridis-values.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2e1b04f1ffc5c63dd93c9676bbf751f8ca698e113ce3403534f40ab868421960 -size 14974 +oid sha256:e36a7a74ac39dac5ac96a4e7c8e56990794ab1cda1b8ee5276087e9814dd1696 +size 10100 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-viridis.png b/examples/notebooks/screenshots/nb-lines-cmap-viridis.png index a78bd9d54..2fcd4749c 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-viridis.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-viridis.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:69166f21efd52253b2b8444c32c9770a87983b3f21afcf49f4d0a94c51e5280c -size 19206 +oid sha256:b12ee5e31f64415b57536c59587b91c7a3a7c74e95be3459d8036a43d073d7db +size 13821 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-white.png b/examples/notebooks/screenshots/nb-lines-cmap-white.png index c61050a7d..397b5fc94 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-white.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-white.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1556f875c6e5538ca289db5ced0e59530174d899d6baa19876e1ad753571d4d1 -size 12921 +oid sha256:6239f23d3b5c1745879f5706352abb905d5638522b171776bff051e511426c2f +size 8359 diff --git a/examples/notebooks/screenshots/nb-lines-colors.png b/examples/notebooks/screenshots/nb-lines-colors.png index 33a751e98..149321e52 100644 --- a/examples/notebooks/screenshots/nb-lines-colors.png +++ b/examples/notebooks/screenshots/nb-lines-colors.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fb407b2008cb7a6793e7e53d487b959e1178de2a3b7fbd504b8ef3b92a7b09ea -size 40842 +oid sha256:9a5e99c5a872d9bbf8dd909498459ddee7f22f08d3fe3cd68b3ea57c105ab51b +size 27634 diff --git a/examples/notebooks/screenshots/nb-lines-data.png b/examples/notebooks/screenshots/nb-lines-data.png index 7447b5b86..9ed38b1f5 100644 --- a/examples/notebooks/screenshots/nb-lines-data.png +++ b/examples/notebooks/screenshots/nb-lines-data.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3fe83c11993dc99085526e11db60ac6797756b2b35145458009b36e56991a0d9 -size 55297 +oid sha256:cc0d3d6819ce0d9f5d531276bbde0397ef35e3084ac1f9b3575f0209eea07456 +size 39512 diff --git a/examples/notebooks/screenshots/nb-lines-underlay.png b/examples/notebooks/screenshots/nb-lines-underlay.png index 692aa76ea..d4b3d9f6d 100644 --- a/examples/notebooks/screenshots/nb-lines-underlay.png +++ b/examples/notebooks/screenshots/nb-lines-underlay.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f65834683f945ae52612fa1ae8415d25866ccd6d776dd94798b30b08769b0387 -size 56338 +oid sha256:9b12c8f29436be8d17c38f420120ab3d54b0eee9bef751eea2f99d01b1a8fa43 +size 50761 diff --git a/examples/notebooks/screenshots/nb-lines.png b/examples/notebooks/screenshots/nb-lines.png index b89e9f47c..603f4a8fc 100644 --- a/examples/notebooks/screenshots/nb-lines.png +++ b/examples/notebooks/screenshots/nb-lines.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cc0511ba508333566c902a3f1a902e76044a33a41f75bfc43ee158ff5b7bd6c7 -size 37671 +oid sha256:eeef2c47e7dde62038307fa7929a306801bf8b708fbcf1062ed9c751727bfb2b +size 24300 diff --git a/examples/tests/test_examples.py b/examples/tests/test_examples.py index 876533fa6..a570b4f36 100644 --- a/examples/tests/test_examples.py +++ b/examples/tests/test_examples.py @@ -1,6 +1,7 @@ """ Test that examples run without error. """ + import importlib import runpy import pytest @@ -8,6 +9,7 @@ import numpy as np import imageio.v3 as iio + from .testutils import ( ROOT, examples_dir, @@ -15,7 +17,11 @@ find_examples, wgpu_backend, is_lavapipe, - diffs_dir + diffs_dir, + generate_diff, + image_similarity, + normalize_image, + prep_for_write, ) # run all tests unless they opt-out @@ -52,7 +58,12 @@ def test_that_we_are_on_lavapipe(): def test_example_screenshots(module, force_offscreen): """Make sure that every example marked outputs the expected.""" # (relative) module name from project root - module_name = module.relative_to(ROOT/"examples").with_suffix("").as_posix().replace("/", ".") + module_name = ( + module.relative_to(ROOT / "examples") + .with_suffix("") + .as_posix() + .replace("/", ".") + ) # import the example module example = importlib.import_module(module_name) @@ -69,20 +80,35 @@ def test_example_screenshots(module, force_offscreen): screenshot_path = screenshots_dir / f"{module.stem}.png" + black = np.zeros(img.shape).astype(np.uint8) + black[:, :, -1] = 255 + + img_alpha = img[..., -1] / 255 + + rgb = img[..., :-1] * img_alpha[..., None] + black[..., :-1] * np.ones( + img_alpha.shape + )[..., None] * (1 - img_alpha[..., None]) + + rgb = rgb.round().astype(np.uint8) + if "REGENERATE_SCREENSHOTS" in os.environ.keys(): if os.environ["REGENERATE_SCREENSHOTS"] == "1": - iio.imwrite(screenshot_path, img) - #np.save(screenshot_path, img) + iio.imwrite(screenshot_path, rgb) assert ( screenshot_path.exists() ), "found # test_example = true but no reference screenshot available" - #stored_img = np.load(screenshot_path) - stored_img = iio.imread(screenshot_path) - is_similar = np.allclose(img, stored_img, atol=1) - update_diffs(module.stem, is_similar, img, stored_img) - assert is_similar, ( - f"rendered image for example {module.stem} changed, see " + + ref_img = iio.imread(screenshot_path) + + rgb = normalize_image(rgb) + ref_img = normalize_image(ref_img) + + similar, rmse = image_similarity(rgb, ref_img, threshold=0.025) + + update_diffs(module.stem, similar, rgb, ref_img) + assert similar, ( + f"diff {rmse} above threshold for {module.stem}, see " f"the {diffs_dir.relative_to(ROOT).as_posix()} folder" " for visual diffs (you can download this folder from" " CI build artifacts as well)" @@ -110,7 +136,6 @@ def get_diffs_rgba(slicer): # split into an rgb and an alpha diff diffs = { diffs_dir / f"diff-{module}-rgb.png": slice(0, 3), - diffs_dir / f"diff-{module}-alpha.png": 3, } for path, slicer in diffs.items(): diff --git a/examples/tests/testutils.py b/examples/tests/testutils.py index 5f6772fb7..f62ae7602 100644 --- a/examples/tests/testutils.py +++ b/examples/tests/testutils.py @@ -7,6 +7,8 @@ import sys from itertools import chain +import numpy as np + ROOT = Path(__file__).parents[2] # repo root examples_dir = ROOT / "examples" / "desktop" @@ -65,3 +67,189 @@ def find_examples(query=None, negative_query=None, return_stems=False): result = [r.stem for r in result] return result + +# image comparison functions from: https://github.com/pygfx/image-comparison +def image_similarity(src, target, threshold=0.2): + """Compute normalized RMSE 0..1 and decide if similar based on threshold. + + For every pixel, the euclidian distance between RGB values is computed, + and normalized by the maximum possible distance (between black and white). + The RMSE is then computed from those errors. + + The normalized RMSE is used to compute the + similarity metric, so larger errors (euclidian distance + between two RGB colors) will have a disproportionately + larger effect on the score than smaller errors. + + In other words, lots of small errors will lead to a good score + (closer to 0) whereas a few large errors will lead to a bad score + (closer to 1). + """ + float_type = np.float64 + src = np.asarray(src, dtype=float_type) + target = np.asarray(target, dtype=float_type) + denom = np.sqrt(np.mean(src * src)) + mse = np.mean((src - target) ** 2) + rmse = np.sqrt(mse) / denom + + similar = bool(rmse < threshold) + return similar, rmse + + +def normalize_image(img): + """Discard the alpha channel and convert from 0..255 uint8 to 0..1 float.""" + assert len(img.shape) == 3 + + # normalize to 0..1 range + if img.dtype == "u1" or np.max(img) > 1: + img = img / 255 + assert np.min(img) >= 0 and np.max(img) <= 1 + + # discard alpha channel + # unsupported if it's not fully opaque + if img.shape[-1] == 4: + assert np.max(img[..., 3]) == 1 + img = img[..., :-1] + + return img + + +def prep_for_write(img): + """Convert 0..1 float back to 0..255 uint8.""" + assert len(img.shape) == 3 + assert np.min(img) >= 0 and np.max(img) <= 1 + if img.dtype != "u1": + img = np.round(img * 255).astype("u1") + return img + + +def rescale_arr(arr, min, max): + """ + histogram rescale utility function + e.g. if the values are 0.3..0.7 + they are rescaled to min..max + """ + return np.interp(arr, (arr.min(), arr.max()), (min, max)) + + +def rgb_to_hls(rgb): + """ + convert rgb to hls + assumes input ranges are 0..1 + returns values in range 0..1 + + vectorized version of colorsys.rgb_to_hls + """ + maxc = np.max(rgb, axis=-1) + minc = np.min(rgb, axis=-1) + hls = np.empty_like(rgb) + l = (minc + maxc) / 2.0 # noqa: E741 + + with np.errstate(invalid="ignore"): + mask = l <= 0.5 + idx = np.where(mask) + hls[(*idx, 2)] = (maxc[idx] - minc[idx]) / (maxc[idx] + minc[idx]) + + idx = np.where(~mask) + hls[(*idx, 2)] = (maxc[idx] - minc[idx]) / (2.0 - maxc[idx] - minc[idx]) + + maxc_minc = maxc - minc + rc = (maxc - rgb[..., 0]) / maxc_minc + gc = (maxc - rgb[..., 1]) / maxc_minc + bc = (maxc - rgb[..., 2]) / maxc_minc + + mask1 = rgb[..., 0] == maxc + idx = np.where(mask1) + hls[(*idx, 0)] = bc[idx] - gc[idx] + + mask2 = rgb[..., 1] == maxc + idx = np.where(~mask1 & mask2) + hls[(*idx, 0)] = 2.0 + rc[idx] - bc[idx] + + idx = np.where(~mask1 & ~mask2) + hls[(*idx, 0)] = 4.0 + gc[idx] - rc[idx] + + hls[..., 0] = (hls[..., 0] / 6.0) % 1.0 + + idx = np.where(minc == maxc) + hls[idx] = 0.0 + hls[..., 1] = l + + return hls + + +def hls_to_rgb(hls): + """ + convert hls to rgb + assumes input ranges are 0..1 + returns values in range 0..1 + + vectorized version of colorsys.hls_to_rgb + """ + rgb = np.empty_like(hls) + + m2 = np.empty_like(hls[..., 1]) + mask = hls[..., 1] <= 0.5 + idx = np.where(mask) + m2[idx] = hls[(*idx, 1)] * (1.0 + hls[(*idx, 2)]) + idx = np.where(~mask) + m2[idx] = hls[(*idx, 1)] + hls[(*idx, 2)] - (hls[(*idx, 1)] * hls[(*idx, 2)]) + m1 = 2.0 * hls[..., 1] - m2 + + h1 = (hls[..., 0] + 1 / 3) % 1.0 + h2 = hls[..., 0] % 1.0 + h3 = (hls[..., 0] - 1 / 3) % 1.0 + + for i, h in enumerate([h1, h2, h3]): + mask1 = h < 1 / 6 + idx = np.where(mask1) + rgb[(*idx, i)] = m1[idx] + (m2[idx] - m1[idx]) * h[idx] * 6.0 + + mask2 = h < 0.5 + idx = np.where(~mask1 & mask2) + rgb[(*idx, i)] = m2[idx] + + mask3 = h < 2 / 3 + idx = np.where(~mask1 & ~mask2 & mask3) + rgb[(*idx, i)] = m1[idx] + (m2[idx] - m1[idx]) * ((2 / 3) - h[idx]) * 6.0 + + idx = np.where(~mask1 & ~mask2 & ~mask3) + rgb[(*idx, i)] = m1[idx] + + return rgb + + +def generate_diff(src, target, fuzz=0.05): + """ + Generate an image that + highlights the differences between src and target image + any pixels with a euclidian color distance < fuzz will be ignored + fuzz is expressed as a percentage of the maximum possible distance + which is the distance between (0,0,0) and (1,1,1) = sqrt(3). + """ + # compute euclidian distance between pixels + # and normalize to 0..1 + max_dist = np.linalg.norm([1, 1, 1], axis=-1) + error = np.linalg.norm(np.abs(target - src), axis=-1) / max_dist + # apply fuzz + error_idx = np.where(error > fuzz) + + diff_img_hls = rgb_to_hls(target) + # lighten the whole image + diff_img_hls[..., 1] = rescale_arr(diff_img_hls[..., 1], 0.25, 1.0) + diff_img_hls[..., 1] **= 0.2 + # reduce the color saturation + diff_img_hls[..., 2] = rescale_arr(diff_img_hls[..., 2], 0.0, 0.75) + diff_img_hls[..., 2] **= 2 + + # make the diff pixels red + diff_img_hls[(*error_idx, 0)] = 0 + # give them the same lighting level + diff_img_hls[(*error_idx, 1)] = 0.5 + # saturate based on the error + diff_img_hls[(*error_idx, 2)] = 0.5 + error[error_idx] * 0.5 + + # convert back to rgb + diff_img = hls_to_rgb(diff_img_hls) + + return diff_img From 704b36a2be501cb1e7e5093bbe20f46c7792e604 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Sun, 31 Mar 2024 04:21:26 -0400 Subject: [PATCH 056/185] Update CONTRIBUTING.md update contributing w.r.t. simpler screenshot testing --- CONTRIBUTING.md | 83 +++++++++++++++++++++++++++++-------------------- 1 file changed, 50 insertions(+), 33 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ad484db5d..facca91db 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,7 +8,7 @@ Contributions are welcome! :smile: ![image](https://github.com/kushalkolar/fastplotlib/assets/9403332/82612021-37b2-48dd-b7e4-01a919535c17) -2. Clone the repo and install according to the development instructions. Replace the `YOUR_ACCOUNT` in the repo URL to the fork on your account. Note that fastplotlib uses [git-lfs](https://git-lfs.com) for storing large files, so you will need to [install it](https://github.com/git-lfs/git-lfs#installing) before cloning the repo. +2. Clone the repo and install according to the development instructions. Replace the `YOUR_ACCOUNT` in the repo URL to the fork on your account. We use [git-lfs](https://git-lfs.com) for storing large files, such as ground-truths for tests, so you will need to [install it](https://github.com/git-lfs/git-lfs#installing) before cloning the repo. ```bash git clone https://github.com/YOUR_ACCOUNT/fastplotlib.git @@ -18,14 +18,12 @@ cd fastplotlib pip install -e ".[notebook,docs,tests]" ``` -> If you cloned before installing `git-lfs`, you can run `git lfs pull` at any -> time to properly download files. +> If you cloned the repo before installing `git-lfs`, you can run `git lfs pull` at any +> time to download the files stored on LFS 3. Checkout the `main` branch, and then checkout your feature or bug fix branch, and run tests: -> **Warning** -> Do not commit or add any changes from `examples/desktop/screenshots`. -> If you are creating new test examples that generate or change screenshots please post an issue on the repo and we will help you. The screenshots will be generated on github actions servers, which you can then copy into the screenshots dir. :) +If your contributions modify how visualizations look, see the "Tests in detail" section at the very bottom. ```bash cd fastplotlib @@ -35,14 +33,14 @@ git checkout main # checkout your new branch from main git checkout -b my-new-feature-branch -# make your changes -# run tests -REGENERATE_SCREENSHOTS=1 pytest -v -k examples - -# make some changes, lint with black, and commit +# make some changes, lint with black black . -# add only your changed files, not the entire repo, do not add changes to examples/screenshots +# run tests from the repo root dir +pytest -v examples +FASTPLOTLIB_NB_TESTS=1 pytest --nbmake examples/notebooks/ + +# add your changed files, do not add any changes from screenshot diff dirs git add my_changed_files # commit changes @@ -172,7 +170,7 @@ This class inherits from `PlotArea` and `GraphicMethodsMixin`. `GraphicMethodsMixin` is a simple class that just has all the `add_` methods. It is autogenerated by a utility script like this: ```bash -python fastplotlib/utils/generate_add_methods.py +python scripts/generate_add_methods.py ``` Each `add_` method basically creates an instance of `Graphic`, adds it to the `Subplot`, and returns a weakref @@ -197,28 +195,47 @@ below the `Canvas`. If using a glfw canvas it just returns the canvas. addition of `Frame`. `GridPlot.__init__` basically does a lot of parsing of user arguments to determine how to create the subplots. All subplots within a `GridPlot` share the same canvas and use different viewports to create the subplots. -## Running tests +## Tests in detail + +The CI pipeline for a plotting library that is supposed to produce things that "look visually correct". Each example +within the `examples` dir is run and an image of the canvas is taken and compared with a ground-truth +screenshot that we have manually inspected. Ground-truth image are stored using `git-lfs`. + +The ground-truth images are in: + +``` +examples/desktop/screenshots +examples/notebooks/screenshots +``` + +The tests will produce slightly different imperceptible (to a human) results on different hardware when compared to the +ground-truth. A small RMSE tolerance has been chosen, `0.025` for most examples. If the output image and +ground-truth image are within that tolerance the test will pass. -The CI pipeline for a plotting library is is supposed to produce things that "look visually correct" is a bit more -complicated than the CI pipeline for most libraries. -Our CI pipeline is modelled after `pygfx`. Basically, a bunch of examples exist within the `examples` dir. Each of these -examples are run and a screenshot of the canvas is taken and compared with a ground-truth screenshot that we have -manually inspected. Screenshots are stored using `git-lfs`. +To run tests: -At the moment these tests will produce slightly different imperceptible (to a human) results on different hardware, but -nonetheless the image arrays will have a small difference. Because of this, we only run tests on GitHub actions. There -is a specific actions workflow that only generate screenshots and doesn't run tests which is run specifically to create -ground-truth screenshot on GitHub actions servers to use for test. +```bash +# desktop examples +pytest -v examples + +# notebook examples +FASTPLOTLIB_NB_TESTS=1 pytest --nbmake examples/notebooks/ +``` + +If your contribution modifies a ground-truth test screenshot then replace the ground-truth image along with your PR and +also notify us of this in the PR. Likewise, if your contribution requires a new test or new ground-truth then include +this new image in your PR. -If your contribution modifies a ground-truth test screenshot the general workflow is like this, if you have questions -don't hesitate to ask us :smile: +You can create/regenerate ground-truths for the examples like this: + +```bash +# desktop examples +REGENERATE_SCREENSHOTS=1 pytest -v examples/ + +# notebook examples +FASTPLOTLIB_NB_TESTS=1 REGENERATE_SCREENSHOTS=1 pytest --nbmake examples/notebooks/image_widget_test.ipynb +``` -1. Create a PR with your code changes, **do not upload any new/modified test screenshots" -1. See if the CI failed, if so it will indicate the specific examples that have failed. -1. Go to the details page for the "Screenshots / Regenerate" workflow. Click on "Summary" in the top left, and download -the build artifact which is named "screenshots". This is a zip file of generated ground-truth screenshots. -1. Visually inspect the specific new/modified screenshot that corresponds to your code change. Make sure it looks like, -past a copy of it in the PR as a reply -1. Copy over the new/modified screenshots to your local repo. -1. Add and commit the new/modified screenshot files, and push. The tests should now pass. +**Please only commit ground-truth images that correspond to your PR** since this will generate ground-truth images for +the entire test suite. From 2f49497da288f0a6de56be5d9eef6908f8dd1a29 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Sun, 31 Mar 2024 04:21:44 -0400 Subject: [PATCH 057/185] min version py3.10, cleanup type annotations (#465) --- fastplotlib/graphics/_base.py | 18 ++-- fastplotlib/graphics/line.py | 6 +- fastplotlib/graphics/line_collection.py | 77 +++++++++-------- fastplotlib/graphics/scatter.py | 6 +- fastplotlib/layouts/_defaults.py | 1 - .../layouts/_frame/_ipywidget_toolbar.py | 4 +- fastplotlib/layouts/_frame/_jupyter_output.py | 4 +- ...ods_mixin.py => _graphic_methods_mixin.py} | 82 ++++++++++--------- fastplotlib/layouts/_gridplot.py | 57 ++++++++----- fastplotlib/layouts/_plot_area.py | 77 +++++++++-------- fastplotlib/layouts/_record_mixin.py | 5 +- fastplotlib/layouts/_subplot.py | 30 ++++--- fastplotlib/layouts/_utils.py | 15 ++-- fastplotlib/legends/legend.py | 12 +-- fastplotlib/utils/functions.py | 7 +- fastplotlib/widgets/histogram_lut.py | 3 +- .../generate_add_graphic_methods.py | 2 +- setup.py | 2 +- 18 files changed, 218 insertions(+), 190 deletions(-) delete mode 100644 fastplotlib/layouts/_defaults.py rename fastplotlib/layouts/{graphic_methods_mixin.py => _graphic_methods_mixin.py} (87%) rename fastplotlib/utils/generate_add_methods.py => scripts/generate_add_graphic_methods.py (97%) diff --git a/fastplotlib/graphics/_base.py b/fastplotlib/graphics/_base.py index fdf120268..4442c851e 100644 --- a/fastplotlib/graphics/_base.py +++ b/fastplotlib/graphics/_base.py @@ -1,4 +1,4 @@ -from typing import * +from typing import Any, Literal import weakref from warnings import warn from abc import ABC, abstractmethod @@ -13,7 +13,7 @@ # dict that holds all world objects for a given python kernel/session # Graphic objects only use proxies to WorldObjects -WORLD_OBJECTS: Dict[str, WorldObject] = dict() #: {hex id str: WorldObject} +WORLD_OBJECTS: dict[str, WorldObject] = dict() #: {hex id str: WorldObject} PYGFX_EVENTS = [ @@ -87,7 +87,7 @@ def __init__( self._plot_area = None @property - def name(self) -> Union[str, None]: + def name(self) -> str | None: """str name reference for this item""" return self._name @@ -162,7 +162,7 @@ def visible(self, v: bool): self.world_object.visible = v @property - def children(self) -> List[WorldObject]: + def children(self) -> list[WorldObject]: """Return the children of the WorldObject.""" return self.world_object.children @@ -432,7 +432,7 @@ class PreviouslyModifiedData: indices: Any -COLLECTION_GRAPHICS: Dict[str, Graphic] = dict() +COLLECTION_GRAPHICS: dict[str, Graphic] = dict() class GraphicCollection(Graphic): @@ -440,7 +440,7 @@ class GraphicCollection(Graphic): def __init__(self, name: str = None): super().__init__(name) - self._graphics: List[str] = list() + self._graphics: list[str] = list() self._graphics_changed: bool = True self._graphics_array: np.ndarray[Graphic] = None @@ -548,7 +548,7 @@ class CollectionIndexer: def __init__( self, parent: GraphicCollection, - selection: List[Graphic], + selection: list[Graphic], ): """ @@ -605,7 +605,7 @@ def __repr__(self): class CollectionFeature: """Collection Feature""" - def __init__(self, selection: List[Graphic], feature: str): + def __init__(self, selection: list[Graphic], feature: str): """ selection: list of Graphics a list of the selected Graphics from the parent GraphicCollection based on the ``selection_indices`` @@ -618,7 +618,7 @@ def __init__(self, selection: List[Graphic], feature: str): self._selection = selection self._feature = feature - self._feature_instances: List[GraphicFeature] = list() + self._feature_instances: list[GraphicFeature] = list() if len(self._selection) > 0: for graphic in self._selection: diff --git a/fastplotlib/graphics/line.py b/fastplotlib/graphics/line.py index d76c8e704..f44347a58 100644 --- a/fastplotlib/graphics/line.py +++ b/fastplotlib/graphics/line.py @@ -18,10 +18,10 @@ def __init__( self, data: Any, thickness: float = 2.0, - colors: Union[str, np.ndarray, Iterable] = "w", + colors: str | np.ndarray | Iterable = "w", alpha: float = 1.0, cmap: str = None, - cmap_values: Union[np.ndarray, List] = None, + cmap_values: np.ndarray | Iterable = None, z_position: float = None, collection_index: int = None, *args, @@ -46,7 +46,7 @@ def __init__( apply a colormap to the line instead of assigning colors manually, this overrides any argument passed to "colors" - cmap_values: 1D array-like or list of numerical values, optional + cmap_values: 1D array-like or Iterable of numerical values, optional if provided, these values are used to map the colors from the cmap alpha: float, optional, default 1.0 diff --git a/fastplotlib/graphics/line_collection.py b/fastplotlib/graphics/line_collection.py index bb7bb2444..8488ec15e 100644 --- a/fastplotlib/graphics/line_collection.py +++ b/fastplotlib/graphics/line_collection.py @@ -20,14 +20,14 @@ class LineCollection(GraphicCollection, Interaction): def __init__( self, data: List[np.ndarray], - z_position: Union[List[float], float] = None, - thickness: Union[float, List[float]] = 2.0, - colors: Union[List[np.ndarray], np.ndarray] = "w", + z_position: Iterable[float] | float = None, + thickness: float | Iterable[float] = 2.0, + colors: str | Iterable[str] | np.ndarray | Iterable[np.ndarray] = "w", alpha: float = 1.0, - cmap: Union[List[str], str] = None, - cmap_values: Union[np.ndarray, List] = None, + cmap: Iterable[str] | str = None, + cmap_values: np.ndarray | List = None, name: str = None, - metadata: Union[list, tuple, np.ndarray] = None, + metadata: Iterable[Any] | np.ndarray = None, *args, **kwargs, ): @@ -36,39 +36,41 @@ def __init__( Parameters ---------- - data: list of array-like or array List of line data to plot, each element must be a 1D, 2D, or 3D numpy array if elements are 2D, interpreted as [y_vals, n_lines] - z_position: list of float or float, optional + z_position: Iterable of float or float, optional | if ``float``, single position will be used for all lines | if ``list`` of ``float``, each value will apply to the individual lines - thickness: float or list of float, default 2.0 + thickness: float or Iterable of float, default 2.0 | if ``float``, single thickness will be used for all lines | if ``list`` of ``float``, each value will apply to the individual lines - colors: str, RGBA array, list of RGBA array, or list of str, default "w" + colors: str, RGBA array, Iterable of RGBA array, or Iterable of str, default "w" | if single ``str`` such as "w", "r", "b", etc, represents a single color for all lines | if single ``RGBA array`` (tuple or list of size 4), represents a single color for all lines | if ``list`` of ``str``, represents color for each individual line, example ["w", "b", "r",...] | if ``RGBA array`` of shape [data_size, 4], represents a single RGBA array for each line - cmap: list of str or str, optional + alpha: float, optional + alpha value for colors, if colors is a ``str`` + + cmap: Iterable of str or str, optional | if ``str``, single cmap will be used for all lines | if ``list`` of ``str``, each cmap will apply to the individual lines .. note:: ``cmap`` overrides any arguments passed to ``colors`` - cmap_values: 1D array-like or list of numerical values, optional + cmap_values: 1D array-like or Iterable of numerical values, optional if provided, these values are used to map the colors from the cmap name: str, optional name of the line collection - metadata: list, tuple, or array + metadata: Iterable or array metadata associated with this collection, this is for the user to manage. ``len(metadata)`` must be same as ``len(data)`` @@ -235,7 +237,7 @@ def cmap_values(self) -> np.ndarray: return self._cmap_values @cmap_values.setter - def cmap_values(self, values: Union[np.ndarray, list]): + def cmap_values(self, values: np.ndarray | Iterable): colors = parse_cmap_values( n_colors=len(self), cmap_name=self.cmap, cmap_values=values ) @@ -477,13 +479,16 @@ class LineStack(LineCollection): def __init__( self, data: List[np.ndarray], - z_position: Union[List[float], float] = None, - thickness: Union[float, List[float]] = 2.0, - colors: Union[List[np.ndarray], np.ndarray] = "w", - cmap: Union[List[str], str] = None, - separation: float = 10, - separation_axis: str = "y", + z_position: Iterable[float] | float = None, + thickness: float | Iterable[float] = 2.0, + colors: str | Iterable[str] | np.ndarray | Iterable[np.ndarray] = "w", + alpha: float = 1.0, + cmap: Iterable[str] | str = None, + cmap_values: np.ndarray | List = None, name: str = None, + metadata: Iterable[Any] | np.ndarray = None, + separation: float = 10.0, + separation_axis: str = "y", *args, **kwargs, ): @@ -492,33 +497,37 @@ def __init__( Parameters ---------- - data: list of array-like + data: list of array-like or array List of line data to plot, each element must be a 1D, 2D, or 3D numpy array if elements are 2D, interpreted as [y_vals, n_lines] - z_position: list of float or float, optional + z_position: Iterable of float or float, optional | if ``float``, single position will be used for all lines - | if ``list`` of ``float``, each value will apply to individual lines + | if ``list`` of ``float``, each value will apply to the individual lines - thickness: float or list of float, default 2.0 + thickness: float or Iterable of float, default 2.0 | if ``float``, single thickness will be used for all lines | if ``list`` of ``float``, each value will apply to the individual lines - colors: str, RGBA array, list of RGBA array, or list of str, default "w" + colors: str, RGBA array, Iterable of RGBA array, or Iterable of str, default "w" | if single ``str`` such as "w", "r", "b", etc, represents a single color for all lines | if single ``RGBA array`` (tuple or list of size 4), represents a single color for all lines - | is ``list`` of ``str``, represents color for each individual line, example ["w", "b", "r",...] - | if ``list`` of ``RGBA array`` of shape [data_size, 4], represents a single RGBA array for each line + | if ``list`` of ``str``, represents color for each individual line, example ["w", "b", "r",...] + | if ``RGBA array`` of shape [data_size, 4], represents a single RGBA array for each line - cmap: list of str or str, optional + cmap: Iterable of str or str, optional | if ``str``, single cmap will be used for all lines | if ``list`` of ``str``, each cmap will apply to the individual lines .. note:: ``cmap`` overrides any arguments passed to ``colors`` - name: str, optional - name of the line stack + cmap_values: 1D array-like or Iterable of numerical values, optional + if provided, these values are used to map the colors from the cmap + + metadata: Iterable or array + metadata associated with this collection, this is for the user to manage. + ``len(metadata)`` must be same as ``len(data)`` separation: float, default 10 space in between each line graphic in the stack @@ -529,13 +538,9 @@ def __init__( name: str, optional name of the line stack - args - passed to LineCollection - kwargs passed to LineCollection - Features -------- @@ -549,8 +554,12 @@ def __init__( z_position=z_position, thickness=thickness, colors=colors, + alpha=alpha, cmap=cmap, + cmap_values=cmap_values, + metadata=metadata, name=name, + *args, **kwargs, ) diff --git a/fastplotlib/graphics/scatter.py b/fastplotlib/graphics/scatter.py index 3f04f644e..2557cd637 100644 --- a/fastplotlib/graphics/scatter.py +++ b/fastplotlib/graphics/scatter.py @@ -14,11 +14,11 @@ class ScatterGraphic(Graphic): def __init__( self, data: np.ndarray, - sizes: Union[int, float, np.ndarray, list] = 1, - colors: np.ndarray = "w", + sizes: float | np.ndarray | Iterable[float] = 1, + colors: str | np.ndarray | Iterable[str] = "w", alpha: float = 1.0, cmap: str = None, - cmap_values: Union[np.ndarray, List] = None, + cmap_values: np.ndarray | List = None, z_position: float = 0.0, *args, **kwargs, diff --git a/fastplotlib/layouts/_defaults.py b/fastplotlib/layouts/_defaults.py deleted file mode 100644 index 8b1378917..000000000 --- a/fastplotlib/layouts/_defaults.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/fastplotlib/layouts/_frame/_ipywidget_toolbar.py b/fastplotlib/layouts/_frame/_ipywidget_toolbar.py index 72976a445..5b42c8eab 100644 --- a/fastplotlib/layouts/_frame/_ipywidget_toolbar.py +++ b/fastplotlib/layouts/_frame/_ipywidget_toolbar.py @@ -4,8 +4,6 @@ from math import copysign from functools import partial from pathlib import Path -from typing import * - from ipywidgets.widgets import ( IntSlider, @@ -238,7 +236,7 @@ def __init__(self, iw): tooltip="reset vmin/vmax and reset histogram using current frame", ) - self.sliders: Dict[str, IntSlider] = dict() + self.sliders: dict[str, IntSlider] = dict() # only for xy data, no time point slider needed if self.iw.ndim == 2: diff --git a/fastplotlib/layouts/_frame/_jupyter_output.py b/fastplotlib/layouts/_frame/_jupyter_output.py index 786041bcf..9ebf0941d 100644 --- a/fastplotlib/layouts/_frame/_jupyter_output.py +++ b/fastplotlib/layouts/_frame/_jupyter_output.py @@ -1,5 +1,3 @@ -from typing import * - from ipywidgets import VBox, Widget from sidecar import Sidecar from IPython.display import display @@ -20,7 +18,7 @@ def __init__( make_toolbar: bool, use_sidecar: bool, sidecar_kwargs: dict, - add_widgets: List[Widget], + add_widgets: list[Widget], ): """ diff --git a/fastplotlib/layouts/graphic_methods_mixin.py b/fastplotlib/layouts/_graphic_methods_mixin.py similarity index 87% rename from fastplotlib/layouts/graphic_methods_mixin.py rename to fastplotlib/layouts/_graphic_methods_mixin.py index 0376fd777..a7acb5eec 100644 --- a/fastplotlib/layouts/graphic_methods_mixin.py +++ b/fastplotlib/layouts/_graphic_methods_mixin.py @@ -178,14 +178,14 @@ def add_image( def add_line_collection( self, data: List[numpy.ndarray], - z_position: Union[List[float], float] = None, - thickness: Union[float, List[float]] = 2.0, - colors: Union[List[numpy.ndarray], numpy.ndarray] = "w", + z_position: Union[Iterable[float], float] = None, + thickness: Union[float, Iterable[float]] = 2.0, + colors: Union[str, Iterable[str], numpy.ndarray, Iterable[numpy.ndarray]] = "w", alpha: float = 1.0, - cmap: Union[List[str], str] = None, + cmap: Union[Iterable[str], str] = None, cmap_values: Union[numpy.ndarray, List] = None, name: str = None, - metadata: Union[list, tuple, numpy.ndarray] = None, + metadata: Union[Iterable[Any], numpy.ndarray] = None, *args, **kwargs ) -> LineCollection: @@ -195,39 +195,41 @@ def add_line_collection( Parameters ---------- - data: list of array-like or array List of line data to plot, each element must be a 1D, 2D, or 3D numpy array if elements are 2D, interpreted as [y_vals, n_lines] - z_position: list of float or float, optional + z_position: Iterable of float or float, optional | if ``float``, single position will be used for all lines | if ``list`` of ``float``, each value will apply to the individual lines - thickness: float or list of float, default 2.0 + thickness: float or Iterable of float, default 2.0 | if ``float``, single thickness will be used for all lines | if ``list`` of ``float``, each value will apply to the individual lines - colors: str, RGBA array, list of RGBA array, or list of str, default "w" + colors: str, RGBA array, Iterable of RGBA array, or Iterable of str, default "w" | if single ``str`` such as "w", "r", "b", etc, represents a single color for all lines | if single ``RGBA array`` (tuple or list of size 4), represents a single color for all lines | if ``list`` of ``str``, represents color for each individual line, example ["w", "b", "r",...] | if ``RGBA array`` of shape [data_size, 4], represents a single RGBA array for each line - cmap: list of str or str, optional + alpha: float, optional + alpha value for colors, if colors is a ``str`` + + cmap: Iterable of str or str, optional | if ``str``, single cmap will be used for all lines | if ``list`` of ``str``, each cmap will apply to the individual lines .. note:: ``cmap`` overrides any arguments passed to ``colors`` - cmap_values: 1D array-like or list of numerical values, optional + cmap_values: 1D array-like or Iterable of numerical values, optional if provided, these values are used to map the colors from the cmap name: str, optional name of the line collection - metadata: list, tuple, or array + metadata: Iterable or array metadata associated with this collection, this is for the user to manage. ``len(metadata)`` must be same as ``len(data)`` @@ -268,7 +270,7 @@ def add_line( colors: Union[str, numpy.ndarray, Iterable] = "w", alpha: float = 1.0, cmap: str = None, - cmap_values: Union[numpy.ndarray, List] = None, + cmap_values: Union[numpy.ndarray, Iterable] = None, z_position: float = None, collection_index: int = None, *args, @@ -294,7 +296,7 @@ def add_line( apply a colormap to the line instead of assigning colors manually, this overrides any argument passed to "colors" - cmap_values: 1D array-like or list of numerical values, optional + cmap_values: 1D array-like or Iterable of numerical values, optional if provided, these values are used to map the colors from the cmap alpha: float, optional, default 1.0 @@ -346,13 +348,16 @@ def add_line( def add_line_stack( self, data: List[numpy.ndarray], - z_position: Union[List[float], float] = None, - thickness: Union[float, List[float]] = 2.0, - colors: Union[List[numpy.ndarray], numpy.ndarray] = "w", - cmap: Union[List[str], str] = None, - separation: float = 10, - separation_axis: str = "y", + z_position: Union[Iterable[float], float] = None, + thickness: Union[float, Iterable[float]] = 2.0, + colors: Union[str, Iterable[str], numpy.ndarray, Iterable[numpy.ndarray]] = "w", + alpha: float = 1.0, + cmap: Union[Iterable[str], str] = None, + cmap_values: Union[numpy.ndarray, List] = None, name: str = None, + metadata: Union[Iterable[Any], numpy.ndarray] = None, + separation: float = 10.0, + separation_axis: str = "y", *args, **kwargs ) -> LineStack: @@ -362,33 +367,37 @@ def add_line_stack( Parameters ---------- - data: list of array-like + data: list of array-like or array List of line data to plot, each element must be a 1D, 2D, or 3D numpy array if elements are 2D, interpreted as [y_vals, n_lines] - z_position: list of float or float, optional + z_position: Iterable of float or float, optional | if ``float``, single position will be used for all lines - | if ``list`` of ``float``, each value will apply to individual lines + | if ``list`` of ``float``, each value will apply to the individual lines - thickness: float or list of float, default 2.0 + thickness: float or Iterable of float, default 2.0 | if ``float``, single thickness will be used for all lines | if ``list`` of ``float``, each value will apply to the individual lines - colors: str, RGBA array, list of RGBA array, or list of str, default "w" + colors: str, RGBA array, Iterable of RGBA array, or Iterable of str, default "w" | if single ``str`` such as "w", "r", "b", etc, represents a single color for all lines | if single ``RGBA array`` (tuple or list of size 4), represents a single color for all lines - | is ``list`` of ``str``, represents color for each individual line, example ["w", "b", "r",...] - | if ``list`` of ``RGBA array`` of shape [data_size, 4], represents a single RGBA array for each line + | if ``list`` of ``str``, represents color for each individual line, example ["w", "b", "r",...] + | if ``RGBA array`` of shape [data_size, 4], represents a single RGBA array for each line - cmap: list of str or str, optional + cmap: Iterable of str or str, optional | if ``str``, single cmap will be used for all lines | if ``list`` of ``str``, each cmap will apply to the individual lines .. note:: ``cmap`` overrides any arguments passed to ``colors`` - name: str, optional - name of the line stack + cmap_values: 1D array-like or Iterable of numerical values, optional + if provided, these values are used to map the colors from the cmap + + metadata: Iterable or array + metadata associated with this collection, this is for the user to manage. + ``len(metadata)`` must be same as ``len(data)`` separation: float, default 10 space in between each line graphic in the stack @@ -399,13 +408,9 @@ def add_line_stack( name: str, optional name of the line stack - args - passed to LineCollection - kwargs passed to LineCollection - Features -------- @@ -421,10 +426,13 @@ def add_line_stack( z_position, thickness, colors, + alpha, cmap, + cmap_values, + name, + metadata, separation, separation_axis, - name, *args, **kwargs ) @@ -432,8 +440,8 @@ def add_line_stack( def add_scatter( self, data: numpy.ndarray, - sizes: Union[int, float, numpy.ndarray, list] = 1, - colors: numpy.ndarray = "w", + sizes: Union[float, numpy.ndarray, Iterable[float]] = 1, + colors: Union[str, numpy.ndarray, Iterable[str]] = "w", alpha: float = 1.0, cmap: str = None, cmap_values: Union[numpy.ndarray, List] = None, diff --git a/fastplotlib/layouts/_gridplot.py b/fastplotlib/layouts/_gridplot.py index fa987b661..5f7f3086d 100644 --- a/fastplotlib/layouts/_gridplot.py +++ b/fastplotlib/layouts/_gridplot.py @@ -1,6 +1,6 @@ from itertools import product, chain import numpy as np -from typing import * +from typing import Literal from inspect import getfullargspec from warnings import warn @@ -18,14 +18,23 @@ class GridPlot(Frame, RecordMixin): def __init__( self, - shape: Tuple[int, int], - cameras: Union[str, list, np.ndarray] = "2d", - controller_types: Union[str, list, np.ndarray] = None, - controller_ids: Union[str, list, np.ndarray] = None, - canvas: Union[str, WgpuCanvasBase, pygfx.Texture] = None, + shape: tuple[int, int], + cameras: ( + Literal["2d", "3d"] + | list[Literal["2d", "3d"]] + | list[pygfx.PerspectiveCamera] + | np.ndarray + ) = "2d", + controller_types: ( + Literal["panzoom", "fly", "trackball", "orbit"] + | list[Literal["panzoom", "fly", "trackball", "orbit"]] + | np.ndarray + ) = None, + controller_ids: str | list[int] | np.ndarray | list[list[str]] = None, + canvas: str | WgpuCanvasBase | pygfx.Texture = None, renderer: pygfx.WgpuRenderer = None, - size: Tuple[int, int] = (500, 300), - names: Union[list, np.ndarray] = None, + size: tuple[int, int] = (500, 300), + names: list | np.ndarray = None, ): """ A grid of subplots. @@ -35,27 +44,31 @@ def __init__( shape: (int, int) (n_rows, n_cols) - cameras: str, list, or np.ndarray, optional - | One of ``"2d"`` or ``"3d"`` indicating 2D or 3D cameras for all subplots + cameras: "2d", "3", list of "2d" | "3d", list of camera instances, or np.ndarray of "2d" | "3d", optional + | if str, one of ``"2d"`` or ``"3d"`` indicating 2D or 3D cameras for all subplots | list/array of ``2d`` and/or ``3d`` that specifies the camera type for each subplot | list/array of pygfx.PerspectiveCamera instances controller_types: str, list or np.ndarray, optional list or array that specifies the controller type for each subplot, or list/array of - pygfx.Controller instances + pygfx.Controller instances. Valid controller types: "panzoom", "fly", "trackball", "orbit". + If not specified a default controller is chosen based on the camera type. + Orthographic projections, i.e. "2d" cameras, use a "panzoom" controller by default. + Perspective projections with a FOV > 0, i.e. "3d" cameras, use a "fly" controller by default. - controller_ids: str, list or np.ndarray of int or str ids, optional + + controller_ids: str, list of int, np.ndarray of int, or list with sublists of subplot str names, optional | If `None` a unique controller is created for each subplot | If "sync" all the subplots use the same controller - | If ``numpy.array``, its shape must be the same as ``grid_shape``. + | If array/list it must be reshapeable to ``grid_shape``. This allows custom assignment of controllers | Example with integers: | sync first 2 plots, and sync last 2 plots: [[0, 0, 1], [2, 3, 3]] | Example with str subplot names: - | list of lists of subplot names, each sublist is synced: [[subplot_a, subplot_b], [subplot_f, subplot_c]] - | this syncs subplot_a and subplot_b together; syncs subplot_f and subplot_c together + | list of lists of subplot names, each sublist is synced: [[subplot_a, subplot_b, subplot_e], [subplot_c, subplot_d]] + | this syncs subplot_a, subplot_b and subplot_e together; syncs subplot_c and subplot_d together canvas: WgpuCanvas, optional Canvas for drawing @@ -249,8 +262,8 @@ def __init__( name=name, ) - self._animate_funcs_pre: List[callable] = list() - self._animate_funcs_post: List[callable] = list() + self._animate_funcs_pre: list[callable] = list() + self._animate_funcs_post: list[callable] = list() self._current_iter = None @@ -269,12 +282,12 @@ def renderer(self) -> pygfx.WgpuRenderer: """The renderer associated to this GridPlot""" return self._renderer - def __getitem__(self, index: Union[Tuple[int, int], str]) -> Subplot: + def __getitem__(self, index: tuple[int, int] | str) -> Subplot: if isinstance(index, str): for subplot in self._subplots.ravel(): if subplot.name == index: return subplot - raise IndexError("no subplot with given name") + raise IndexError(f"no subplot with given name: {index}") else: return self._subplots[index[0], index[1]] @@ -291,7 +304,7 @@ def render(self): # call post-render animate functions self._call_animate_functions(self._animate_funcs_post) - def _call_animate_functions(self, funcs: Iterable[callable]): + def _call_animate_functions(self, funcs: list[callable]): for fn in funcs: try: if len(getfullargspec(fn).args) > 0: @@ -307,7 +320,7 @@ def _call_animate_functions(self, funcs: Iterable[callable]): def add_animations( self, - *funcs: Iterable[callable], + *funcs: callable, pre_render: bool = True, post_render: bool = False, ): @@ -317,7 +330,7 @@ def add_animations( Parameters ---------- - *funcs: callable or iterable of callable + *funcs: callable(s) function(s) that are called on each render cycle pre_render: bool, default ``True``, optional keyword-only argument diff --git a/fastplotlib/layouts/_plot_area.py b/fastplotlib/layouts/_plot_area.py index 2c93d7e9e..299bc6e5d 100644 --- a/fastplotlib/layouts/_plot_area.py +++ b/fastplotlib/layouts/_plot_area.py @@ -1,5 +1,5 @@ from inspect import getfullargspec -from typing import * +from typing import TypeAlias, Literal, Union import weakref from warnings import warn @@ -9,7 +9,7 @@ from pylinalg import vec_transform, vec_unproject from wgpu.gui import WgpuCanvasBase -from ._utils import create_camera, create_controller +from ._utils import create_controller from ..graphics._base import Graphic from ..graphics.selectors._base_selector import BaseSelector from ..legends import Legend @@ -17,17 +17,18 @@ # dict to store Graphic instances # this is the only place where the real references to Graphics are stored in a Python session # {hex id str: Graphic} -GRAPHICS: Dict[str, Graphic] = dict() -SELECTORS: Dict[str, BaseSelector] = dict() +HexStr: TypeAlias = str +GRAPHICS: dict[HexStr, Graphic] = dict() +SELECTORS: dict[HexStr, BaseSelector] = dict() class PlotArea: def __init__( self, - parent, - position: Any, - camera: Union[pygfx.PerspectiveCamera], - controller: Union[pygfx.Controller], + parent: Union["PlotArea", "GridPlot"], + position: tuple[int, int] | str, + camera: pygfx.PerspectiveCamera, + controller: pygfx.Controller, scene: pygfx.Scene, canvas: WgpuCanvasBase, renderer: pygfx.WgpuRenderer, @@ -39,18 +40,18 @@ def __init__( Parameters ---------- - parent: PlotArea - parent class of subclasses will be a ``PlotArea`` instance + parent: PlotArea or GridPlot + parent object position: Any - typical use will be for ``subplots`` in a ``gridplot``, position would correspond to the ``[row, column]`` - location of the ``subplot`` in its ``gridplot`` + position of the plot area. In a ``subplot`` position would correspond to the ``[row, column]`` + index of the ``subplot``. In docks this would correspond to a str name, "top", "right", "bottom" or "left" camera: pygfx.PerspectiveCamera - Use perspective camera for both perspective and orthographic views. Set fov = 0 for orthographic mode. + Use perspective camera for both perspective and orthographic views. Set fov = 0 for orthographic projection controller: pygfx.Controller - One of the pygfx controllers, panzoom, fly, orbit, or trackball + One of the pygfx controllers: "panzoom", "fly", "trackball", "orbit" scene: pygfx.Scene represents the root of a scene graph, will be viewed by the given ``camera`` @@ -62,20 +63,17 @@ def __init__( renders the scene onto the canvas name: str, optional - name this ``subplot`` or ``plot`` + name this plot area """ - self._parent: PlotArea = parent + self._parent = parent self._position = position self._scene = scene self._canvas = canvas self._renderer = renderer - if parent is None: - self._viewport: pygfx.Viewport = pygfx.Viewport(renderer) - else: - self._viewport = pygfx.Viewport(parent.renderer) + self._viewport: pygfx.Viewport = pygfx.Viewport(renderer) self._camera = camera self._controller = controller @@ -85,18 +83,18 @@ def __init__( self.viewport, ) - self._animate_funcs_pre = list() - self._animate_funcs_post = list() + self._animate_funcs_pre: list[callable] = list() + self._animate_funcs_post: list[callable] = list() self.renderer.add_event_handler(self.set_viewport_rect, "resize") # list of hex id strings for all graphics managed by this PlotArea # the real Graphic instances are stored in the ``GRAPHICS`` dict - self._graphics: List[str] = list() + self._graphics: list[str] = list() # selectors are in their own list so they can be excluded from scene bbox calculations # managed similar to GRAPHICS for garbage collection etc. - self._selectors: List[str] = list() + self._selectors: list[str] = list() self._name = name @@ -108,11 +106,11 @@ def __init__( # several read-only properties @property def parent(self): - """A parent if relevant, used by individual Subplots in GridPlot""" + """A parent if relevant""" return self._parent @property - def position(self) -> Union[Tuple[int, int], Any]: + def position(self) -> tuple[int, int] | str: """Position of this plot area within a larger layout (such as GridPlot) if relevant""" return self._position @@ -142,7 +140,7 @@ def camera(self) -> pygfx.PerspectiveCamera: return self._camera @camera.setter - def camera(self, new_camera: Union[str, pygfx.PerspectiveCamera]): + def camera(self, new_camera: str | pygfx.PerspectiveCamera): # user wants to set completely new camera, remove current camera from controller if isinstance(new_camera, pygfx.PerspectiveCamera): self.controller.remove_camera(self._camera) @@ -178,7 +176,7 @@ def controller(self) -> pygfx.Controller: return self._controller @controller.setter - def controller(self, new_controller: Union[str, pygfx.Controller]): + def controller(self, new_controller: str | pygfx.Controller): new_controller = create_controller(new_controller, self._camera) cameras_list = list() @@ -206,7 +204,7 @@ def controller(self, new_controller: Union[str, pygfx.Controller]): self._controller = new_controller @property - def graphics(self) -> Tuple[Graphic, ...]: + def graphics(self) -> tuple[Graphic, ...]: """Graphics in the plot area. Always returns a proxy to the Graphic instances.""" proxies = list() for loc in self._graphics: @@ -218,7 +216,7 @@ def graphics(self) -> Tuple[Graphic, ...]: return tuple(proxies) @property - def selectors(self) -> Tuple[BaseSelector, ...]: + def selectors(self) -> tuple[BaseSelector, ...]: """Selectors in the plot area. Always returns a proxy to the Graphic instances.""" proxies = list() for loc in self._selectors: @@ -228,7 +226,7 @@ def selectors(self) -> Tuple[BaseSelector, ...]: return tuple(proxies) @property - def legends(self) -> Tuple[Legend, ...]: + def legends(self) -> tuple[Legend, ...]: """Legends in the plot area.""" proxies = list() for loc in self._graphics: @@ -253,7 +251,7 @@ def name(self, name: str): raise TypeError("PlotArea `name` must be of type ") self._name = name - def get_rect(self) -> Tuple[float, float, float, float]: + def get_rect(self) -> tuple[float, float, float, float]: """ Returns the viewport rect to define the rectangle occupied by the viewport w.r.t. the Canvas. @@ -267,7 +265,7 @@ def get_rect(self) -> Tuple[float, float, float, float]: raise NotImplementedError("Must be implemented in subclass") def map_screen_to_world( - self, pos: Union[Tuple[float, float], pygfx.PointerEvent] + self, pos: tuple[float, float] | pygfx.PointerEvent ) -> np.ndarray: """ Map screen position to world position @@ -316,7 +314,7 @@ def render(self): self._call_animate_functions(self._animate_funcs_post) - def _call_animate_functions(self, funcs: Iterable[callable]): + def _call_animate_functions(self, funcs: list[callable]): for fn in funcs: try: args = getfullargspec(fn).args @@ -337,7 +335,7 @@ def _call_animate_functions(self, funcs: Iterable[callable]): def add_animations( self, - *funcs: Iterable[callable], + *funcs: callable, pre_render: bool = True, post_render: bool = False, ): @@ -347,7 +345,7 @@ def add_animations( Parameters ---------- - *funcs: callable or iterable of callable + *funcs: callable(s) function(s) that are called on each render cycle pre_render: bool, default ``True``, optional keyword-only argument @@ -460,7 +458,7 @@ def _add_or_insert_graphic( self, graphic: Graphic, center: bool = True, - action: str = Union["insert", "add"], + action: str = Literal["insert", "add"], index: int = 0, ): """Private method to handle inserting or adding a graphic to a PlotArea.""" @@ -570,7 +568,7 @@ def center_scene(self, *, zoom: float = 1.35): def auto_scale( self, *, # since this is often used as an event handler, don't want to coerce maintain_aspect = True - maintain_aspect: Union[None, bool] = None, + maintain_aspect: None | bool = None, zoom: float = 0.8, ): """ @@ -650,6 +648,7 @@ def delete_graphic(self, graphic: Graphic): """ # TODO: proper gc of selectors, RAM is freed for regular graphics but not selectors # TODO: references to selectors must be lingering somewhere + # TODO: update March 2024, I think selectors are gc properly, should check # get location loc = graphic.loc @@ -718,7 +717,7 @@ def __getitem__(self, name: str): f"The current selectors are:\n {selector_names}" ) - def __contains__(self, item: Union[str, Graphic]): + def __contains__(self, item: str | Graphic): to_check = [*self.graphics, *self.selectors, *self.legends] if isinstance(item, Graphic): diff --git a/fastplotlib/layouts/_record_mixin.py b/fastplotlib/layouts/_record_mixin.py index e3bfdeba5..59a8e92e4 100644 --- a/fastplotlib/layouts/_record_mixin.py +++ b/fastplotlib/layouts/_record_mixin.py @@ -1,4 +1,3 @@ -from typing import * from pathlib import Path from multiprocessing import Queue, Process from time import time @@ -21,7 +20,7 @@ class VideoWriterAV(Process): def __init__( self, - path: Union[Path, str], + path: Path | str, queue: Queue, fps: int, width: int, @@ -115,7 +114,7 @@ def _record(self): def record_start( self, - path: Union[str, Path], + path: str | Path, fps: int = 25, codec: str = "mpeg4", pixel_format: str = "yuv420p", diff --git a/fastplotlib/layouts/_subplot.py b/fastplotlib/layouts/_subplot.py index 509840fa7..4b1e92c51 100644 --- a/fastplotlib/layouts/_subplot.py +++ b/fastplotlib/layouts/_subplot.py @@ -1,4 +1,4 @@ -from typing import * +from typing import Literal, Union import numpy as np @@ -9,18 +9,22 @@ from ..graphics import TextGraphic from ._utils import make_canvas_and_renderer, create_camera, create_controller from ._plot_area import PlotArea -from .graphic_methods_mixin import GraphicMethodsMixin +from ._graphic_methods_mixin import GraphicMethodsMixin class Subplot(PlotArea, GraphicMethodsMixin): def __init__( self, - parent: Any = None, - position: Tuple[int, int] = None, - parent_dims: Tuple[int, int] = None, - camera: Union[str, pygfx.PerspectiveCamera] = "2d", - controller: Union[str, pygfx.Controller] = None, - canvas: Union[str, WgpuCanvasBase, pygfx.Texture] = None, + parent: Union["GridPlot", None] = None, + position: tuple[int, int] = None, + parent_dims: tuple[int, int] = None, + camera: Literal["2d", "3d"] | pygfx.PerspectiveCamera = "2d", + controller: ( + Literal["panzoom", "fly", "trackball", "orbit"] | pygfx.Controller + ) = None, + canvas: ( + Literal["glfw", "jupyter", "qt", "wx"] | WgpuCanvasBase | pygfx.Texture + ) = None, renderer: pygfx.WgpuRenderer = None, name: str = None, ): @@ -33,7 +37,7 @@ def __init__( Parameters ---------- - parent: Any + parent: 'GridPlot' | None parent GridPlot instance position: (int, int), optional @@ -51,7 +55,7 @@ def __init__( | if ``str``, must be one of: `"panzoom", "fly", "trackball", or "orbit"`. | also accepts a pygfx.Controller instance - canvas: one of "jupyter", "glfw", "qt", WgpuCanvas, or pygfx.Texture, optional + canvas: one of "jupyter", "glfw", "qt", "ex, a WgpuCanvas, or a pygfx.Texture, optional Provides surface on which a scene will be rendered. Can optionally provide a WgpuCanvas instance or a str to force the PlotArea to use a specific canvas from one of the following options: "jupyter", "glfw", "qt". Can also provide a pygfx Texture to render to. @@ -113,11 +117,11 @@ def __init__( self.set_title(self.name) @property - def name(self) -> Any: + def name(self) -> str: return self._name @name.setter - def name(self, name: Any): + def name(self, name: str): self._name = name self.set_title(name) @@ -136,7 +140,7 @@ def docks(self) -> dict: """ return self._docks - def set_title(self, text: Any): + def set_title(self, text: str): """Sets the plot title, stored as a ``TextGraphic`` in the "top" dock area""" if text is None: return diff --git a/fastplotlib/layouts/_utils.py b/fastplotlib/layouts/_utils.py index 5ee930b67..6994838d5 100644 --- a/fastplotlib/layouts/_utils.py +++ b/fastplotlib/layouts/_utils.py @@ -1,15 +1,14 @@ -from typing import * import importlib import pygfx -from pygfx import WgpuRenderer, Texture +from pygfx import WgpuRenderer, Texture, Renderer from wgpu.gui import WgpuCanvasBase from ..utils import gui def make_canvas_and_renderer( - canvas: Union[str, WgpuCanvasBase, Texture, None], renderer: [WgpuRenderer, None] + canvas: str | WgpuCanvasBase | Texture | None, renderer: Renderer | None ): """ Parses arguments and returns the appropriate canvas and renderer instances @@ -22,19 +21,23 @@ def make_canvas_and_renderer( m = importlib.import_module("wgpu.gui." + canvas) canvas = m.WgpuCanvas(max_fps=60) elif not isinstance(canvas, (WgpuCanvasBase, Texture)): - raise ValueError( + raise TypeError( f"canvas option must either be a valid WgpuCanvas implementation, a pygfx Texture" f" or a str with the wgpu gui backend name." ) if renderer is None: renderer = WgpuRenderer(canvas, pixel_ratio=2) + elif not isinstance(renderer, Renderer): + raise TypeError( + f"renderer option must be a pygfx.Renderer instance such as pygfx.WgpuRenderer" + ) return canvas, renderer def create_camera( - camera_type: Union[pygfx.PerspectiveCamera, str], + camera_type: pygfx.PerspectiveCamera | str, ) -> pygfx.PerspectiveCamera: if isinstance(camera_type, pygfx.PerspectiveCamera): return camera_type @@ -61,7 +64,7 @@ def create_camera( def create_controller( - controller_type: Union[pygfx.Controller, None, str], + controller_type: pygfx.Controller | None | str, camera: pygfx.PerspectiveCamera, ) -> pygfx.Controller: """ diff --git a/fastplotlib/legends/legend.py b/fastplotlib/legends/legend.py index 291c25ff3..be90004aa 100644 --- a/fastplotlib/legends/legend.py +++ b/fastplotlib/legends/legend.py @@ -1,6 +1,6 @@ from functools import partial from collections import OrderedDict -from typing import * +from typing import Iterable import numpy as np import pygfx @@ -31,7 +31,7 @@ def __init__( class LineLegendItem(LegendItem): def __init__( - self, parent, graphic: LineGraphic, label: str, position: Tuple[int, int] + self, parent, graphic: LineGraphic, label: str, position: tuple[int, int] ): """ @@ -142,7 +142,7 @@ class Legend(Graphic): def __init__( self, plot_area, - highlight_color: Union[str, tuple, np.ndarray] = "w", + highlight_color: str | tuple | np.ndarray = "w", max_rows: int = 5, *args, **kwargs, @@ -161,7 +161,7 @@ def __init__( maximum number of rows allowed in the legend """ - self._graphics: List[Graphic] = list() + self._graphics: list[Graphic] = list() # hex id of Graphic, i.e. graphic.loc are the keys self._items: OrderedDict[str:LegendItem] = OrderedDict() @@ -204,7 +204,7 @@ def __init__( self._row_counter = 0 self._col_counter = 0 - def graphics(self) -> Tuple[Graphic, ...]: + def graphics(self) -> tuple[Graphic, ...]: return tuple(self._graphics) def _check_label_unique(self, label): @@ -235,7 +235,7 @@ def add_graphic(self, graphic: Graphic, label: str = None): # get x position offset for this new column of LegendItems # start by getting the LegendItems in the previous column - prev_column_items: List[LegendItem] = list(self._items.values())[ + prev_column_items: list[LegendItem] = list(self._items.values())[ -self._max_rows : ] # x position of LegendItems in previous column diff --git a/fastplotlib/utils/functions.py b/fastplotlib/utils/functions.py index 8d1e8694f..da781b521 100644 --- a/fastplotlib/utils/functions.py +++ b/fastplotlib/utils/functions.py @@ -1,5 +1,4 @@ from collections import OrderedDict -from typing import * from pathlib import Path import numpy as np @@ -165,7 +164,7 @@ def make_colors_dict(labels: iter, cmap: str, **kwargs) -> OrderedDict: return OrderedDict(zip(labels, colors)) -def quick_min_max(data: np.ndarray) -> Tuple[float, float]: +def quick_min_max(data: np.ndarray) -> tuple[float, float]: """ Adapted from pyqtgraph.ImageView. Estimate the min/max values of *data* by subsampling. @@ -220,7 +219,7 @@ def make_pygfx_colors(colors, n_colors): return colors_array -def calculate_gridshape(n_subplots: int) -> Tuple[int, int]: +def calculate_gridshape(n_subplots: int) -> tuple[int, int]: """ Returns ``(n_rows, n_cols)`` from given number of subplots ``n_subplots`` """ @@ -240,7 +239,7 @@ def normalize_min_max(a): def parse_cmap_values( n_colors: int, cmap_name: str, - cmap_values: Union[np.ndarray, List[Union[int, float]]] = None, + cmap_values: np.ndarray | list[int | float] = None, ) -> np.ndarray: """ diff --git a/fastplotlib/widgets/histogram_lut.py b/fastplotlib/widgets/histogram_lut.py index 31f6ab8e9..43f2b48b3 100644 --- a/fastplotlib/widgets/histogram_lut.py +++ b/fastplotlib/widgets/histogram_lut.py @@ -1,4 +1,3 @@ -from typing import * import weakref import numpy as np @@ -112,7 +111,7 @@ def __init__( self.image_graphic.cmap.add_event_handler(self._image_cmap_handler) - def _get_vmin_vmax_str(self) -> Tuple[str, str]: + def _get_vmin_vmax_str(self) -> tuple[str, str]: if self.vmin < 0.001 or self.vmin > 99_999: vmin_str = f"{self.vmin:.2e}" else: diff --git a/fastplotlib/utils/generate_add_methods.py b/scripts/generate_add_graphic_methods.py similarity index 97% rename from fastplotlib/utils/generate_add_methods.py rename to scripts/generate_add_graphic_methods.py index 100ad7757..2a480d884 100644 --- a/fastplotlib/utils/generate_add_methods.py +++ b/scripts/generate_add_graphic_methods.py @@ -4,7 +4,7 @@ import black root = pathlib.Path(__file__).parent.parent.resolve() -filename = root.joinpath("layouts/graphic_methods_mixin.py") +filename = root.joinpath("fastplotlib", "layouts", "_graphic_methods_mixin.py") # if there is an existing mixin class, replace it with an empty class # so that fastplotlib will import diff --git a/setup.py b/setup.py index e8f2613d9..b50a6a9bf 100644 --- a/setup.py +++ b/setup.py @@ -77,7 +77,7 @@ license="Apache 2.0", author="Kushal Kolar, Caitlin Lewis", author_email="", - python_requires=">=3.9", + python_requires=">=3.10", install_requires=install_requires, extras_require=extras_require, include_package_data=True, From 5ec3fb29946bdff1ade94654b5ec37d034f1b0aa Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Sun, 31 Mar 2024 05:35:08 -0400 Subject: [PATCH 058/185] revert setting pixel ratio (#466) --- fastplotlib/layouts/_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastplotlib/layouts/_utils.py b/fastplotlib/layouts/_utils.py index 6994838d5..85c35532c 100644 --- a/fastplotlib/layouts/_utils.py +++ b/fastplotlib/layouts/_utils.py @@ -27,7 +27,7 @@ def make_canvas_and_renderer( ) if renderer is None: - renderer = WgpuRenderer(canvas, pixel_ratio=2) + renderer = WgpuRenderer(canvas) elif not isinstance(renderer, Renderer): raise TypeError( f"renderer option must be a pygfx.Renderer instance such as pygfx.WgpuRenderer" From bc2de99d8af5d9639488e39cea0de04162378783 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Mon, 1 Apr 2024 23:59:04 -0400 Subject: [PATCH 059/185] add `References` object to `PlotArea`, other cleanup, better garbage collection (#467) * add References object to PlotArea, other cleanup * more cleanup * black * add refcount utility to References, fix Graphic.name setter bug * actually fix Graphic.name setter bug * more generalized event handler cleanup in Graphic * all gc works now * black --- examples/notebooks/test_gc.ipynb | 200 +++++++++++++++ fastplotlib/graphics/_base.py | 78 ++++-- fastplotlib/graphics/line.py | 2 +- fastplotlib/graphics/line_collection.py | 3 +- .../graphics/selectors/_base_selector.py | 39 +-- fastplotlib/graphics/selectors/_linear.py | 30 +-- .../graphics/selectors/_linear_region.py | 7 +- fastplotlib/graphics/selectors/_polygon.py | 2 +- fastplotlib/layouts/_plot_area.py | 240 +++++++++--------- fastplotlib/legends/legend.py | 12 +- fastplotlib/widgets/histogram_lut.py | 67 ++--- fastplotlib/widgets/image.py | 14 +- 12 files changed, 471 insertions(+), 223 deletions(-) create mode 100644 examples/notebooks/test_gc.ipynb diff --git a/examples/notebooks/test_gc.ipynb b/examples/notebooks/test_gc.ipynb new file mode 100644 index 000000000..6caf6a9e3 --- /dev/null +++ b/examples/notebooks/test_gc.ipynb @@ -0,0 +1,200 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "9dfba6cf-38af-4003-90b9-463c0cb1063f", + "metadata": {}, + "outputs": [], + "source": [ + "import fastplotlib as fpl\n", + "import numpy as np\n", + "import pytest" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7552eedc-3b9b-4682-8e3b-7d44e0e5510d", + "metadata": {}, + "outputs": [], + "source": [ + "def test_references(plot_objects):\n", + " for i in range(len(plot_objects)):\n", + " with pytest.raises(ReferenceError) as failure:\n", + " plot_objects[i]\n", + " pytest.fail(f\"GC failed for object: {objects[i]}\")" + ] + }, + { + "cell_type": "markdown", + "id": "948108e8-a4fa-4dc7-9953-a956428128cf", + "metadata": {}, + "source": [ + "# Add graphics and selectors, add feature event handlers, test gc occurs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3d96bf14-b484-455e-bcd7-5b2fe7b45fb4", + "metadata": {}, + "outputs": [], + "source": [ + "xs = np.linspace(0, 20 * np.pi, 1_000)\n", + "ys = np.sin(xs)\n", + "zs = np.zeros(xs.size)\n", + "\n", + "points_data = np.column_stack([xs, ys, zs])\n", + "\n", + "line_collection_data = [points_data[:, 1].copy() for i in range(10)]\n", + "\n", + "img_data = np.random.rand(2_000, 2_000)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "284b83e1-8cfc-4105-b7c2-6214137dab31", + "metadata": {}, + "outputs": [], + "source": [ + "gp = fpl.GridPlot((2, 2))\n", + "\n", + "line = gp[0, 0].add_line(points_data, name=\"line\")\n", + "scatter = gp[0, 1].add_scatter(points_data.copy(), name=\"scatter\")\n", + "line_stack = gp[1, 0].add_line_stack(line_collection_data, name=\"line-stack\")\n", + "image = gp[1, 1].add_image(img_data, name=\"image\")\n", + "\n", + "linear_sel = line.add_linear_selector(name=\"line_linear_sel\")\n", + "linear_region_sel = line.add_linear_region_selector(name=\"line_region_sel\")\n", + "\n", + "linear_sel2 = line_stack.add_linear_selector(name=\"line-stack_linear_sel\")\n", + "linear_region_sel2 = line_stack.add_linear_region_selector(name=\"line-stack_region_sel\")\n", + "\n", + "linear_sel_img = image.add_linear_selector(name=\"image_linear_sel\")\n", + "linear_region_sel_img = image.add_linear_region_selector(name=\"image_linear_region_sel\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb2083c1-f6b7-417c-86b8-9980819917db", + "metadata": {}, + "outputs": [], + "source": [ + "def feature_changed_handler(ev):\n", + " pass\n", + "\n", + "\n", + "objects = list()\n", + "for subplot in gp:\n", + " objects += subplot.objects\n", + "\n", + "\n", + "for g in objects:\n", + " for feature in g.feature_events:\n", + " if isinstance(g, fpl.LineCollection):\n", + " continue # skip collections for now\n", + " \n", + " f = getattr(g, feature)\n", + " f.add_event_handler(feature_changed_handler)\n", + "\n", + "gp.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ba9fffeb-45bd-4a0c-a941-e7c7e68f2e55", + "metadata": {}, + "outputs": [], + "source": [ + "gp.clear()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e33bf32d-b13a-474b-92ca-1d1e1c7b820b", + "metadata": {}, + "outputs": [], + "source": [ + "test_references(objects)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8078a7d2-9bc6-48a1-896c-7e169c5bbdcf", + "metadata": {}, + "outputs": [], + "source": [ + "movies = [np.random.rand(100, 100, 100) for i in range(6)]\n", + "\n", + "iw = fpl.ImageWidget(movies)\n", + "\n", + "# add some events onto all the image graphics\n", + "for g in iw.managed_graphics:\n", + " for f in g.feature_events:\n", + " fea = getattr(g, f)\n", + " fea.add_event_handler(feature_changed_handler)\n", + "\n", + "iw.show()" + ] + }, + { + "cell_type": "markdown", + "id": "189bcd7a-40a2-4e84-abcf-c334e50f5544", + "metadata": {}, + "source": [ + "# Test that setting new data with different dims clears old ImageGraphics" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "38557b63-997f-433a-b744-e562e30be6ae", + "metadata": {}, + "outputs": [], + "source": [ + "old_graphics = iw.managed_graphics\n", + "\n", + "new_movies = [np.random.rand(100, 200, 200) for i in range(6)]\n", + "\n", + "iw.set_data(new_movies)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "59e3c193-5672-4a66-bdca-12f1dd675d32", + "metadata": {}, + "outputs": [], + "source": [ + "test_references(old_graphics)" + ] + } + ], + "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.11.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/fastplotlib/graphics/_base.py b/fastplotlib/graphics/_base.py index 4442c851e..3a5b043f5 100644 --- a/fastplotlib/graphics/_base.py +++ b/fastplotlib/graphics/_base.py @@ -1,4 +1,4 @@ -from typing import Any, Literal +from typing import Any, Literal, TypeAlias import weakref from warnings import warn from abc import ABC, abstractmethod @@ -11,9 +11,12 @@ from ._features import GraphicFeature, PresentFeature, GraphicFeatureIndexable, Deleted + +HexStr: TypeAlias = str + # dict that holds all world objects for a given python kernel/session # Graphic objects only use proxies to WorldObjects -WORLD_OBJECTS: dict[str, WorldObject] = dict() #: {hex id str: WorldObject} +WORLD_OBJECTS: dict[HexStr, WorldObject] = dict() #: {hex id str: WorldObject} PYGFX_EVENTS = [ @@ -80,7 +83,7 @@ def __init__( self.present = PresentFeature(parent=self) # store hex id str of Graphic instance mem location - self.loc: str = hex(id(self)) + self._fpl_address: HexStr = hex(id(self)) self.deleted = Deleted(self, False) @@ -93,19 +96,25 @@ def name(self) -> str | None: @name.setter def name(self, name: str): + if self.name == name: + return + if not isinstance(name, str): raise TypeError("`Graphic` name must be of type ") + if self._plot_area is not None: self._plot_area._check_graphic_name_exists(name) + self._name = name + @property def world_object(self) -> WorldObject: """Associated pygfx WorldObject. Always returns a proxy, real object cannot be accessed directly.""" # We use weakref to simplify garbage collection - return weakref.proxy(WORLD_OBJECTS[hex(id(self))]) + return weakref.proxy(WORLD_OBJECTS[self._fpl_address]) def _set_world_object(self, wo: WorldObject): - WORLD_OBJECTS[hex(id(self))] = wo + WORLD_OBJECTS[self._fpl_address] = wo @property def position(self) -> np.ndarray: @@ -166,6 +175,9 @@ def children(self) -> list[WorldObject]: """Return the children of the WorldObject.""" return self.world_object.children + def _fpl_add_plot_area_hook(self, plot_area): + self._plot_area = plot_area + def __setattr__(self, key, value): if hasattr(self, key): attr = getattr(self, key) @@ -187,23 +199,52 @@ def __eq__(self, other): if not isinstance(other, Graphic): raise TypeError("`==` operator is only valid between two Graphics") - if self.loc == other.loc: + if self._fpl_address == other._fpl_address: return True return False - def _cleanup(self): + def _fpl_cleanup(self): """ Cleans up the graphic in preparation for __del__(), such as removing event handlers from plot renderer, feature event handlers, etc. Optionally implemented in subclasses """ - pass + # clear any attached event handlers and animation functions + for attr in dir(self): + try: + method = getattr(self, attr) + except: + continue + + if not callable(method): + continue + + for ev_type in PYGFX_EVENTS: + try: + self._plot_area.renderer.remove_event_handler(method, ev_type) + except (KeyError, TypeError): + pass + + try: + self._plot_area.remove_animation(method) + except KeyError: + pass + + for child in self.world_object.children: + child._event_handlers.clear() + + self.world_object._event_handlers.clear() + + feature_names = getattr(self, "feature_events") + for n in feature_names: + fea = getattr(self, n) + fea.clear_event_handlers() def __del__(self): self.deleted = True - del WORLD_OBJECTS[self.loc] + del WORLD_OBJECTS[self._fpl_address] def rotate(self, alpha: float, axis: Literal["x", "y", "z"] = "y"): """Rotate the Graphic with respect to the world. @@ -372,7 +413,7 @@ def _event_handler(self, event): else: # get index of world object that made this event for i, item in enumerate(self.graphics): - wo = WORLD_OBJECTS[item.loc] + wo = WORLD_OBJECTS[item._fpl_address] # we only store hex id of worldobject, but worldobject `pick_info` is always the real object # so if pygfx worldobject triggers an event by itself, such as `click`, etc., this will be # the real world object in the pick_info and not the proxy @@ -432,7 +473,8 @@ class PreviouslyModifiedData: indices: Any -COLLECTION_GRAPHICS: dict[str, Graphic] = dict() +# Dict that holds all collection graphics in one python instance +COLLECTION_GRAPHICS: dict[HexStr, Graphic] = dict() class GraphicCollection(Graphic): @@ -450,7 +492,7 @@ def graphics(self) -> np.ndarray[Graphic]: """The Graphics within this collection. Always returns a proxy to the Graphics.""" if self._graphics_changed: proxies = [ - weakref.proxy(COLLECTION_GRAPHICS[loc]) for loc in self._graphics + weakref.proxy(COLLECTION_GRAPHICS[addr]) for addr in self._graphics ] self._graphics_array = np.array(proxies) self._graphics_array.flags["WRITEABLE"] = False @@ -479,10 +521,10 @@ def add_graphic(self, graphic: Graphic, reset_index: False): f"you are trying to add a {graphic.__class__.__name__}." ) - loc = hex(id(graphic)) - COLLECTION_GRAPHICS[loc] = graphic + addr = graphic._fpl_address + COLLECTION_GRAPHICS[addr] = graphic - self._graphics.append(loc) + self._graphics.append(addr) if reset_index: self._reset_index() @@ -507,7 +549,7 @@ def remove_graphic(self, graphic: Graphic, reset_index: True): """ - self._graphics.remove(graphic.loc) + self._graphics.remove(graphic._fpl_address) if reset_index: self._reset_index() @@ -525,8 +567,8 @@ def __getitem__(self, key): def __del__(self): self.world_object.clear() - for loc in self._graphics: - del COLLECTION_GRAPHICS[loc] + for addr in self._graphics: + del COLLECTION_GRAPHICS[addr] super().__del__() diff --git a/fastplotlib/graphics/line.py b/fastplotlib/graphics/line.py index f44347a58..cfb697dff 100644 --- a/fastplotlib/graphics/line.py +++ b/fastplotlib/graphics/line.py @@ -278,7 +278,7 @@ def _get_linear_selector_init_args(self, padding: float, **kwargs): return bounds_init, limits, size, origin, axis, end_points - def _add_plot_area_hook(self, plot_area): + def _fpl_add_plot_area_hook(self, plot_area): self._plot_area = plot_area def set_feature(self, feature: str, new_data: Any, indices: Any = None): diff --git a/fastplotlib/graphics/line_collection.py b/fastplotlib/graphics/line_collection.py index 8488ec15e..1c2e151e8 100644 --- a/fastplotlib/graphics/line_collection.py +++ b/fastplotlib/graphics/line_collection.py @@ -15,7 +15,6 @@ class LineCollection(GraphicCollection, Interaction): child_type = LineGraphic.__name__ - feature_events = {"data", "colors", "cmap", "thickness", "present"} def __init__( self, @@ -416,7 +415,7 @@ def _get_linear_selector_init_args(self, padding, **kwargs): return bounds, limits, size, origin, axis, end_points - def _add_plot_area_hook(self, plot_area): + def _fpl_add_plot_area_hook(self, plot_area): self._plot_area = plot_area def set_feature(self, feature: str, new_data: Any, indices: Any): diff --git a/fastplotlib/graphics/selectors/_base_selector.py b/fastplotlib/graphics/selectors/_base_selector.py index 6c1f8c6ae..f20eba4a0 100644 --- a/fastplotlib/graphics/selectors/_base_selector.py +++ b/fastplotlib/graphics/selectors/_base_selector.py @@ -1,6 +1,7 @@ from typing import * from dataclasses import dataclass from functools import partial +import weakref import numpy as np @@ -123,7 +124,7 @@ def _get_source(self, graphic): return source - def _add_plot_area_hook(self, plot_area): + def _fpl_add_plot_area_hook(self, plot_area): self._plot_area = plot_area # when the pointer is pressed on a fill, edge or vertex @@ -136,8 +137,10 @@ def _add_plot_area_hook(self, plot_area): for fill in self._fill: if fill.material.color_is_transparent: - pfunc_fill = partial(self._check_fill_pointer_event, fill) - self._plot_area.renderer.add_event_handler(pfunc_fill, "pointer_down") + self._pfunc_fill = partial(self._check_fill_pointer_event, fill) + self._plot_area.renderer.add_event_handler( + self._pfunc_fill, "pointer_down" + ) # when the pointer moves self._plot_area.renderer.add_event_handler(self._move, "pointer_move") @@ -356,26 +359,10 @@ def _key_up(self, ev): self._move_info = None - def _cleanup(self): - """ - Cleanup plot renderer event handlers etc. - """ - self._plot_area.renderer.remove_event_handler(self._move, "pointer_move") - self._plot_area.renderer.remove_event_handler(self._move_end, "pointer_up") - self._plot_area.renderer.remove_event_handler(self._move_to_pointer, "click") - - self._plot_area.renderer.remove_event_handler(self._key_down, "key_down") - self._plot_area.renderer.remove_event_handler(self._key_up, "key_up") - - # remove animation func - self._plot_area.remove_animation(self._key_hold) - - # clear wo event handlers - for wo in self._world_objects: - wo._event_handlers.clear() - - if hasattr(self, "feature_events"): - feature_names = getattr(self, "feature_events") - for n in feature_names: - fea = getattr(self, n) - fea.clear_event_handlers() + def _fpl_cleanup(self): + if hasattr(self, "_pfunc_fill"): + self._plot_area.renderer.remove_event_handler( + self._pfunc_fill, "pointer_down" + ) + del self._pfunc_fill + super()._fpl_cleanup() diff --git a/fastplotlib/graphics/selectors/_linear.py b/fastplotlib/graphics/selectors/_linear.py index 886ccbaaf..4b77a6cd9 100644 --- a/fastplotlib/graphics/selectors/_linear.py +++ b/fastplotlib/graphics/selectors/_linear.py @@ -140,18 +140,6 @@ def __init__( world_object.add(self.line_outer) world_object.add(line_inner) - self._set_world_object(world_object) - - # set x or y position - if axis == "x": - self.position_x = selection - else: - self.position_y = selection - - self.selection = LinearSelectionFeature( - self, axis=axis, value=selection, limits=self._limits - ) - self._move_info: dict = None self.parent = parent @@ -170,6 +158,14 @@ def __init__( name=name, ) + self._set_world_object(world_object) + + self.selection = LinearSelectionFeature( + self, axis=axis, value=selection, limits=self._limits + ) + + self.selection = selection + def _setup_ipywidget_slider(self, widget): # setup an ipywidget slider with bidirectional callbacks to this LinearSelector value = self.selection() @@ -208,8 +204,8 @@ def _ipywidget_callback(self, change): self.selection = change["new"] - def _add_plot_area_hook(self, plot_area): - super()._add_plot_area_hook(plot_area=plot_area) + def _fpl_add_plot_area_hook(self, plot_area): + super()._fpl_add_plot_area_hook(plot_area=plot_area) # resize the slider widgets when the canvas is resized self._plot_area.renderer.add_event_handler(self._set_slider_layout, "resize") @@ -375,10 +371,8 @@ def _move_graphic(self, delta: np.ndarray): else: self.selection = self.selection() + delta[1] - def _cleanup(self): - super()._cleanup() - + def _fpl_cleanup(self): for widget in self._handled_widgets: widget.unobserve(self._ipywidget_callback, "value") - self._plot_area.renderer.remove_event_handler(self._set_slider_layout, "resize") + super()._fpl_cleanup() diff --git a/fastplotlib/graphics/selectors/_linear_region.py b/fastplotlib/graphics/selectors/_linear_region.py index b88174ddb..47191bfb1 100644 --- a/fastplotlib/graphics/selectors/_linear_region.py +++ b/fastplotlib/graphics/selectors/_linear_region.py @@ -135,7 +135,6 @@ def __init__( # basic mesh for the fill area of the selector # line for each edge of the selector group = pygfx.Group() - self._set_world_object(group) if axis == "x": mesh = pygfx.Mesh( @@ -155,7 +154,7 @@ def __init__( self.fill = mesh self.fill.world.position = (*origin, -2) - self.world_object.add(self.fill) + group.add(self.fill) self._resizable = resizable @@ -223,7 +222,7 @@ def __init__( # add the edge lines for edge in self.edges: edge.world.z = -1 - self.world_object.add(edge) + group.add(edge) # set the initial bounds of the selector self.selection = LinearRegionSelectionFeature( @@ -244,6 +243,8 @@ def __init__( name=name, ) + self._set_world_object(group) + def get_selected_data( self, graphic: Graphic = None ) -> Union[np.ndarray, List[np.ndarray], None]: diff --git a/fastplotlib/graphics/selectors/_polygon.py b/fastplotlib/graphics/selectors/_polygon.py index 44d378329..3d2ee98fd 100644 --- a/fastplotlib/graphics/selectors/_polygon.py +++ b/fastplotlib/graphics/selectors/_polygon.py @@ -39,7 +39,7 @@ def get_vertices(self) -> np.ndarray: return np.vstack(vertices) - def _add_plot_area_hook(self, plot_area): + def _fpl_add_plot_area_hook(self, plot_area): self._plot_area = plot_area # click to add new segment diff --git a/fastplotlib/layouts/_plot_area.py b/fastplotlib/layouts/_plot_area.py index 299bc6e5d..37a25bbcc 100644 --- a/fastplotlib/layouts/_plot_area.py +++ b/fastplotlib/layouts/_plot_area.py @@ -1,4 +1,5 @@ from inspect import getfullargspec +from sys import getrefcount from typing import TypeAlias, Literal, Union import weakref from warnings import warn @@ -14,15 +15,81 @@ from ..graphics.selectors._base_selector import BaseSelector from ..legends import Legend -# dict to store Graphic instances -# this is the only place where the real references to Graphics are stored in a Python session -# {hex id str: Graphic} + HexStr: TypeAlias = str -GRAPHICS: dict[HexStr, Graphic] = dict() -SELECTORS: dict[HexStr, BaseSelector] = dict() + + +class References: + """ + This is the only place where the real graphic objects are stored. Everywhere else gets a proxy. + """ + + _graphics: dict[HexStr, Graphic] = dict() + _selectors: dict[HexStr, BaseSelector] = dict() + _legends: dict[HexStr, Legend] = dict() + + def add(self, graphic: Graphic | BaseSelector | Legend): + """Adds the real graphic to the dict""" + addr = graphic._fpl_address + + if isinstance(graphic, BaseSelector): + self._selectors[addr] = graphic + + elif isinstance(graphic, Legend): + self._legends[addr] = graphic + + elif isinstance(graphic, Graphic): + self._graphics[addr] = graphic + + else: + raise TypeError("Can only add Graphic, Selector or Legend types") + + def remove(self, address): + if address in self._graphics.keys(): + del self._graphics[address] + elif address in self._selectors.keys(): + del self._selectors[address] + elif address in self._legends.keys(): + del self._legends[address] + else: + raise KeyError(f"graphic with address not found: {address}") + + def get_proxies(self, refs: list[HexStr]) -> tuple[weakref.proxy]: + proxies = list() + for key in refs: + if key in self._graphics.keys(): + proxies.append(weakref.proxy(self._graphics[key])) + + elif key in self._selectors.keys(): + proxies.append(weakref.proxy(self._selectors[key])) + + elif key in self._legends.keys(): + proxies.append(weakref.proxy(self._legends[key])) + + else: + raise KeyError(f"graphic object with address not found: {key}") + + return tuple(proxies) + + def get_refcounts(self) -> dict[HexStr:int]: + counts = dict() + + for item in (self._graphics, self._selectors, self._legends): + for k in item.keys(): + counts[(k, item[k].name, item[k].__class__.__name__)] = getrefcount( + item[k] + ) + + return counts + + +REFERENCES = References() class PlotArea: + def get_refcounts(self): + return REFERENCES.get_refcounts() + def __init__( self, parent: Union["PlotArea", "GridPlot"], @@ -89,12 +156,15 @@ def __init__( self.renderer.add_event_handler(self.set_viewport_rect, "resize") # list of hex id strings for all graphics managed by this PlotArea - # the real Graphic instances are stored in the ``GRAPHICS`` dict - self._graphics: list[str] = list() + # the real Graphic instances are managed by REFERENCES + self._graphics: list[HexStr] = list() # selectors are in their own list so they can be excluded from scene bbox calculations # managed similar to GRAPHICS for garbage collection etc. - self._selectors: list[str] = list() + self._selectors: list[HexStr] = list() + + # legends, managed just like other graphics as explained above + self._legends: list[HexStr] = list() self._name = name @@ -206,35 +276,21 @@ def controller(self, new_controller: str | pygfx.Controller): @property def graphics(self) -> tuple[Graphic, ...]: """Graphics in the plot area. Always returns a proxy to the Graphic instances.""" - proxies = list() - for loc in self._graphics: - p = weakref.proxy(GRAPHICS[loc]) - if p.__class__.__name__ == "Legend": - continue - proxies.append(p) - - return tuple(proxies) + return REFERENCES.get_proxies(self._graphics) @property def selectors(self) -> tuple[BaseSelector, ...]: """Selectors in the plot area. Always returns a proxy to the Graphic instances.""" - proxies = list() - for loc in self._selectors: - p = weakref.proxy(SELECTORS[loc]) - proxies.append(p) - - return tuple(proxies) + return REFERENCES.get_proxies(self._selectors) @property def legends(self) -> tuple[Legend, ...]: """Legends in the plot area.""" - proxies = list() - for loc in self._graphics: - p = weakref.proxy(GRAPHICS[loc]) - if p.__class__.__name__ == "Legend": - proxies.append(p) + return REFERENCES.get_proxies(self._legends) - return tuple(proxies) + @property + def objects(self) -> tuple[Graphic | BaseSelector | Legend, ...]: + return *self.graphics, *self.selectors, *self.legends @property def name(self) -> str: @@ -470,28 +526,28 @@ def _add_or_insert_graphic( if graphic.name is not None: # skip for those that have no name self._check_graphic_name_exists(graphic.name) + addr = graphic._fpl_address + if isinstance(graphic, BaseSelector): - # store in SELECTORS dict - loc = graphic.loc - SELECTORS[loc] = ( - graphic # add hex id string for referencing this graphic instance - ) - # don't manage garbage collection of LineSliders for now - if action == "insert": - self._selectors.insert(index, loc) - else: - self._selectors.append(loc) + addr_list = self._selectors + + elif isinstance(graphic, Legend): + addr_list = self._legends + + elif isinstance(graphic, Graphic): + addr_list = self._graphics + else: - # store in GRAPHICS dict - loc = graphic.loc - GRAPHICS[loc] = ( - graphic # add hex id string for referencing this graphic instance - ) + raise TypeError("graphic must be of type Graphic | BaseSelector | Legend") - if action == "insert": - self._graphics.insert(index, loc) - else: - self._graphics.append(loc) + if action == "insert": + addr_list.insert(index, addr) + elif action == "add": + addr_list.append(addr) + else: + raise ValueError("valid actions are 'insert' | 'add'") + + REFERENCES.add(graphic) # now that it's in the dict, just use the weakref graphic = weakref.proxy(graphic) @@ -503,24 +559,13 @@ def _add_or_insert_graphic( self.center_graphic(graphic) # if we don't use the weakref above, then the object lingers if a plot hook is used! - if hasattr(graphic, "_add_plot_area_hook"): - graphic._add_plot_area_hook(self) + graphic._fpl_add_plot_area_hook(self) def _check_graphic_name_exists(self, name): - graphic_names = list() - - for g in self.graphics: - graphic_names.append(g.name) - - for s in self.selectors: - graphic_names.append(s.name) - - for l in self.legends: - graphic_names.append(l.name) - - if name in graphic_names: + if name in self: raise ValueError( - f"graphics must have unique names, current graphic names are:\n {graphic_names}" + f"Graphic with given name already exists in subplot or plot area. " + f"All graphics within a subplot or plot area must have a unique name." ) def center_graphic(self, graphic: Graphic, zoom: float = 1.35): @@ -649,85 +694,50 @@ def delete_graphic(self, graphic: Graphic): # TODO: proper gc of selectors, RAM is freed for regular graphics but not selectors # TODO: references to selectors must be lingering somewhere # TODO: update March 2024, I think selectors are gc properly, should check - # get location - loc = graphic.loc - - # check which dict it's in - if loc in self._graphics: - glist = self._graphics - kind = "graphic" - elif loc in self._selectors: - kind = "selector" - glist = self._selectors - else: - raise KeyError( - f"Graphic with following address not found in plot area: {loc}" - ) + # get memory address + address = graphic._fpl_address + + if graphic not in self: + raise KeyError(f"Graphic not found in plot area: {graphic}") + + # check which type it is + for l in [self._graphics, self._selectors, self._legends]: + if address in l: + l.remove(address) + break # remove from scene if necessary if graphic.world_object in self.scene.children: self.scene.remove(graphic.world_object) - # remove from list of addresses - glist.remove(loc) - # cleanup - graphic._cleanup() + graphic._fpl_cleanup() - if kind == "graphic": - del GRAPHICS[loc] - elif kind == "selector": - del SELECTORS[loc] + REFERENCES.remove(address) def clear(self): """ Clear the Plot or Subplot. Also performs garbage collection, i.e. runs ``delete_graphic`` on all graphics. """ - - for g in self.graphics: + for g in self.objects: self.delete_graphic(g) - for s in self.selectors: - self.delete_graphic(s) - def __getitem__(self, name: str): - for graphic in self.graphics: + for graphic in self.objects: if graphic.name == name: return graphic - for selector in self.selectors: - if selector.name == name: - return selector - - for legend in self.legends: - if legend.name == name: - return legend - - graphic_names = list() - for g in self.graphics: - graphic_names.append(g.name) - - selector_names = list() - for s in self.selectors: - selector_names.append(s.name) - - raise IndexError( - f"No graphic or selector of given name.\n" - f"The current graphics are:\n {graphic_names}\n" - f"The current selectors are:\n {selector_names}" - ) + raise IndexError(f"No graphic or selector of given name in plot area.\n") def __contains__(self, item: str | Graphic): - to_check = [*self.graphics, *self.selectors, *self.legends] - if isinstance(item, Graphic): - if item in to_check: + if item in self.objects: return True else: return False elif isinstance(item, str): - for graphic in to_check: + for graphic in self.objects: # only check named graphics if graphic.name is None: continue diff --git a/fastplotlib/legends/legend.py b/fastplotlib/legends/legend.py index be90004aa..b7e55f321 100644 --- a/fastplotlib/legends/legend.py +++ b/fastplotlib/legends/legend.py @@ -163,7 +163,7 @@ def __init__( """ self._graphics: list[Graphic] = list() - # hex id of Graphic, i.e. graphic.loc are the keys + # hex id of Graphic, i.e. graphic._fpl_address are the keys self._items: OrderedDict[str:LegendItem] = OrderedDict() super().__init__(*args, **kwargs) @@ -218,7 +218,7 @@ def _check_label_unique(self, label): def add_graphic(self, graphic: Graphic, label: str = None): if graphic in self._graphics: raise KeyError( - f"Graphic already exists in legend with label: '{self._items[graphic.loc].label}'" + f"Graphic already exists in legend with label: '{self._items[graphic._fpl_address].label}'" ) self._check_label_unique(label) @@ -268,7 +268,7 @@ def add_graphic(self, graphic: Graphic, label: str = None): self._reset_mesh_dims() self._graphics.append(graphic) - self._items[graphic.loc] = legend_item + self._items[graphic._fpl_address] = legend_item graphic.deleted.add_event_handler(partial(self.remove_graphic, graphic)) @@ -288,7 +288,7 @@ def _reset_mesh_dims(self): def remove_graphic(self, graphic: Graphic): self._graphics.remove(graphic) - legend_item = self._items.pop(graphic.loc) + legend_item = self._items.pop(graphic._fpl_address) self._legend_items_group.remove(legend_item.world_object) self._reset_item_positions() @@ -350,7 +350,7 @@ def __getitem__(self, graphic: Graphic) -> LegendItem: if not isinstance(graphic, Graphic): raise TypeError("Must index Legend with Graphics") - if graphic.loc not in self._items.keys(): + if graphic._fpl_address not in self._items.keys(): raise KeyError("Graphic not in legend") - return self._items[graphic.loc] + return self._items[graphic._fpl_address] diff --git a/fastplotlib/widgets/histogram_lut.py b/fastplotlib/widgets/histogram_lut.py index 43f2b48b3..67af972b8 100644 --- a/fastplotlib/widgets/histogram_lut.py +++ b/fastplotlib/widgets/histogram_lut.py @@ -44,14 +44,14 @@ def __init__( line_data = np.column_stack([hist_scaled, edges_flanked]) - self.line = LineGraphic(line_data) + self._histogram_line = LineGraphic(line_data) bounds = (edges[0], edges[-1]) limits = (edges_flanked[0], edges_flanked[-1]) size = 120 # since it's scaled to 100 origin = (hist_scaled.max() / 2, 0) - self.linear_region = LinearRegionSelector( + self._linear_region_selector = LinearRegionSelector( bounds=bounds, limits=limits, size=size, @@ -61,7 +61,7 @@ def __init__( ) # there will be a small difference with the histogram edges so this makes them both line up exactly - self.linear_region.selection = ( + self._linear_region_selector.selection = ( image_graphic.cmap.vmin, image_graphic.cmap.vmax, ) @@ -91,8 +91,8 @@ def __init__( widget_wo = Group() widget_wo.add( - self.line.world_object, - self.linear_region.world_object, + self._histogram_line.world_object, + self._linear_region_selector.world_object, self._text_vmin.world_object, self._text_vmax.world_object, ) @@ -102,12 +102,14 @@ def __init__( self.world_object.local.scale_x *= -1 self._text_vmin.position_x = -120 - self._text_vmin.position_y = self.linear_region.selection()[0] + self._text_vmin.position_y = self._linear_region_selector.selection()[0] self._text_vmax.position_x = -120 - self._text_vmax.position_y = self.linear_region.selection()[1] + self._text_vmax.position_y = self._linear_region_selector.selection()[1] - self.linear_region.selection.add_event_handler(self._linear_region_handler) + self._linear_region_selector.selection.add_event_handler( + self._linear_region_handler + ) self.image_graphic.cmap.add_event_handler(self._image_cmap_handler) @@ -124,10 +126,10 @@ def _get_vmin_vmax_str(self) -> tuple[str, str]: return vmin_str, vmax_str - def _add_plot_area_hook(self, plot_area): + def _fpl_add_plot_area_hook(self, plot_area): self._plot_area = plot_area - self.linear_region._add_plot_area_hook(plot_area) - self.line._add_plot_area_hook(plot_area) + self._linear_region_selector._fpl_add_plot_area_hook(plot_area) + self._histogram_line._fpl_add_plot_area_hook(plot_area) self._plot_area.auto_scale() @@ -192,7 +194,7 @@ def _calculate_histogram(self, data): def _linear_region_handler(self, ev): # must use world coordinate values directly from selection() # otherwise the linear region bounds jump to the closest bin edges - vmin, vmax = self.linear_region.selection() + vmin, vmax = self._linear_region_selector.selection() vmin, vmax = vmin / self._scale_factor, vmax / self._scale_factor self.vmin, self.vmax = vmin, vmax @@ -201,7 +203,7 @@ def _image_cmap_handler(self, ev): def _block_events(self, b: bool): self.image_graphic.cmap.block_events(b) - self.linear_region.selection.block_events(b) + self._linear_region_selector.selection.block_events(b) @property def vmin(self) -> float: @@ -213,9 +215,9 @@ def vmin(self, value: float): # must use world coordinate values directly from selection() # otherwise the linear region bounds jump to the closest bin edges - self.linear_region.selection = ( + self._linear_region_selector.selection = ( value * self._scale_factor, - self.linear_region.selection()[1], + self._linear_region_selector.selection()[1], ) self.image_graphic.cmap.vmin = value @@ -224,7 +226,7 @@ def vmin(self, value: float): self._vmin = value vmin_str, vmax_str = self._get_vmin_vmax_str() - self._text_vmin.position_y = self.linear_region.selection()[0] + self._text_vmin.position_y = self._linear_region_selector.selection()[0] self._text_vmin.text = vmin_str @property @@ -237,8 +239,8 @@ def vmax(self, value: float): # must use world coordinate values directly from selection() # otherwise the linear region bounds jump to the closest bin edges - self.linear_region.selection = ( - self.linear_region.selection()[0], + self._linear_region_selector.selection = ( + self._linear_region_selector.selection()[0], value * self._scale_factor, ) self.image_graphic.cmap.vmax = value @@ -248,7 +250,7 @@ def vmax(self, value: float): self._vmax = value vmin_str, vmax_str = self._get_vmin_vmax_str() - self._text_vmax.position_y = self.linear_region.selection()[1] + self._text_vmax.position_y = self._linear_region_selector.selection()[1] self._text_vmax.text = vmax_str def set_data(self, data, reset_vmin_vmax: bool = True): @@ -256,7 +258,7 @@ def set_data(self, data, reset_vmin_vmax: bool = True): line_data = np.column_stack([hist_scaled, edges_flanked]) - self.line.data = line_data + self._histogram_line.data = line_data bounds = (edges[0], edges[-1]) limits = (edges_flanked[0], edges_flanked[-11]) @@ -265,12 +267,12 @@ def set_data(self, data, reset_vmin_vmax: bool = True): if reset_vmin_vmax: # reset according to the new data - self.linear_region.limits = limits - self.linear_region.selection = bounds + self._linear_region_selector.limits = limits + self._linear_region_selector.selection = bounds else: # don't change the current selection self._block_events(True) - self.linear_region.limits = limits + self._linear_region_selector.limits = limits self._block_events(False) self._data = weakref.proxy(data) @@ -289,14 +291,21 @@ def image_graphic(self, graphic): f"HistogramLUT can only use ImageGraphic types, you have passed: {type(graphic)}" ) - # cleanup events from current image graphic - self._image_graphic.cmap.remove_event_handler(self._image_cmap_handler) + if self._image_graphic is not None: + # cleanup events from current image graphic + self._image_graphic.cmap.remove_event_handler(self._image_cmap_handler) self._image_graphic = graphic self.image_graphic.cmap.add_event_handler(self._image_cmap_handler) - def _cleanup(self): - self.linear_region._cleanup() - del self.line - del self.linear_region + def disconnect_image_graphic(self): + self._image_graphic.cmap.remove_event_handler(self._image_cmap_handler) + del self._image_graphic + # self._image_graphic = None + + def _fpl_cleanup(self): + self._linear_region_selector._fpl_cleanup() + self._histogram_line._fpl_cleanup() + del self._histogram_line + del self._linear_region_selector diff --git a/fastplotlib/widgets/image.py b/fastplotlib/widgets/image.py index 9412f7cc5..acef26a7d 100644 --- a/fastplotlib/widgets/image.py +++ b/fastplotlib/widgets/image.py @@ -875,17 +875,23 @@ def set_data( self._data[i] = new_array if old_data_shape != new_array.shape[-2:]: - # delete graphics at index zero - subplot.delete_graphic(graphic=subplot["image_widget_managed"]) - # insert new graphic at index zero + # make a new graphic with the new xy dims frame = self._process_indices( new_array, slice_indices=self._current_index ) frame = self._process_frame_apply(frame, i) + + # make new graphic first new_graphic = ImageGraphic(data=frame, name="image_widget_managed") - subplot.insert_graphic(graphic=new_graphic) + + # set hlut tool to use new graphic subplot.docks["right"]["histogram_lut"].image_graphic = new_graphic + # delete old graphic after setting hlut tool to new graphic + # this ensures gc + subplot.delete_graphic(graphic=subplot["image_widget_managed"]) + subplot.insert_graphic(graphic=new_graphic) + if new_array.ndim > 2: # to set max of time slider, txy or tzxy max_lengths["t"] = min(max_lengths["t"], new_array.shape[0] - 1) From 418a73b2aef82043af004f392faf6b4d2c577deb Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Sun, 7 Apr 2024 06:44:56 -0400 Subject: [PATCH 060/185] gridplot controllers kwarg is back, other improvements to gp (#477) * gridplot controllers kwarg is back, other improvements to gp * gridplot manipulation tests * black * done wtih gp tests * move non example tests to new tests dir * force offscreen canvas * force offscreen canvas with env var for now --- .github/workflows/ci.yml | 2 + fastplotlib/layouts/_gridplot.py | 327 ++++++++++++++++++++----------- tests/test_gridplot.py | 164 ++++++++++++++++ 3 files changed, 375 insertions(+), 118 deletions(-) create mode 100644 tests/test_gridplot.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 53e88bdf8..7db694d01 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -90,6 +90,7 @@ jobs: env: PYGFX_EXPECT_LAVAPIPE: true run: | + WGPU_FORCE_OFFSCREEN=1 pytest -v tests/ pytest -v examples FASTPLOTLIB_NB_TESTS=1 pytest --nbmake examples/notebooks/ - uses: actions/upload-artifact@v3 @@ -145,6 +146,7 @@ jobs: env: PYGFX_EXPECT_LAVAPIPE: true run: | + WGPU_FORCE_OFFSCREEN=1 pytest -v tests/ pytest -v examples - uses: actions/upload-artifact@v3 if: ${{ failure() }} diff --git a/fastplotlib/layouts/_gridplot.py b/fastplotlib/layouts/_gridplot.py index 5f7f3086d..472d3dd2e 100644 --- a/fastplotlib/layouts/_gridplot.py +++ b/fastplotlib/layouts/_gridplot.py @@ -1,6 +1,6 @@ from itertools import product, chain import numpy as np -from typing import Literal +from typing import Literal, Iterable from inspect import getfullargspec from warnings import warn @@ -21,16 +21,21 @@ def __init__( shape: tuple[int, int], cameras: ( Literal["2d", "3d"] - | list[Literal["2d", "3d"]] - | list[pygfx.PerspectiveCamera] - | np.ndarray + | Iterable[Iterable[Literal["2d", "3d"]]] + | pygfx.PerspectiveCamera + | Iterable[Iterable[pygfx.PerspectiveCamera]] ) = "2d", controller_types: ( - Literal["panzoom", "fly", "trackball", "orbit"] - | list[Literal["panzoom", "fly", "trackball", "orbit"]] - | np.ndarray + Iterable[Iterable[Literal["panzoom", "fly", "trackball", "orbit"]]] + | Iterable[Literal["panzoom", "fly", "trackball", "orbit"]] ) = None, - controller_ids: str | list[int] | np.ndarray | list[list[str]] = None, + controller_ids: ( + Literal["sync"] + | Iterable[int] + | Iterable[Iterable[int]] + | Iterable[Iterable[str]] + ) = None, + controllers: pygfx.Controller | Iterable[Iterable[pygfx.Controller]] = None, canvas: str | WgpuCanvasBase | pygfx.Texture = None, renderer: pygfx.WgpuRenderer = None, size: tuple[int, int] = (500, 300), @@ -44,19 +49,18 @@ def __init__( shape: (int, int) (n_rows, n_cols) - cameras: "2d", "3", list of "2d" | "3d", list of camera instances, or np.ndarray of "2d" | "3d", optional + cameras: "2d", "3", list of "2d" | "3d", Iterable of camera instances, or Iterable of "2d" | "3d", optional | if str, one of ``"2d"`` or ``"3d"`` indicating 2D or 3D cameras for all subplots - | list/array of ``2d`` and/or ``3d`` that specifies the camera type for each subplot - | list/array of pygfx.PerspectiveCamera instances + | Iterable/list/array of ``2d`` and/or ``3d`` that specifies the camera type for each subplot + | Iterable/list/array of pygfx.PerspectiveCamera instances - controller_types: str, list or np.ndarray, optional - list or array that specifies the controller type for each subplot, or list/array of - pygfx.Controller instances. Valid controller types: "panzoom", "fly", "trackball", "orbit". + controller_types: str, Iterable, optional + list/array that specifies the controller type for each subplot. + Valid controller types: "panzoom", "fly", "trackball", "orbit". If not specified a default controller is chosen based on the camera type. Orthographic projections, i.e. "2d" cameras, use a "panzoom" controller by default. Perspective projections with a FOV > 0, i.e. "3d" cameras, use a "fly" controller by default. - controller_ids: str, list of int, np.ndarray of int, or list with sublists of subplot str names, optional | If `None` a unique controller is created for each subplot | If "sync" all the subplots use the same controller @@ -70,6 +74,11 @@ def __init__( | list of lists of subplot names, each sublist is synced: [[subplot_a, subplot_b, subplot_e], [subplot_c, subplot_d]] | this syncs subplot_a, subplot_b and subplot_e together; syncs subplot_c and subplot_d together + controllers: pygfx.Controller | list[pygfx.Controller] | np.ndarray[pygfx.Controller], optional + directly provide pygfx.Controller instances(s). Useful if you want to use a controller from an existing + plot/subplot. Other controller kwargs, i.e. ``controller_Types`` and ``controller_ids`` are ignored if + ``controllers`` are provided. + canvas: WgpuCanvas, optional Canvas for drawing @@ -83,25 +92,23 @@ def __init__( subplot names """ - self.shape = shape + self._shape = shape if names is not None: - if len(list(chain(*names))) != self.shape[0] * self.shape[1]: + if len(list(chain(*names))) != len(self): raise ValueError( "must provide same number of subplot `names` as specified by gridplot shape" ) - self.names = np.asarray(names).reshape(self.shape) + subplot_names = np.asarray(names).reshape(self.shape) else: - self.names = None + subplot_names = None canvas, renderer = make_canvas_and_renderer(canvas, renderer) if isinstance(cameras, str): # create the array representing the views for each subplot in the grid - cameras = np.array([cameras] * self.shape[0] * self.shape[1]).reshape( - self.shape - ) + cameras = np.array([cameras] * len(self)).reshape(self.shape) # list -> array if necessary cameras = np.asarray(cameras).reshape(self.shape) @@ -110,127 +117,177 @@ def __init__( raise ValueError("Number of cameras does not match the number of subplots") # create the cameras - self._cameras = np.empty(self.shape, dtype=object) + subplot_cameras = np.empty(self.shape, dtype=object) for i, j in product(range(self.shape[0]), range(self.shape[1])): - self._cameras[i, j] = create_camera(camera_type=cameras[i, j]) + subplot_cameras[i, j] = create_camera(camera_type=cameras[i, j]) - if controller_ids is None: - # individual controller for each subplot - controller_ids = np.arange(self.shape[0] * self.shape[1]).reshape( - self.shape - ) + # if controller instances have been specified for each subplot + if controllers is not None: + + # one controller for all subplots + if isinstance(controllers, pygfx.Controller): + controllers = [controllers] * len(self) + # subplot_controllers[:] = controllers + # # subplot_controllers = np.asarray([controllers] * len(self), dtype=object) - elif isinstance(controller_ids, str): - if controller_ids == "sync": - controller_ids = np.zeros(self.shape, dtype=int) + # individual controller instance specified for each subplot else: - raise ValueError( - f"`controller_ids` must be one of 'sync', an array/list of subplot names, or an array/list of " - f"integer ids. See the docstring for more details." - ) + # I found that this is better than list(*chain()) because chain doesn't give the right + # result we want for arrays + for item in controllers: + if isinstance(item, pygfx.Controller): + pass + elif all(isinstance(c, pygfx.Controller) for c in item): + pass + else: + raise TypeError( + "controllers argument must be a single pygfx.Controller instance of a Iterable of " + "pygfx.Controller instances" + ) - # list controller_ids - elif isinstance(controller_ids, (list, np.ndarray)): - ids_flat = list(chain(*controller_ids)) + try: + controllers = np.asarray(controllers).reshape(shape) + except ValueError: + raise ValueError( + f"number of controllers passed must be the same as the number of subplots specified " + f"by shape: {self.shape}. You have passed: <{controllers.size}> controllers" + ) from None - # list of str of subplot names, convert this to integer ids - if all([isinstance(item, str) for item in ids_flat]): - if self.names is None: - raise ValueError( - "must specify subplot `names` to use list of str for `controller_ids`" - ) + subplot_controllers: np.ndarray[pygfx.Controller] = np.empty( + self.shape, dtype=object + ) - # make sure each controller_id str is a subplot name - if not all([n in self.names for n in ids_flat]): - raise KeyError( - f"all `controller_ids` strings must be one of the subplot names" - ) + for i, j in product(range(self.shape[0]), range(self.shape[1])): + subplot_controllers[i, j] = controllers[i, j] + subplot_controllers[i, j].add_camera(subplot_cameras[i, j]) - if len(ids_flat) > len(set(ids_flat)): + # parse controller_ids and controller_types to make desired controller for each supblot + else: + if controller_ids is None: + # individual controller for each subplot + controller_ids = np.arange(len(self)).reshape(self.shape) + + elif isinstance(controller_ids, str): + if controller_ids == "sync": + # this will eventually make one controller for all subplots + controller_ids = np.zeros(self.shape, dtype=int) + else: raise ValueError( - "id strings must not appear twice in `controller_ids`" + f"`controller_ids` must be one of 'sync', an array/list of subplot names, or an array/list of " + f"integer ids. See the docstring for more details." ) - # initialize controller_ids array - ids_init = np.arange(self.shape[0] * self.shape[1]).reshape(self.shape) + # list controller_ids + elif isinstance(controller_ids, (list, np.ndarray)): + ids_flat = list(chain(*controller_ids)) - # set id based on subplot position for each synced sublist - for i, sublist in enumerate(controller_ids): - for name in sublist: - ids_init[self.names == name] = -( - i + 1 - ) # use negative numbers because why not + # list of str of subplot names, convert this to integer ids + if all([isinstance(item, str) for item in ids_flat]): + if subplot_names is None: + raise ValueError( + "must specify subplot `names` to use list of str for `controller_ids`" + ) - controller_ids = ids_init + # make sure each controller_id str is a subplot name + if not all([n in subplot_names for n in ids_flat]): + raise KeyError( + f"all `controller_ids` strings must be one of the subplot names" + ) - # integer ids - elif all([isinstance(item, (int, np.integer)) for item in ids_flat]): - controller_ids = np.asarray(controller_ids).reshape(self.shape) + if len(ids_flat) > len(set(ids_flat)): + raise ValueError( + "id strings must not appear twice in `controller_ids`" + ) - else: - raise TypeError( - f"list argument to `controller_ids` must be a list of `str` or `int`, " - f"you have passed: {controller_ids}" - ) + # initialize controller_ids array + ids_init = np.arange(len(self)).reshape(self.shape) - if controller_ids.shape != self.shape: - raise ValueError( - "Number of controller_ids does not match the number of subplots" - ) + # set id based on subplot position for each synced sublist + for i, sublist in enumerate(controller_ids): + for name in sublist: + ids_init[subplot_names == name] = -( + i + 1 + ) # use negative numbers because why not - if controller_types is None: - # `create_controller()` will auto-determine controller for each subplot based on defaults - controller_types = np.array( - ["default"] * self.shape[0] * self.shape[1] - ).reshape(self.shape) + controller_ids = ids_init - # validate controller types - types_flat = list(chain(*controller_types)) - # str controller_type or pygfx instances - valid_str = list(valid_controller_types.keys()) + ["default"] - valid_instances = tuple(valid_controller_types.values()) + # integer ids + elif all([isinstance(item, (int, np.integer)) for item in ids_flat]): + controller_ids = np.asarray(controller_ids).reshape(self.shape) - # make sure each controller type is valid - for controller_type in types_flat: - if controller_type is None: - continue + else: + raise TypeError( + f"list argument to `controller_ids` must be a list of `str` or `int`, " + f"you have passed: {controller_ids}" + ) - if (controller_type not in valid_str) and ( - not isinstance(controller_type, valid_instances) - ): + if controller_ids.shape != self.shape: raise ValueError( - f"You have passed an invalid controller type, valid controller_types arguments are:\n" - f"{valid_str} or instances of {[c.__name__ for c in valid_instances]}" + "Number of controller_ids does not match the number of subplots" ) - controller_types = np.asarray(controller_types).reshape(self.shape) + if controller_types is None: + # `create_controller()` will auto-determine controller for each subplot based on defaults + controller_types = np.array(["default"] * len(self)).reshape(self.shape) - # make the real controllers for each subplot - self._controllers = np.empty(shape=self.shape, dtype=object) - for cid in np.unique(controller_ids): - cont_type = controller_types[controller_ids == cid] - if np.unique(cont_type).size > 1: - raise ValueError( - "Multiple controller types have been assigned to the same controller id. " - "All controllers with the same id must use the same type of controller." - ) + elif isinstance(controller_types, str): + if controller_types not in valid_controller_types.keys(): + raise ValueError( + f"invalid controller_types argument, you may pass either a single controller type as a str, or an" + f"iterable of controller types from the selection: {valid_controller_types.keys()}" + ) + + # valid controller types + types_flat = list(chain(*controller_types)) + # str controller_type or pygfx instances + valid_str = list(valid_controller_types.keys()) + ["default"] + valid_instances = tuple(valid_controller_types.values()) + + # make sure each controller type is valid + for controller_type in types_flat: + if controller_type is None: + continue + + if (controller_type not in valid_str) and ( + not isinstance(controller_type, valid_instances) + ): + raise ValueError( + f"You have passed an invalid controller type, valid controller_types arguments are:\n" + f"{valid_str} or instances of {[c.__name__ for c in valid_instances]}" + ) - cont_type = cont_type[0] + controller_types: np.ndarray[pygfx.Controller] = np.asarray( + controller_types + ).reshape(self.shape) - # get all the cameras that use this controller - cams = self._cameras[controller_ids == cid].ravel() + # make the real controllers for each subplot + subplot_controllers = np.empty(shape=self.shape, dtype=object) + for cid in np.unique(controller_ids): + cont_type = controller_types[controller_ids == cid] + if np.unique(cont_type).size > 1: + raise ValueError( + "Multiple controller types have been assigned to the same controller id. " + "All controllers with the same id must use the same type of controller." + ) - if cont_type == "default": - # hacky fix for now because of how `create_controller()` works - cont_type = None - _controller = create_controller(controller_type=cont_type, camera=cams[0]) + cont_type = cont_type[0] - self._controllers[controller_ids == cid] = _controller + # get all the cameras that use this controller + cams = subplot_cameras[controller_ids == cid].ravel() - # add the other cameras that go with this controller - if cams.size > 1: - for cam in cams[1:]: - _controller.add_camera(cam) + if cont_type == "default": + # hacky fix for now because of how `create_controller()` works + cont_type = None + _controller = create_controller( + controller_type=cont_type, camera=cams[0] + ) + + subplot_controllers[controller_ids == cid] = _controller + + # add the other cameras that go with this controller + if cams.size > 1: + for cam in cams[1:]: + _controller.add_camera(cam) self._canvas = canvas self._renderer = renderer @@ -243,11 +300,11 @@ def __init__( for i, j in self._get_iterator(): position = (i, j) - camera = self._cameras[i, j] - controller = self._controllers[i, j] + camera = subplot_cameras[i, j] + controller = subplot_controllers[i, j] - if self.names is not None: - name = self.names[i, j] + if subplot_names is not None: + name = subplot_names[i, j] else: name = None @@ -272,6 +329,11 @@ def __init__( RecordMixin.__init__(self) Frame.__init__(self) + @property + def shape(self) -> tuple[int, int]: + """[n_rows, n_cols]""" + return self._shape + @property def canvas(self) -> WgpuCanvasBase: """The canvas associated to this GridPlot""" @@ -282,6 +344,31 @@ def renderer(self) -> pygfx.WgpuRenderer: """The renderer associated to this GridPlot""" return self._renderer + @property + def controllers(self) -> np.ndarray[pygfx.Controller]: + """controllers, read-only array, access individual subplots to change a controller""" + controllers = np.asarray( + [subplot.controller for subplot in self], dtype=object + ).reshape(self.shape) + controllers.flags.writeable = False + return controllers + + @property + def cameras(self) -> np.ndarray[pygfx.Camera]: + """cameras, read-only array, access individual subplots to change a camera""" + cameras = np.asarray( + [subplot.camera for subplot in self], dtype=object + ).reshape(self.shape) + cameras.flags.writeable = False + return cameras + + @property + def names(self) -> np.ndarray[str]: + """subplot names, read-only array, access individual subplots to change a name""" + names = np.asarray([subplot.name for subplot in self]).reshape(self.shape) + names.flags.writeable = False + return names + def __getitem__(self, index: tuple[int, int] | str) -> Subplot: if isinstance(index, str): for subplot in self._subplots.ravel(): @@ -389,6 +476,10 @@ def __next__(self) -> Subplot: pos = self._current_iter.__next__() return self._subplots[pos] + def __len__(self): + """number of subplots""" + return self.shape[0] * self.shape[1] + def __str__(self): return f"{self.__class__.__name__} @ {hex(id(self))}" diff --git a/tests/test_gridplot.py b/tests/test_gridplot.py new file mode 100644 index 000000000..3814664d7 --- /dev/null +++ b/tests/test_gridplot.py @@ -0,0 +1,164 @@ +import numpy as np +import pytest + +import fastplotlib as fpl +import pygfx + + +def test_cameras_controller_properties(): + cameras = [ + ["2d", "3d", "3d"], + ["3d", "3d", "3d"] + ] + + controller_types = [ + ["panzoom", "panzoom", "fly"], + ["orbit", "trackball", "panzoom"] + ] + + gp = fpl.GridPlot( + shape=(2, 3), + cameras=cameras, + controller_types=controller_types, + canvas="offscreen" + ) + + print(gp.canvas) + + subplot_cameras = [subplot.camera for subplot in gp] + subplot_controllers = [subplot.controller for subplot in gp] + + for c1, c2 in zip(subplot_cameras, gp.cameras.ravel()): + assert c1 is c2 + + for c1, c2 in zip(subplot_controllers, gp.controllers.ravel()): + assert c1 is c2 + + for camera_type, subplot_camera in zip(np.asarray(cameras).ravel(), gp.cameras.ravel()): + if camera_type == "2d": + assert subplot_camera.fov == 0 + else: + assert subplot_camera.fov == 50 + + for controller_type, subplot_controller in zip(np.asarray(controller_types).ravel(), gp.controllers.ravel()): + match controller_type: + case "panzoom": + assert isinstance(subplot_controller, pygfx.PanZoomController) + case "fly": + assert isinstance(subplot_controller, pygfx.FlyController) + case "orbit": + assert isinstance(subplot_controller, pygfx.OrbitController) + case "trackball": + assert isinstance(subplot_controller, pygfx.TrackballController) + + # check changing cameras + gp[0, 0].camera = "3d" + assert gp[0, 0].camera.fov == 50 + gp[1, 0].camera = "2d" + assert gp[1, 0].camera.fov == 0 + + # test changing controller + gp[1, 1].controller = "fly" + assert isinstance(gp[1, 1].controller, pygfx.FlyController) + assert gp[1, 1].controller is gp.controllers[1, 1] + gp[0, 2].controller = "panzoom" + assert isinstance(gp[0, 2].controller, pygfx.PanZoomController) + assert gp[0, 2].controller is gp.controllers[0, 2] + + +def test_gridplot_controller_ids_int(): + ids = [ + [0, 1, 1], + [0, 2, 3], + [4, 1, 2] + ] + + gp = fpl.GridPlot(shape=(3, 3), controller_ids=ids, canvas="offscreen") + + assert gp[0, 0].controller is gp[1, 0].controller + assert gp[0, 1].controller is gp[0, 2].controller is gp[2, 1].controller + assert gp[1, 1].controller is gp[2, 2].controller + + +def test_gridplot_controller_ids_int_change_controllers(): + ids = [ + [0, 1, 1], + [0, 2, 3], + [4, 1, 2] + ] + + cameras = [ + ["2d", "3d", "3d"], + ["2d", "3d", "2d"], + ["3d", "3d", "3d"] + ] + + gp = fpl.GridPlot(shape=(3, 3), cameras=cameras, controller_ids=ids, canvas="offscreen") + + assert isinstance(gp[0, 1].controller, pygfx.FlyController) + + # changing controller when id matches should change the others too + gp[0, 1].controller = "panzoom" + assert isinstance(gp[0, 1].controller, pygfx.PanZoomController) + assert gp[0, 1].controller is gp[0, 2].controller is gp[2, 1].controller + assert set(gp[0, 1].controller.cameras) == {gp[0, 1].camera, gp[0, 2].camera, gp[2, 1].camera} + + # change to orbit + gp[0, 1].controller = "orbit" + assert isinstance(gp[0, 1].controller, pygfx.OrbitController) + assert gp[0, 1].controller is gp[0, 2].controller is gp[2, 1].controller + assert set(gp[0, 1].controller.cameras) == {gp[0, 1].camera, gp[0, 2].camera, gp[2, 1].camera} + + +def test_gridplot_controller_ids_str(): + names = [ + ["a", "b", "c"], + ["d", "e", "f"] + ] + + controller_ids = [ + ["a", "f"], + ["b", "d", "e"] + ] + + gp = fpl.GridPlot(shape=(2, 3), controller_ids=controller_ids, names=names, canvas="offscreen") + + assert gp[0, 0].controller is gp[1, 2].controller is gp["a"].controller is gp["f"].controller + assert gp[0, 1].controller is gp[1, 0].controller is gp[1, 1].controller is gp["b"].controller is gp["d"].controller is gp["e"].controller + + # make sure subplot c is unique + exclude_c = [gp[n].controller for n in ["a", "b", "d", "e", "f"]] + assert gp["c"] not in exclude_c + + +def test_set_gridplot_controllers_from_existing_controllers(): + gp = fpl.GridPlot(shape=(3, 3), canvas="offscreen") + gp2 = fpl.GridPlot(shape=gp.shape, controllers=gp.controllers, canvas="offscreen") + + assert gp.controllers[:-1].size == 6 + with pytest.raises(ValueError): + gp3 = fpl.GridPlot(shape=gp.shape, controllers=gp.controllers[:-1], canvas="offscreen") + + for sp_gp, sp_gp2 in zip(gp, gp2): + assert sp_gp.controller is sp_gp2.controller + + cameras = [ + [pygfx.PerspectiveCamera(), "3d"], + ["3d", "2d"] + ] + + controllers = [ + [pygfx.FlyController(cameras[0][0]), pygfx.TrackballController()], + [pygfx.OrbitController(), pygfx.PanZoomController()] + ] + + gp = fpl.GridPlot(shape=(2, 2), cameras=cameras, controllers=controllers, canvas="offscreen") + + assert gp[0, 0].controller is controllers[0][0] + assert gp[0, 1].controller is controllers[0][1] + assert gp[1, 0].controller is controllers[1][0] + assert gp[1, 1].controller is controllers[1][1] + + assert gp[0, 0].camera is cameras[0][0] + + assert gp[0, 1].camera.fov == 50 From e8876478fc472549bfaef41c86028b3caf9e4228 Mon Sep 17 00:00:00 2001 From: kushalkolar Date: Wed, 10 Apr 2024 19:37:57 -0400 Subject: [PATCH 061/185] update gov --- GOVERNANCE.md | 117 +++++++++++++++++++++++--------------------------- 1 file changed, 54 insertions(+), 63 deletions(-) diff --git a/GOVERNANCE.md b/GOVERNANCE.md index ff7b107b5..3ad450dcb 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -6,116 +6,107 @@ The purpose of this document is to formalize the governance process used by the ## Mission -Leverage new graphics APIs and modern GPU hardware to create fast and interactive scientific visualizations using an expressive and elegant API. +The mission of `fastplotlib` is to leverage new graphics APIs and modern GPU hardware to create fast and interactive scientific visualizations using an expressive and elegant API. ## Leadership Team ### Maintainers -The maintainers are the core developers of fastplotlib and together have a complete understanding of the codebase. +The maintainers are the core developers of fastplotlib and together have a complete understanding of the codebase. They are also known as code-owners. + +The current maintainers are: 1. [Kushal Kolar](https://github.com/kushalkolar) 1. [Caitlin Lewis](https://github.com/clewis7) +Responsibilities: + +* Carry out the `fastplotlib` mission. +* Work towards completion of the roadmap. +* Timely responses to issues and pull requests. +* Code review. +* Attend a yearly Roadmap meeting. +* Be available for conflict resolution. ### Advisory Committee -The Advisory Committee hold a significant stake in fastplotlib as determined by the **Maintainers**. The responsibilities of the **Advisory Committee** are to 1) attend a yearly Roadmap meeting, 2) be available for conflict resolution. +The Advisory Committee holds a significant interest in fastplotlib as determined solely by the **Maintainers**. The responsibilities of the **Advisory Committee** are to 1) attend a yearly Roadmap meeting, 2) be available for conflict resolution. +1. Amol Pasarkar 1. Eric Thomson 1. Guillaume Viejo 1. Andrea Giovannucci 1. John Pearson +Responsibilities: + +* Help carry out the `fastplotlib` mission. +* Provide strategic guidance. +* Attend a yearly Roadmap meeting. +* Be available for conflict resolution. + ### Neutral moderator No voting power, has no stake in the fastplotlib project. * Reagan Bullins -### Voting power distribution +Responsibilities: + +* Facilitate conflict resolution without voting power. + +## Adding a member to the advisory committee +1. Only individuals, not organizations, may be added to the leadership team. A candidate individual must be nominated by a current member of the leadership team. +2. A candidate must: + * Be committed to the fastplotlib mission. + * Have demonstrated contibutions to `fastplotlib` through one of: + * Significant contributions to the codebase. + * Significant application of fastplotlib in a dependent package. + * Significant technical guidance or feedback on the development of `fastplotlib`. -Maintainers: 50% +## Adding a maintainer -Advisory Committee: 50% +Candidate maintainers must have demonstrated prolonged and significant contributions to the codebase over a long period of time. A candidate can be nominated by any current maintainer. The candidate may then be added as a maintainer through a unanimous vote within the current maintainers. -Veto: Any vote can be vetoed by a unanimous vote within the maintainers. +## Decision making -Note that currently the voting power is primarily held by the maintainers - Kushal Kolar & Caitlin Lewis. This is intentional since the library is in an early stage. Knowledge of the codebase and its inner workings are predominantly held by Kushal & Caitlin, and nobody else. +Decisions about the future of the project are made through discussion with all members of the community. All non-sensitive project management discussion takes place on the issue tracker. Occasionally, sensitive discussions may occur on a private core developer medium. -### Voting Process +Decisions should be made in accordance with the mission and code of conduct of the `fastplotlib` project. -1. For the purpose of conflict resolution the Leadership Team must be convened by the neutral moderator. In all other situations the Leadership Team may be convened by one of the maintainers. -1. Once the Leadership Team is convened and discussions have occurred, voting must conclude within 1 hour. -1. Voting is performed anonymously and handled solely by the neutral moderator in the case of conflict resolution, or by one of the maintainers. -1. After voting has finished, any maintainer may invoke a veto vote. If the veto succeeds, then the same item may not be voted on for 100 days. +We use a “consensus seeking” process for making decisions. The Leadership Team tries to find a resolution that has no open objections among Leadership Team members. Leadership Team members are expected to distinguish between fundamental objections to a proposal and minor perceived flaws that they can live with, and not hold up the decision-making process for the latter. If no option can be found without objections, the decision is escalated to the maintainers who have ultimate authority. -## Adding members to the Leadership Team +Decisions are made according to the following rules: -### Confirming Maintainers +Minor documentation changes, such as typo fixes, or addition / correction of a sentence, require approval by a maintainer and no disagreement or requested changes by other maintainers on the issue or pull request page via lazy consensus. Pull-request authors are expected to give “reasonable time” to others to give their opinion on the pull request if they’re not confident others would agree. -* Given to individuals on merit basis after they have demonstrated strong expertise of the library through contributions, reviews and discussions. -* Adding a maintainer requires one of: - * a unanimous vote by current maintainers - * 75% vote within the advisory committee -* For continued membership in the maintainer group the individual has to demonstrate strong and continued alignment with the fastplotlib mission. The individual must also actively commit to the repo, respond to issues, and review pull requests. -* The membership is for an individual, not a company or organization. -* There must always be a minimum of 2 maintainers. -* A maintainer may be removed by one of: - * 60% vote within the maintainers - * 75% vote within the advisory committee +Code changes and major documentation changes require agreement by one maintainer and no disagreement or requested changes by other maintainers on the issue or pull-request page (lazy consensus). For all changes of this type, maintainers are expected to give “reasonable time” after approval and before merging for others to weigh in on the pull request in its final state. -### Confirming an advisory committee member +Changes to the API principles require a dedicated issue on our issue tracker and follow the decision-making process outlined above. -* A candidate advisory committee member may only be nominated by a current maintainer or advisory committee member. -* Candidate must have used fastplotlib in their own work or library, or made contributions to fastplotlib. -* Candidate must be committed to the mission and demonstrate, with examples, how their role on the advisory committee would further the mission. -* If the Individual fulfills the above criteria they may be considered. The Leadership Team then requires an overall 80% vote to add the candidate as an advisory member. -* The membership is for an individual, not a company or organization. - * 60% vote within the maintainers - * 75% vote within the advisory committee +Changes to this governance model or our mission, vision, and values require a dedicated issue on our issue tracker and follow the decision-making process outlined above. -## Invoking a vote from the leadership team +If an objection is raised on a lazy consensus, the proposer can appeal to the Leadership Team and the change can be approved or rejected by escalating to the maintainers. -### Conflict +## Conflict Resolution Anyone (absolutely anyone, not just the leadership team members) who feels that the code of conduct or governance document has been breached may invoke a vote by contacting the neutral moderator. -#### Process +### Process 1. Contact the neutral moderator with a description of the conflict, max of 250 words. 2. Neutral moderator must schedule a vote within 15 days. If that is not possible then within the next 45 days. -3. If there is a special case that requires urgency, such as upcoming events, workshops, etc., the neutral moderator may try to schedule a vote ASAP. Exercising this urgency is solely at the discretion of the neutral moderator. -4. The individual who has invoked the conflict vote can choose to present their case, or they may choose to let the neutral moderator represent them. +3. The individual who has invoked the conflict vote can choose to present their case, or they may choose to let the neutral moderator represent them. * Every individual involved in the conflict is given a maximum of 15 minutes to be represented. This time limit may be expanded at the discretion of the neutral moderator if a justifiable reason is provided. -5. The Leadership Team votes on one of the actions from “Enforcement Guidelines”: https://www.contributor-covenant.org/version/2/1/code_of_conduct/. It is advised that the first offense leads to action (1) “Correction” and then go down the list for repeated conflicts from the same individual/organization. Very bad behavior, as determined by the leadership team, can justify a first offense resulting in (3) “Temporary Ban” or (4) “Permanent Ban”. -6. Voting is performed as described in the section **Voting Process**. +4. The maintainers vote on one of the actions from “Enforcement Guidelines”: https://www.contributor-covenant.org/version/2/1/code_of_conduct/. It is advised that the first offense leads to action (1) “Correction”. Repeated or serious offenses from the same individual/organization may lead to escalating levels of actions. Very bad behavior, as determined by the leadership team, can justify a first offense resulting in (3) “Temporary Ban” or (4) “Permanent Ban”. +5. The advisory committee members may advise on the actions, but the ultimate decision is voted on by the maintainers. -### Adding a member to the leadership team -1. A current maintainer or advisory committee member may nominate a candidate and then contact the neutral moderator with a written summary, of no more than 100 words, with reasons for adding the candidate to the Leadership Team. -1. The nominator should specify whether the candidate should be a maintainer or advisory committee member. -1. The neutral moderator must then schedule a vote within 100 days. -1. The nominator and candidate may then speak for no more than 10 minutes each on why the candidate should be added to the leadership team. -1. Voting is performed as described in the section **Voting Process**. +## Transparency + +Governance decisions, meeting minutes, and voting outcomes are publicly documented and accessible. We aim for transparency to allow the broader community to understand and trust the governance process. ## Changes to this governance document -#### Until February 28, 2025 +### Until February 28, 2025 During early stages of fastplotlib development, changes to the governance document may be made directly through unanimous approval by the original maintainers, Kushal Kolar & Caitlin Lewis, without contacting the neutral moderator or consulting with the advisory committee. They (Kushal & Caitlin) may also add new members to the advisory committee through unanimous approval. - -#### After February 28, 2025 - -Any member of the Leadership Team can propose changes to the governance document. Approving the changes requires a 75% vote within the maintainers and a 75% vote within the advisory committee. - -## Reasons for invoking a vote - -Things that can be voted on include but aren’t limited to: -1. Nominating a member to the Leadership Team -1. Adding a nominee to the Leadership Team -1. Removing a member from the Leadership Team -1. Banning any person (not just Leadership Team members) or organization from interacting with the fastplotlib GitHub repository and/or fastplotlib GitHub organization. -1. Appeal to un-ban a previously banned person or organization. - * Can only occur 3 months after the initial ban - * Person must contact the neutral moderator directly to start an appeal - * Maximum of 3 appeal attempts are allowed From e7d940d3d4f0098baaff7ea5a55d307bd00855b5 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Fri, 12 Apr 2024 00:48:44 -0400 Subject: [PATCH 062/185] Iw refactor (#482) * fefactor imwidget, restrict to tzxy, txy, xy, added RGB(A) support (#459) * add new iw test screenshots * update api docs --------- Co-authored-by: Amol Pasarkar --- docs/source/api/widgets/ImageWidget.rst | 4 +- examples/notebooks/image_widget.ipynb | 1 - examples/notebooks/image_widget_test.ipynb | 63 ++- ...dget-zfish-mixed-rgb-cockatoo-frame-50.png | 3 + ...dget-zfish-mixed-rgb-cockatoo-set-data.png | 3 + ...get-zfish-mixed-rgb-cockatoo-windowrgb.png | 3 + fastplotlib/widgets/image.py | 515 +++++++++--------- 7 files changed, 306 insertions(+), 286 deletions(-) create mode 100644 examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-frame-50.png create mode 100644 examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-set-data.png create mode 100644 examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-windowrgb.png diff --git a/docs/source/api/widgets/ImageWidget.rst b/docs/source/api/widgets/ImageWidget.rst index 08bce8d7a..2b4708007 100644 --- a/docs/source/api/widgets/ImageWidget.rst +++ b/docs/source/api/widgets/ImageWidget.rst @@ -23,9 +23,11 @@ Properties ImageWidget.cmap ImageWidget.current_index ImageWidget.data - ImageWidget.dims_order + ImageWidget.frame_apply ImageWidget.gridplot ImageWidget.managed_graphics + ImageWidget.n_img_dims + ImageWidget.n_scrollable_dims ImageWidget.ndim ImageWidget.slider_dims ImageWidget.sliders diff --git a/examples/notebooks/image_widget.ipynb b/examples/notebooks/image_widget.ipynb index 56d5c8a81..a7527601a 100644 --- a/examples/notebooks/image_widget.ipynb +++ b/examples/notebooks/image_widget.ipynb @@ -115,7 +115,6 @@ "source": [ "iw_movie = ImageWidget(\n", " data=gray_movie, \n", - " slider_dims=[\"t\"],\n", " cmap=\"gray\"\n", ")" ] diff --git a/examples/notebooks/image_widget_test.ipynb b/examples/notebooks/image_widget_test.ipynb index c236ce9b7..39cf0b887 100644 --- a/examples/notebooks/image_widget_test.ipynb +++ b/examples/notebooks/image_widget_test.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "id": "07019035-83f2-4753-9e7c-628ae439b441", "metadata": { "tags": [] @@ -18,7 +18,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "id": "10b8ab40-944d-472c-9b7e-cae8a129e7ce", "metadata": {}, "outputs": [], @@ -130,7 +130,6 @@ "source": [ "iw_movie = ImageWidget(\n", " data=gray_movie, \n", - " slider_dims=[\"t\"],\n", " cmap=\"gray\",\n", " grid_plot_kwargs={\"size\": (900, 600)},\n", ")" @@ -275,9 +274,6 @@ "execution_count": null, "id": "76535d56-e514-4c16-aa48-a6359f8019d5", "metadata": { - "jupyter": { - "source_hidden": true - }, "tags": [] }, "outputs": [], @@ -444,23 +440,66 @@ "iw_z.close()" ] }, + { + "cell_type": "markdown", + "id": "6716f255-44c2-400d-a2bf-254683e4cd9d", + "metadata": {}, + "source": [ + "# Test Mixed Shapes, RGB (and set data)" + ] + }, { "cell_type": "code", - "execution_count": null, - "id": "870627ef-09d8-44e4-8952-aedb702d1526", + "execution_count": 30, + "id": "ed783360-992d-40f8-bb6f-152a59edff43", "metadata": {}, "outputs": [], "source": [ - "notebook_finished()" + "zfish_data = np.load(\"./zfish_test.npy\")\n", + "zfish_frame_1 = zfish_data[0, 0, :, :]\n", + "zfish_frame_2 = zfish_data[20, 3, :, :]\n", + "movie = iio.imread(\"imageio:cockatoo.mp4\")\n", + "\n", + "iw_mixed_shapes = ImageWidget(\n", + " data=[zfish_frame_1, movie], # you can also provide a list of tzxy arrays\n", + " rgb=[False, True],\n", + " histogram_widget=True,\n", + " cmap=\"gnuplot2\", \n", + " grid_plot_kwargs = {\"controller_ids\": None},\n", + ")\n", + "\n", + "iw_mixed_shapes.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "274c67b4-aa07-4fcf-a094-1b1e70d0378a", + "metadata": {}, + "outputs": [], + "source": [ + "iw_mixed_shapes.sliders[\"t\"].value = 50\n", + "plot_test(\"image-widget-zfish-mixed-rgb-cockatoo-frame-50\", iw_mixed_shapes.gridplot)\n", + "\n", + "#Set the data, changing the first array and also the size of the \"T\" slider\n", + "iw_mixed_shapes.set_data([zfish_frame_2, movie[:200, :, :, :]], reset_indices=True)\n", + "plot_test(\"image-widget-zfish-mixed-rgb-cockatoo-set-data\", iw_mixed_shapes.gridplot)\n", + "\n", + "#Check how a window function might work on the RGB data\n", + "iw_mixed_shapes.window_funcs = {\"t\": (np.mean, 4)}\n", + "iw_mixed_shapes.sliders[\"t\"].value = 20\n", + "plot_test(\"image-widget-zfish-mixed-rgb-cockatoo-windowrgb\", iw_mixed_shapes.gridplot)" ] }, { "cell_type": "code", "execution_count": null, - "id": "b8fff1a6-119e-4f03-ba3a-4c7b9e8c212b", + "id": "870627ef-09d8-44e4-8952-aedb702d1526", "metadata": {}, "outputs": [], - "source": [] + "source": [ + "notebook_finished()" + ] } ], "metadata": { @@ -479,7 +518,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.3" + "version": "3.11.8" } }, "nbformat": 4, diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-frame-50.png b/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-frame-50.png new file mode 100644 index 000000000..5e0750ac8 --- /dev/null +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-frame-50.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7f8f74a0a5fa24e10a88d3723836306913243fa5fc23f46f44bbdae4c0209075 +size 58878 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-set-data.png b/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-set-data.png new file mode 100644 index 000000000..8df83fe33 --- /dev/null +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-set-data.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0809b2dda0e773b7f100386f97144c40d36d51cd935c86ef1dcd4a938fce3981 +size 56319 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-windowrgb.png b/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-windowrgb.png new file mode 100644 index 000000000..5bbefc7ae --- /dev/null +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-windowrgb.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a2e2e2cf7ac6be1a4fccec54494c3fd48af673765653675438fa2469c549e90c +size 55055 diff --git a/fastplotlib/widgets/image.py b/fastplotlib/widgets/image.py index acef26a7d..86671b1fc 100644 --- a/fastplotlib/widgets/image.py +++ b/fastplotlib/widgets/image.py @@ -3,20 +3,30 @@ import numpy as np - from ..layouts import GridPlot from ..graphics import ImageGraphic from ..utils import calculate_gridshape from .histogram_lut import HistogramLUT -DEFAULT_DIMS_ORDER = { - 2: "xy", - 3: "txy", - 4: "tzxy", - 5: "tzcxy", +# Number of dimensions that represent one image/one frame. For grayscale shape will be [x, y], i.e. 2 dims, for RGB(A) +# shape will be [x, y, c] where c is of size 3 (RGB) or 4 (RGBA) +IMAGE_DIM_COUNTS = {"gray": 2, "rgb": 3} + +# Map boolean (indicating whether we use RGB or grayscale) to the string. Used to index RGB_DIM_MAP +RGB_BOOL_MAP = {False: "gray", True: "rgb"} + +# Dimensions that can be scrolled from a given data array +SCROLLABLE_DIMS_ORDER = { + 0: "", + 1: "t", + 2: "tz", } +ALLOWED_SLIDER_DIMS = {0: "t", 1: "z"} + +ALLOWED_WINDOW_DIMS = {"t", "z"} + def _is_arraylike(obj) -> bool: """ @@ -149,13 +159,16 @@ def data(self) -> List[np.ndarray]: @property def ndim(self) -> int: - """number of dimensions in the image data displayed in the widget""" + """Number of dimensions of grayscale data displayed in the widget (it will be 1 more for RGB(A) data)""" return self._ndim @property - def dims_order(self) -> List[str]: - """dimension order of the data displayed in the widget""" - return self._dims_order + def n_scrollable_dims(self) -> List[int]: + """ + list indicating the number of dimenensions that are scrollable for each data array + All other dimensions are frame/image data, i.e. [x, y] or [x, y, c] + """ + return self._n_scrollable_dims @property def sliders(self) -> Dict[str, Any]: @@ -184,6 +197,56 @@ def current_index(self) -> Dict[str, int]: """ return self._current_index + @property + def n_img_dims(self) -> list[int]: + """ + list indicating the number of dimensions that contain image/single frame data for each data array. + if 2: data are grayscale, i.e. [x, y] dims, if 3: data are [x, y, c] where c is RGB or RGBA, + this is the complement of `n_scrollable_dims` + """ + return self._n_img_dims + + def _get_n_scrollable_dims(self, curr_arr: np.ndarray, rgb: bool) -> list[int]: + """ + For a given ``array`` displayed in the ImageWidget, this function infers how many of the dimensions are + supported by sliders (aka scrollable). Ex: "xy" data has 0 scrollable dims, "txy" has 1, "tzxy" has 2. + + Parameters + ---------- + curr_arr: np.ndarray + np.ndarray or a list of array-like + + rgb: bool + True if we view this as RGB(A) and False if grayscale + + Returns + ------- + int + Number of scrollable dimensions for each ``array`` in the dataset. + """ + + n_img_dims = IMAGE_DIM_COUNTS[RGB_BOOL_MAP[rgb]] + # Make sure each image stack at least ``n_img_dims`` dimensions + if len(curr_arr.shape) < n_img_dims: + raise ValueError( + f"Your array has shape {curr_arr.shape} " + f"but you specified that each image in your array is {n_img_dims}D " + ) + + # If RGB(A), last dim must be 3 or 4 + if n_img_dims == 3: + if not (curr_arr.shape[-1] == 3 or curr_arr.shape[-1] == 4): + raise ValueError( + f"Expected size 3 or 4 for last dimension of RGB(A) array, got: {curr_arr.shape[-1]}." + ) + + n_scrollable_dims = len(curr_arr.shape) - n_img_dims + + if n_scrollable_dims not in SCROLLABLE_DIMS_ORDER.keys(): + raise ValueError(f"Array had shape {curr_arr.shape} which is not supported") + + return n_scrollable_dims + @current_index.setter def current_index(self, index: Dict[str, int]): # ignore if output context has not been created yet @@ -223,30 +286,29 @@ def current_index(self, index: Dict[str, int]): def __init__( self, data: Union[np.ndarray, List[np.ndarray]], - dims_order: Union[str, Dict[int, str]] = None, - slider_dims: Union[str, int, List[Union[str, int]]] = None, window_funcs: Union[int, Dict[str, int]] = None, frame_apply: Union[callable, Dict[int, callable]] = None, grid_shape: Tuple[int, int] = None, names: List[str] = None, grid_plot_kwargs: dict = None, histogram_widget: bool = True, + rgb: list[bool] = None, **kwargs, ): """ - A high level widget for displaying n-dimensional image data in conjunction with automatically generated - sliders for navigating through 1-2 selected dimensions within image data. - - Can display a single n-dimensional image array or a grid of n-dimensional images. + This widget facilitates high-level navigation through image stacks, which are arrays containing one or more + images. It includes sliders for key dimensions such as "t" (time) and "z", enabling users to smoothly navigate + through one or multiple image stacks simultaneously. - Default dimension orders: + Allowed dimensions orders for each image stack: Note that each has a an optional (c) channel which refers to + RGB(A) a channel. So this channel should be either 3 or 4. ======= ========== n_dims dims order ======= ========== - 2 "xy" - 3 "txy" - 4 "tzxy" + 2 "xy(c)" + 3 "txy(c)" + 4 "tzxy(c)" ======= ========== Parameters @@ -254,31 +316,18 @@ def __init__( data: Union[np.ndarray, List[np.ndarray] array-like or a list of array-like - dims_order: Optional[Union[str, Dict[np.ndarray, str]]] - | ``str`` or a dict mapping to indicate dimension order - | a single ``str`` if ``data`` is a single array, or a list of arrays with the same dimension order - | examples: ``"xyt"``, ``"tzxy"`` - | ``dict`` mapping of ``{array_index: axis_order}`` if specific arrays have a non-default axes order. - | "array_index" is the position of the corresponding array in the data list. - | examples: ``{array_index: "tzxy", another_array_index: "xytz"}`` - - slider_dims: Optional[Union[str, int, List[Union[str, int]]]] - | The dimensions for which to create a slider - | can be a single ``str`` such as **"t"**, **"z"** or a numerical ``int`` that indexes the desired dimension - | can also be a list of ``str`` or ``int`` if multiple sliders are desired for multiple dimensions - | examples: ``"t"``, ``["t", "z"]`` - window_funcs: Dict[Union[int, str], int] - | average one or more dimensions using a given window - | if a slider exists for only one dimension this can be an ``int``. - | if multiple sliders exist, then it must be a `dict`` mapping in the form of: ``{dimension: window_size}`` - | dimension/axes can be specified using ``str`` such as "t", "z" etc. or ``int`` that indexes the dimension - | if window_size is not an odd number, adds 1 - | use ``None`` to disable averaging for a dimension, example: ``{"t": 5, "z": None}`` + | Apply function(s) with rolling windows along "t" and/or "z" dimensions of the `data` arrays. + | Pass a dict in the form: {dimension: (func, window_size)}, `func` must take a slice of the data array as the + | first argument and must take `axis` as a kwarg. + | Ex: mean along "t" dimension: {"t": (np.mean, 11)}, if `current_index` of "t" is 50, it will pass frames + | 45 to 55 to `np.mean` with `axis = 0`. + | Ex2: max along z dim: {"z": (np.max, 3)}, passes current, previous and next frame to `np.max` with `axis = 1` frame_apply: Union[callable, Dict[int, callable]] - | apply a function to slices of the array before displaying the frame - | pass a single function or a dict of functions to apply to each array individually + | Apply function(s) to `data` arrays before to generate final 2D image that is displayed. + | Ex: apply a spatial Gaussian filter + | Pass a single function or a dict of functions to apply to each array individually | examples: ``{array_index: to_grayscale}``, ``{0: to_grayscale, 2: threshold_img}`` | "array_index" is the position of the corresponding array in the data list. | if `window_funcs` is used, then this function is applied after `window_funcs` @@ -297,19 +346,27 @@ def __init__( histogram_widget: bool, default False make histogram LUT widget for each subplot + rgb: bool | list[bool], default None + Includes a True or False for each ``array`` in the ImageWidget, indicating whether images are displayed as + grayscale or RGB(A). + kwargs: Any passed to fastplotlib.graphics.Image """ - self._names = None # output context self._output = None + if _is_arraylike(data): + data = [data] + if isinstance(data, list): # verify that it's a list of np.ndarray if all([_is_arraylike(d) for d in data]): + + # Grid computations if grid_shape is None: grid_shape = calculate_gridshape(len(data)) @@ -320,17 +377,44 @@ def __init__( f"Invalid `grid_shape` passed, setting grid shape to: {grid_shape}" ) - _ndim = [d.ndim for d in data] + self._data: List[np.ndarray] = data - # verify that all image arrays have same number of dimensions - # sliders get messy otherwise - if not len(set(_ndim)) == 1: + # Establish number of image dimensions and number of scrollable dimensions for each array + if rgb is None: + rgb = [False] * len(self.data) + if rgb is bool: + rgb = [rgb] + if not isinstance(rgb, list): + raise TypeError( + f"rgb_disp parameter must be a list, a {type(rgb)} was provided" + ) + if not len(rgb) == len(self.data): raise ValueError( - f"Number of dimensions of all data arrays must match, your ndims are: {_ndim}" + f"rgb had length {len(rgb)} but there are {len(self.data)} data arrays; these must be equal" ) - self._data: List[np.ndarray] = data - self._ndim = self.data[0].ndim # all ndim must be same + self._rgb = rgb + + self._n_img_dims = [ + IMAGE_DIM_COUNTS[RGB_BOOL_MAP[self._rgb[i]]] + for i in range(len(self.data)) + ] + + self._n_scrollable_dims = [ + self._get_n_scrollable_dims(self.data[i], self._rgb[i]) + for i in range(len(self.data)) + ] + + # Define ndim of ImageWidget instance as largest number of scrollable dims + 2 (grayscale dimensions) + self._ndim = ( + max( + [ + self.n_scrollable_dims[i] + for i in range(len(self.n_scrollable_dims)) + ] + ) + + IMAGE_DIM_COUNTS[RGB_BOOL_MAP[False]] + ) if names is not None: if not all([isinstance(n, str) for n in names]): @@ -351,12 +435,6 @@ def __init__( f"You have passed the following types:\n" f"{[type(a) for a in data]}" ) - - elif _is_arraylike(data): - self._data = [data] - self._ndim = self.data[0].ndim - - grid_shape = calculate_gridshape(len(self._data)) else: raise TypeError( f"`data` must be an array-like type representing an n-dimensional image " @@ -364,149 +442,20 @@ def __init__( f"You have passed the following type {type(data)}" ) - # default dims order if not passed - # updated later if passed - self._dims_order: List[str] = [DEFAULT_DIMS_ORDER[self.ndim]] * len(self.data) - - if dims_order is not None: - if isinstance(dims_order, str): - dims_order = dims_order.lower() - if len(dims_order) != self.ndim: - raise ValueError( - f"number of dims '{len(dims_order)} passed to `dims_order` " - f"does not match ndim '{self.ndim}' of data" - ) - self._dims_order: List[str] = [dims_order] * len(self.data) - elif isinstance(dims_order, dict): - self._dims_order: List[str] = [DEFAULT_DIMS_ORDER[self.ndim]] * len( - self.data - ) - - # dict of {array_ix: dims_order_str} - for data_ix in list(dims_order.keys()): - if not isinstance(data_ix, int): - raise TypeError("`dims_order` dict keys must be ") - if len(dims_order[data_ix]) != self.ndim: - raise ValueError( - f"number of dims '{len(dims_order)} passed to `dims_order` " - f"does not match ndim '{self.ndim}' of data" - ) - _do = dims_order[data_ix].lower() - # make sure the same dims are present - if not set(_do) == set(DEFAULT_DIMS_ORDER[self.ndim]): - raise ValueError( - f"Invalid `dims_order` passed for one of your arrays, " - f"valid `dims_order` for given number of dimensions " - f"can only contain the following characters: " - f"{DEFAULT_DIMS_ORDER[self.ndim]}" - ) - try: - self.dims_order[data_ix] = _do - except Exception: - raise IndexError( - f"index {data_ix} out of bounds for `dims_order`, the bounds are 0 - {len(self.data)}" - ) - else: - raise TypeError( - f"`dims_order` must be a or , you have passed a: <{type(dims_order)}>" - ) - - if not len(self.dims_order[0]) == self.ndim: - raise ValueError( - f"Number of dims specified by `dims_order`: {len(self.dims_order[0])} does not" - f" match number of dimensions in the `data`: {self.ndim}" - ) - - ao = np.array([sorted(v) for v in self.dims_order]) - - if not np.all(ao == ao[0]): - raise ValueError( - f"`dims_order` for all arrays must contain the same combination of dimensions, your `dims_order` are: " - f"{self.dims_order}" - ) - - # if slider_dims not provided - if slider_dims is None: - # by default sliders are made for all dimensions except the last 2 - default_dim_names = {0: "t", 1: "z", 2: "c"} - slider_dims = list() - for dim in range(self.ndim - 2): - if dim in default_dim_names.keys(): - slider_dims.append(default_dim_names[dim]) - else: - slider_dims.append(f"{dim}") - - # slider for only one of the dimensions - if isinstance(slider_dims, (int, str)): - # if numerical dimension is specified - if isinstance(slider_dims, int): - ao = np.array([v for v in self.dims_order]) - if not np.all(ao == ao[0]): - raise ValueError( - f"`dims_order` for all arrays must be identical if passing in a `slider_dims` argument. " - f"Pass in a argument if the `dims_order` are different for each array." - ) - self._slider_dims: List[str] = [self.dims_order[0][slider_dims]] - - # if dimension specified by str - elif isinstance(slider_dims, str): - if slider_dims not in self.dims_order[0]: - raise ValueError( - f"if `slider_dims` is a , it must be a character found in `dims_order`. " - f"Your `dims_order` characters are: {set(self.dims_order[0])}." - ) - self._slider_dims: List[str] = [slider_dims] - - # multiple sliders, one for each dimension - elif isinstance(slider_dims, list): - self._slider_dims: List[str] = list() - - # make sure window_funcs and frame_apply are dicts if multiple sliders are desired - if (not isinstance(window_funcs, dict)) and (window_funcs is not None): - raise TypeError( - f"`window_funcs` must be a if multiple `slider_dims` are provided. You must specify the " - f"window for each dimension." - ) - if (not isinstance(frame_apply, dict)) and (frame_apply is not None): - raise TypeError( - f"`frame_apply` must be a if multiple `slider_dims` are provided. You must specify a " - f"function for each dimension." - ) - - for sdm in slider_dims: - if isinstance(sdm, int): - ao = np.array([v for v in self.dims_order]) - if not np.all(ao == ao[0]): - raise ValueError( - f"`dims_order` for all arrays must be identical if passing in a `slider_dims` argument. " - f"Pass in a argument if the `dims_order` are different for each array." - ) - # parse int to a str - self.slider_dims.append(self.dims_order[0][sdm]) - - elif isinstance(sdm, str): - if sdm not in self.dims_order[0]: - raise ValueError( - f"if `slider_dims` is a , it must be a character found in `dims_order`. " - f"Your `dims_order` characters are: {set(self.dims_order[0])}." - ) - self.slider_dims.append(sdm) - - else: - raise TypeError( - "If passing a list for `slider_dims` each element must be either an or " - ) - - else: - raise TypeError( - f"`slider_dims` must a , or , you have passed a: {type(slider_dims)}" - ) + # Sliders are made for all dimensions except the image dimensions + self._slider_dims = list() + max_scrollable = max( + [self.n_scrollable_dims[i] for i in range(len(self.n_scrollable_dims))] + ) + for dim in range(max_scrollable): + if dim in ALLOWED_SLIDER_DIMS.keys(): + self.slider_dims.append(ALLOWED_SLIDER_DIMS[dim]) self._frame_apply: Dict[int, callable] = dict() if frame_apply is not None: if callable(frame_apply): - self._frame_apply = {0: frame_apply} + self._frame_apply = frame_apply elif isinstance(frame_apply, dict): self._frame_apply: Dict[int, callable] = dict.fromkeys( @@ -537,13 +486,19 @@ def __init__( self._sliders: Dict[str, Any] = dict() - # get max bound for all data arrays for all dimensions - self._dims_max_bounds: Dict[str, int] = {k: np.inf for k in self.slider_dims} - for _dim in list(self._dims_max_bounds.keys()): - for array, order in zip(self.data, self.dims_order): - self._dims_max_bounds[_dim] = min( - self._dims_max_bounds[_dim], array.shape[order.index(_dim)] - ) + # get max bound for all data arrays for all slider dimensions and ensure compatibility across slider dims + self._dims_max_bounds: Dict[str, int] = {k: 0 for k in self.slider_dims} + for i, _dim in enumerate(list(self._dims_max_bounds.keys())): + for array, partition in zip(self.data, self.n_scrollable_dims): + if partition <= i: + continue + else: + if 0 < self._dims_max_bounds[_dim] != array.shape[i]: + raise ValueError(f"Two arrays differ along dimension {_dim}") + else: + self._dims_max_bounds[_dim] = max( + self._dims_max_bounds[_dim], array.shape[i] + ) grid_plot_kwargs_default = {"controller_ids": "sync"} if grid_plot_kwargs is None: @@ -557,6 +512,7 @@ def __init__( shape=grid_shape, **grid_plot_kwargs_default ) + self._histogram_widget = histogram_widget for data_ix, (d, subplot) in enumerate(zip(self.data, self.gridplot)): if self._names is not None: name = self._names[data_ix] @@ -570,7 +526,7 @@ def __init__( subplot.name = name subplot.set_title(name) - if histogram_widget: + if self._histogram_widget: hlut = HistogramLUT(data=d, image_graphic=ig, name="histogram_lut") subplot.docks["right"].add_graphic(hlut) @@ -607,62 +563,54 @@ def window_funcs(self) -> Dict[str, _WindowFunctions]: return self._window_funcs @window_funcs.setter - def window_funcs(self, sa: Union[int, Dict[str, int]]): - if sa is None: + def window_funcs(self, callable_dict: Dict[str, int]): + if callable_dict is None: self._window_funcs = None # force frame to update self.current_index = self.current_index return - # for a single dim - elif isinstance(sa, tuple): - if len(self.slider_dims) > 1: - raise TypeError( - "Must pass dict argument to window_funcs if using multiple sliders. See the docstring." - ) - if not callable(sa[0]) or not isinstance(sa[1], int): - raise TypeError( - "Tuple argument to `window_funcs` must be in the form of (func, window_size). See the docstring." + elif isinstance(callable_dict, dict): + if not set(callable_dict.keys()).issubset(ALLOWED_WINDOW_DIMS): + raise ValueError( + f"The only allowed keys to window funcs are {list(ALLOWED_WINDOW_DIMS)} " + f"Your window func passed in these keys: {list(callable_dict.keys())}" ) - - dim_str = self.slider_dims[0] - self._window_funcs = dict() - self._window_funcs[dim_str] = _WindowFunctions(self, *sa) - - # for multiple dims - elif isinstance(sa, dict): if not all( - [isinstance(_sa, tuple) or (_sa is None) for _sa in sa.values()] + [ + isinstance(_callable_dict, tuple) + for _callable_dict in callable_dict.values() + ] ): raise TypeError( "dict argument to `window_funcs` must be in the form of: " "`{dimension: (func, window_size)}`. " "See the docstring." ) - for v in sa.values(): - if v is not None: - if not callable(v[0]) or not ( - isinstance(v[1], int) or v[1] is None - ): - raise TypeError( - "dict argument to `window_funcs` must be in the form of: " - "`{dimension: (func, window_size)}`. " - "See the docstring." - ) + for v in callable_dict.values(): + if not callable(v[0]): + raise TypeError( + "dict argument to `window_funcs` must be in the form of: " + "`{dimension: (func, window_size)}`. " + "See the docstring." + ) + if not isinstance(v[1], int): + raise TypeError( + f"dict argument to `window_funcs` must be in the form of: " + "`{dimension: (func, window_size)}`. " + f"where window_size is integer. you passed in {v[1]} for window_size" + ) if not isinstance(self._window_funcs, dict): self._window_funcs = dict() - for k in list(sa.keys()): - if sa[k] is None: - self._window_funcs[k] = None - else: - self._window_funcs[k] = _WindowFunctions(self, *sa[k]) + for k in list(callable_dict.keys()): + self._window_funcs[k] = _WindowFunctions(self, *callable_dict[k]) else: raise TypeError( - f"`window_funcs` must be of type `int` if using a single slider or a dict if using multiple sliders. " - f"You have passed a {type(sa)}. See the docstring." + f"`window_funcs` must be either Nonetype or dict." + f"You have passed a {type(callable_dict)}. See the docstring." ) # force frame to update @@ -684,7 +632,7 @@ def _process_indices( dict in form of {dimension_index: slice_index} For example if an array has shape [1000, 30, 512, 512] corresponding to [t, z, x, y]: To get the 100th timepoint and 3rd z-plane pass: - {"t": 100, "z": 3}, or {0: 100, 1: 3} + {"t": 100, "z": 3} Returns ------- @@ -692,26 +640,32 @@ def _process_indices( array-like, 2D slice """ - indexer = [slice(None)] * self.ndim + + data_ix = None + for i in range(len(self.data)): + if self.data[i] is array: + data_ix = i + break numerical_dims = list() + + # Totally number of dimensions for this specific array + curr_ndim = self.data[data_ix].ndim + + # Initialize slices for each dimension of array + indexer = [slice(None)] * curr_ndim + + # Maps from n_scrollable_dims to one of "", "t", "tz", etc. + curr_scrollable_format = SCROLLABLE_DIMS_ORDER[self.n_scrollable_dims[data_ix]] for dim in list(slice_indices.keys()): - if isinstance(dim, str): - data_ix = None - for i in range(len(self.data)): - if self.data[i] is array: - data_ix = i - break - if data_ix is None: - raise ValueError(f"Given `array` not found in `self.data`") - # get axes order for that specific array - numerical_dim = self.dims_order[data_ix].index(dim) - else: - numerical_dim = dim + if dim not in curr_scrollable_format: + continue + # get axes order for that specific array + numerical_dim = curr_scrollable_format.index(dim) indices_dim = slice_indices[dim] - # takes care of averaging if it was specified + # takes care of index selection (window slicing) for this specific axis indices_dim = self._get_window_indices(data_ix, numerical_dim, indices_dim) # set the indices for this dimension @@ -724,9 +678,9 @@ def _process_indices( if self.window_funcs is not None: a = array for i, dim in enumerate(sorted(numerical_dims)): - dim_str = self.dims_order[data_ix][dim] + dim_str = curr_scrollable_format[dim] dim = dim - i # since we loose a dimension every iteration - _indexer = [slice(None)] * (self.ndim - i) + _indexer = [slice(None)] * (curr_ndim - i) _indexer[dim] = indexer[dim + i] # if the indexer is an int, this dim has no window func @@ -737,7 +691,6 @@ def _process_indices( func = self.window_funcs[dim_str].func window = a[tuple(_indexer)] a = func(window, axis=dim) - # a = np.mean(a[tuple(_indexer)], axis=dim) return a else: return array[tuple(indexer)] @@ -749,7 +702,7 @@ def _get_window_indices(self, data_ix, dim, indices_dim): else: ix = indices_dim - dim_str = self.dims_order[data_ix][dim] + dim_str = SCROLLABLE_DIMS_ORDER[self.n_scrollable_dims[data_ix]][dim] # if no window stuff specified for this dim if dim_str not in self.window_funcs.keys(): @@ -848,9 +801,11 @@ def set_data( self.sliders[key].value = 0 # set slider max according to new data - max_lengths = {"t": np.inf, "z": np.inf} + max_lengths = dict() + for scroll_dim in self.slider_dims: + max_lengths[scroll_dim] = np.inf - if isinstance(new_data, np.ndarray): + if _is_arraylike(new_data): new_data = [new_data] if len(self._data) != len(new_data): @@ -866,16 +821,24 @@ def set_data( f"does not equal current data ndim {current_array.ndim}" ) + # Computes the number of scrollable dims and also validates new_array + new_scrollable_dims = self._get_n_scrollable_dims(new_array, self._rgb[i]) + + if self.n_scrollable_dims[i] != new_scrollable_dims: + raise ValueError( + f"number of dimensions of data arrays must match number of dimensions of " + f"existing data arrays" + ) + # if checks pass, update with new data for i, (new_array, current_array, subplot) in enumerate( zip(new_data, self._data, self.gridplot) ): # check last two dims (x and y) to see if data shape is changing - old_data_shape = self._data[i].shape[-2:] + old_data_shape = self._data[i].shape[-self.n_img_dims[i] :] self._data[i] = new_array - if old_data_shape != new_array.shape[-2:]: - # make a new graphic with the new xy dims + if old_data_shape != new_array.shape[-self.n_img_dims[i] :]: frame = self._process_indices( new_array, slice_indices=self._current_index ) @@ -886,23 +849,31 @@ def set_data( # set hlut tool to use new graphic subplot.docks["right"]["histogram_lut"].image_graphic = new_graphic - # delete old graphic after setting hlut tool to new graphic # this ensures gc subplot.delete_graphic(graphic=subplot["image_widget_managed"]) subplot.insert_graphic(graphic=new_graphic) - if new_array.ndim > 2: - # to set max of time slider, txy or tzxy - max_lengths["t"] = min(max_lengths["t"], new_array.shape[0] - 1) - - if new_array.ndim > 3: # tzxy - max_lengths["z"] = min(max_lengths["z"], new_array.shape[1] - 1) + # Returns "", "t", or "tz" + curr_scrollable_format = SCROLLABLE_DIMS_ORDER[self.n_scrollable_dims[i]] + + for scroll_dim in self.slider_dims: + if scroll_dim in curr_scrollable_format: + new_length = new_array.shape[ + curr_scrollable_format.index(scroll_dim) + ] + if max_lengths[scroll_dim] == np.inf: + max_lengths[scroll_dim] = new_length + elif max_lengths[scroll_dim] != new_length: + raise ValueError( + f"New arrays have differing values along dim {scroll_dim}" + ) # set histogram widget - subplot.docks["right"]["histogram_lut"].set_data( - new_array, reset_vmin_vmax=reset_vmin_vmax - ) + if self._histogram_widget: + subplot.docks["right"]["histogram_lut"].set_data( + new_array, reset_vmin_vmax=reset_vmin_vmax + ) # set slider maxes # TODO: maybe make this stuff a property, like ndims, n_frames etc. and have it set the sliders From 25aa9e3b63c9881660f3cbbdd081e09483b286a3 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Fri, 12 Apr 2024 02:29:19 -0400 Subject: [PATCH 063/185] `Gridplot` -> `Figure` (#479) * figure refactor seems to work * update nbs w.r.t. new figure * update test util * black * remove lingering uses of gridplot * update API doc generation * update contributing * typo Co-authored-by: Caitlin Lewis <69729525+clewis7@users.noreply.github.com> * Iw refactor (#482) * fefactor imwidget, restrict to tzxy, txy, xy, added RGB(A) support (#459) * add new iw test screenshots * update api docs --------- Co-authored-by: Amol Pasarkar * figure refactor seems to work * update nbs w.r.t. new figure * update test util * black * remove lingering uses of gridplot * update API doc generation * update contributing * typos * update iw with FIgure * update api docs * black * update quickstart * update iw examples * update iw type annotations * default cmap iw * remove gp docs * update readme * forgot to delete more api docs * I need to run nbs before committing them * more docs --------- Co-authored-by: Caitlin Lewis <69729525+clewis7@users.noreply.github.com> Co-authored-by: Amol Pasarkar --- CONTRIBUTING.md | 36 +- README.md | 13 +- docs/source/api/graphic_features/Deleted.rst | 33 ++ docs/source/api/graphic_features/index.rst | 1 + docs/source/api/graphics/HeatmapGraphic.rst | 3 + docs/source/api/graphics/ImageGraphic.rst | 3 + docs/source/api/graphics/LineCollection.rst | 3 + docs/source/api/graphics/LineGraphic.rst | 3 + docs/source/api/graphics/LineStack.rst | 3 + docs/source/api/graphics/ScatterGraphic.rst | 3 + docs/source/api/graphics/TextGraphic.rst | 3 + docs/source/api/layouts/figure.rst | 44 +++ docs/source/api/layouts/gridplot.rst | 42 --- docs/source/api/layouts/plot.rst | 73 ----- docs/source/api/layouts/subplot.rst | 3 + .../api/selectors/LinearRegionSelector.rst | 3 + docs/source/api/selectors/LinearSelector.rst | 3 + docs/source/api/selectors/PolygonSelector.rst | 3 + docs/source/api/widgets/ImageWidget.rst | 2 +- docs/source/generate_api.py | 13 +- docs/source/index.rst | 3 +- docs/source/quickstart.ipynb | 203 ++++++------ examples/desktop/gridplot/gridplot.py | 18 +- .../desktop/gridplot/gridplot_non_square.py | 16 +- examples/desktop/heatmap/heatmap.py | 13 +- examples/desktop/heatmap/heatmap_cmap.py | 13 +- examples/desktop/heatmap/heatmap_data.py | 13 +- examples/desktop/heatmap/heatmap_vmin_vmax.py | 13 +- examples/desktop/image/image_cmap.py | 15 +- examples/desktop/image/image_rgb.py | 14 +- examples/desktop/image/image_rgbvminvmax.py | 14 +- examples/desktop/image/image_simple.py | 12 +- examples/desktop/image/image_vminvmax.py | 12 +- examples/desktop/image/image_widget.py | 3 +- examples/desktop/line/line.py | 16 +- examples/desktop/line/line_cmap.py | 12 +- examples/desktop/line/line_colorslice.py | 16 +- examples/desktop/line/line_dataslice.py | 16 +- examples/desktop/line/line_present_scaling.py | 16 +- .../line_collection/line_collection.py | 10 +- .../line_collection_cmap_values.py | 10 +- ...line_collection_cmap_values_qualitative.py | 10 +- .../line_collection/line_collection_colors.py | 10 +- .../desktop/line_collection/line_stack.py | 10 +- examples/desktop/scatter/scatter.py | 12 +- examples/desktop/scatter/scatter_cmap.py | 15 +- .../desktop/scatter/scatter_colorslice.py | 12 +- examples/desktop/scatter/scatter_dataslice.py | 12 +- examples/desktop/scatter/scatter_present.py | 12 +- examples/desktop/scatter/scatter_size.py | 14 +- examples/notebooks/heatmap.ipynb | 6 +- examples/notebooks/image_widget.ipynb | 6 +- examples/notebooks/image_widget_test.ipynb | 80 ++--- .../notebooks/linear_region_selector.ipynb | 41 ++- examples/notebooks/linear_selector.ipynb | 14 +- examples/notebooks/lineplot.ipynb | 24 +- examples/notebooks/lines_cmap.ipynb | 32 +- .../multiprocessing_zmq_plot.ipynb | 12 +- examples/notebooks/nb_test_utils.py | 6 +- examples/notebooks/scatter.ipynb | 26 +- .../notebooks/scatter_sizes_animation.ipynb | 52 +-- examples/notebooks/scatter_sizes_grid.ipynb | 22 +- examples/notebooks/simple.ipynb | 229 +++++++------ .../{gridplot.ipynb => subplots.ipynb} | 80 +++-- ...lot_simple.ipynb => subplots_simple.ipynb} | 85 +++-- examples/notebooks/test_gc.ipynb | 16 +- examples/tests/test_examples.py | 2 +- fastplotlib/__init__.py | 10 +- fastplotlib/layouts/__init__.py | 5 +- .../layouts/{_gridplot.py => _figure.py} | 307 +++++++++++++++++- fastplotlib/layouts/_frame/__init__.py | 1 - fastplotlib/layouts/_frame/_frame.py | 152 --------- fastplotlib/layouts/_frame/_qt_toolbar.py | 250 -------------- fastplotlib/layouts/_plot.py | 68 ---- fastplotlib/layouts/_plot_area.py | 12 +- fastplotlib/layouts/_record_mixin.py | 241 -------------- fastplotlib/layouts/_subplot.py | 14 +- fastplotlib/layouts/_video_writer.py | 82 +++++ fastplotlib/layouts/output/__init__.py | 0 .../{_frame => output}/_ipywidget_toolbar.py | 175 ++-------- fastplotlib/layouts/output/_qt_toolbar.py | 125 +++++++ .../{_frame => output}/_qtoolbar_template.py | 0 .../layouts/{_frame => output}/_toolbar.py | 12 +- .../jupyter_output.py} | 0 .../_qt_output.py => output/qt_output.py} | 2 +- .../layouts/{_frame => output}/qtoolbar.ui | 0 fastplotlib/utils/functions.py | 2 +- .../_image_widget_ipywidget_toolbar.py | 135 ++++++++ .../widgets/_image_widget_qt_toolbar.py | 127 ++++++++ fastplotlib/widgets/image.py | 155 +++++---- tests/test_figure.py | 164 ++++++++++ tests/test_gridplot.py | 164 ---------- 92 files changed, 1839 insertions(+), 1952 deletions(-) create mode 100644 docs/source/api/graphic_features/Deleted.rst create mode 100644 docs/source/api/layouts/figure.rst delete mode 100644 docs/source/api/layouts/gridplot.rst delete mode 100644 docs/source/api/layouts/plot.rst rename examples/notebooks/{gridplot.ipynb => subplots.ipynb} (72%) rename examples/notebooks/{gridplot_simple.ipynb => subplots_simple.ipynb} (69%) rename fastplotlib/layouts/{_gridplot.py => _figure.py} (67%) delete mode 100644 fastplotlib/layouts/_frame/__init__.py delete mode 100644 fastplotlib/layouts/_frame/_frame.py delete mode 100644 fastplotlib/layouts/_frame/_qt_toolbar.py delete mode 100644 fastplotlib/layouts/_plot.py delete mode 100644 fastplotlib/layouts/_record_mixin.py create mode 100644 fastplotlib/layouts/_video_writer.py create mode 100644 fastplotlib/layouts/output/__init__.py rename fastplotlib/layouts/{_frame => output}/_ipywidget_toolbar.py (54%) create mode 100644 fastplotlib/layouts/output/_qt_toolbar.py rename fastplotlib/layouts/{_frame => output}/_qtoolbar_template.py (100%) rename fastplotlib/layouts/{_frame => output}/_toolbar.py (82%) rename fastplotlib/layouts/{_frame/_jupyter_output.py => output/jupyter_output.py} (100%) rename fastplotlib/layouts/{_frame/_qt_output.py => output/qt_output.py} (95%) rename fastplotlib/layouts/{_frame => output}/qtoolbar.ui (100%) create mode 100644 fastplotlib/widgets/_image_widget_ipywidget_toolbar.py create mode 100644 fastplotlib/widgets/_image_widget_qt_toolbar.py create mode 100644 tests/test_figure.py delete mode 100644 tests/test_gridplot.py diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index facca91db..fe9c90242 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -58,9 +58,9 @@ Fastplotlib uses the [`pygfx`](https://github.com/pygfx/pygfx) rendering engine plotting library. Some degree of familiarity with [`pygfx`](https://github.com/pygfx/pygfx) or rendering engines may be useful depending on the type of contribution you're working on. -There are currently 2 major subpackages within `fastplotlib`, `layouts` and `graphics`. The two user-facing public -classes within `layouts` are `Plot` and `GridPlot`. A user is intended to create either a `Plot` or `GridPlot`, and -then add *Graphics* to that layout, such as an `ImageGraphic`, `LineGraphic`, etc. +There are currently 2 major subpackages within `fastplotlib`, `layouts` and `graphics`. The user-facing public +class within `layouts` is `Figure`. A user is intended to create a `Figure`, and +then add *Graphics* to subplots within that `Figure`. ### Graphics @@ -70,7 +70,7 @@ fastplotlib graphics, such as `ImageGraphic`, `ScatterGraphic`, etc. inherit fro These might change in the future (ex. `Graphic.position_x` etc.). All graphics can be given a string name for the user's convenience. This allows graphics to be easily accessed from -plots, ex: `plot["some_image"]`. +plots, ex: `subplot["some_image"]`. All graphics contain a `world_object` property which is just the `pygfx.WorldObject` that this graphic uses. Fastplotlib keeps a *private* global dictionary of all `WorldObject` instances and users are only given a weakref proxy to this world object. @@ -119,8 +119,8 @@ after the aforementioned `Input` class PR in `pygfx` and after https://github.co #### PlotArea -This is the main base class within layouts. Every kind of "plot area", whether it's a single `Plot`, subplots within a -`GridPlot`, or `Dock` area, use `PlotArea` in some way. +This is the main base class within layouts. Subplots within a `Figure` and `Dock` areas within a `Subplot`, +inherit from `PlotArea`. `PlotArea` has the following key properties that allow it to be a "plot area" that can be used to view graphical objects: @@ -135,10 +135,10 @@ Abstract method that must be implemented in subclasses: * get_rect - musut return [x, y, width, height] that defines the viewport rect for this `PlotArea` -Properties specifically used by subplots in a gridplot: +Properties specifically used by subplots in a Figure: -* parent - A parent if relevant, used by individual `Subplots` in `GridPlot`, and by `Dock` which are "docked" subplots at the edges of a subplot. -* position - if a subplot within a gridplot, it is the position of this subplot within the `GridPlot` +* parent - A parent if relevant, used by individual `Subplots` in `Figure`, and by `Dock` which are "docked" subplots at the edges of a subplot. +* position - if a subplot within a Figure, it is the position of this subplot within the `Figure` Other important properties: @@ -182,18 +182,13 @@ Subplot has one property that is not in `PlotArea`: The key method in `Subplot` is an implementation of `get_rect` that returns the viewport rect for this subplot. -#### Plot, GridPlot, and Frame +#### Figure -Now that we have understood `PlotArea` and `Subplot` we need a way for the user to create either single plots or gridplots -and display them! +Now that we have understood `PlotArea` and `Subplot` we need a way for the user to create them! -There's one more class to talk about, `Frame`. This is a class that "frames" a `Plot` or `GridPlot`. Depending on -whether the plot's `Canvas` is a Qt or jupyter canvas, `Frame.show()` will create a plot toolbar and place this toolbar -below the `Canvas`. If using a glfw canvas it just returns the canvas. - -`Plot` and `GridPlot` both inherit from `Frame` which gives them `show()`. `Plot` is just a single `Subplot` with the -addition of `Frame`. `GridPlot.__init__` basically does a lot of parsing of user arguments to determine how to create -the subplots. All subplots within a `GridPlot` share the same canvas and use different viewports to create the subplots. +A `Figure` contains a grid of subplot and has methods such as `show()` to output the figure. +`Figure.__init__` basically does a lot of parsing of user arguments to determine how to create +the subplots. All subplots within a `Figure` share the same canvas and use different viewports to create the subplots. ## Tests in detail @@ -216,6 +211,9 @@ ground-truth image are within that tolerance the test will pass. To run tests: ```bash +# tests basic backend functionality +WGPU_FORCE_OFFSCREEN=1 pytest -v -s tests/ + # desktop examples pytest -v examples diff --git a/README.md b/README.md index 37a2d0779..64e1649e8 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,6 @@ [![CI](https://github.com/kushalkolar/fastplotlib/actions/workflows/ci.yml/badge.svg)](https://github.com/kushalkolar/fastplotlib/actions/workflows/ci.yml) [![PyPI version](https://badge.fury.io/py/fastplotlib.svg)](https://badge.fury.io/py/fastplotlib) [![Documentation Status](https://readthedocs.org/projects/fastplotlib/badge/?version=latest)](https://fastplotlib.readthedocs.io/en/latest/?badge=latest) -[![Gitter](https://badges.gitter.im/fastplotlib/community.svg)](https://gitter.im/fastplotlib/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [**Installation**](https://github.com/kushalkolar/fastplotlib#installation) | [**GPU Drivers**](https://github.com/kushalkolar/fastplotlib#graphics-drivers) | @@ -15,7 +14,7 @@ [**Examples**](https://github.com/kushalkolar/fastplotlib#examples) | [**Contributing**](https://github.com/kushalkolar/fastplotlib#heart-contributing) -Next-gen plotting library built using the [`pygfx`](https://github.com/pygfx/pygfx) rendering engine that can utilize [Vulkan](https://en.wikipedia.org/wiki/Vulkan), [DX12](https://en.wikipedia.org/wiki/DirectX#DirectX_12), or [Metal](https://developer.apple.com/metal/) via WGPU, so it is very fast! We also aim to be an expressive plotting library that enables rapid prototyping for large scale explorative scientific visualization. +Next-gen plotting library built using the [`pygfx`](https://github.com/pygfx/pygfx) rendering engine that can utilize [Vulkan](https://en.wikipedia.org/wiki/Vulkan), [DX12](https://en.wikipedia.org/wiki/DirectX#DirectX_12), or [Metal](https://developer.apple.com/metal/) via WGPU, so it is very fast! `fastplotlib` also aims to be an expressive plotting library that enables rapid prototyping for large scale explorative scientific visualization. ![scipy-fpl](https://github.com/fastplotlib/fastplotlib/assets/9403332/b981a54c-05f9-443f-a8e4-52cd01cd802a) @@ -23,8 +22,8 @@ Next-gen plotting library built using the [`pygfx`](https://github.com/pygfx/pyg [![fpl_thumbnail](http://i3.ytimg.com/vi/Q-UJpAqljsU/hqdefault.jpg)](https://www.youtube.com/watch?v=Q-UJpAqljsU) -Notebooks from talk: https://github.com/fastplotlib/fastplotlib-scipy2023 - +Note that the API is currently evolving quickly. We recommend using the latest notebooks from the repo but the general +concepts are similar to those from the API shown in the video. # Supported frameworks @@ -36,9 +35,9 @@ Notebooks from talk: https://github.com/fastplotlib/fastplotlib-scipy2023 :heavy_check_mark: `wxPython` **Notes:**\ -:heavy_check_mark: Non-blocking Qt output is supported in ipython and notebooks by using [`%gui qt`](https://ipython.readthedocs.io/en/stable/interactive/magics.html#magic-gui) before creating plots. This hook only supports pyqt6 at the moment.\ +:heavy_check_mark: Non-blocking Qt/PySide output is supported in ipython and notebooks by using [`%gui qt`](https://ipython.readthedocs.io/en/stable/interactive/magics.html#magic-gui). This **must** be called *before* importing `fastplotlib`! :grey_exclamation: We do not officially support `jupyter notebook` through `jupyter_rfb`, this may change with notebook v7\ -:disappointed: [`jupyter_rfb`](https://github.com/vispy/jupyter_rfb) does not work in collab yet, see https://github.com/vispy/jupyter_rfb/pull/77 +:disappointed: [`jupyter_rfb`](https://github.com/vispy/jupyter_rfb) does not work in collab, see https://github.com/vispy/jupyter_rfb/pull/77 > **Note** > @@ -169,4 +168,4 @@ WGPU uses Metal instead of Vulkan on Mac. You will need at least Mac OSX 10.13. We welcome contributions! See the contributing guide: https://github.com/kushalkolar/fastplotlib/blob/main/CONTRIBUTING.md -You can also take a look at our [**Roadmap for 2024**](https://github.com/kushalkolar/fastplotlib/issues/55) and [**Issues**](https://github.com/kushalkolar/fastplotlib/issues) for ideas on how to contribute! +You can also take a look at our [**Roadmap for 2025**](https://github.com/kushalkolar/fastplotlib/issues/55) and [**Issues**](https://github.com/kushalkolar/fastplotlib/issues) for ideas on how to contribute! diff --git a/docs/source/api/graphic_features/Deleted.rst b/docs/source/api/graphic_features/Deleted.rst new file mode 100644 index 000000000..998e94588 --- /dev/null +++ b/docs/source/api/graphic_features/Deleted.rst @@ -0,0 +1,33 @@ +.. _api.Deleted: + +Deleted +******* + +======= +Deleted +======= +.. currentmodule:: fastplotlib.graphics._features + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: Deleted_api + + Deleted + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: Deleted_api + + +Methods +~~~~~~~ +.. autosummary:: + :toctree: Deleted_api + + Deleted.add_event_handler + Deleted.block_events + Deleted.clear_event_handlers + Deleted.remove_event_handler + diff --git a/docs/source/api/graphic_features/index.rst b/docs/source/api/graphic_features/index.rst index 1c4b33392..06e3119e5 100644 --- a/docs/source/api/graphic_features/index.rst +++ b/docs/source/api/graphic_features/index.rst @@ -20,3 +20,4 @@ Graphic Features to_gpu_supported_dtype LinearSelectionFeature LinearRegionSelectionFeature + Deleted diff --git a/docs/source/api/graphics/HeatmapGraphic.rst b/docs/source/api/graphics/HeatmapGraphic.rst index 3bd2f2baa..ffa86eb16 100644 --- a/docs/source/api/graphics/HeatmapGraphic.rst +++ b/docs/source/api/graphics/HeatmapGraphic.rst @@ -21,10 +21,12 @@ Properties :toctree: HeatmapGraphic_api HeatmapGraphic.children + HeatmapGraphic.name HeatmapGraphic.position HeatmapGraphic.position_x HeatmapGraphic.position_y HeatmapGraphic.position_z + HeatmapGraphic.rotation HeatmapGraphic.visible HeatmapGraphic.world_object @@ -37,5 +39,6 @@ Methods HeatmapGraphic.add_linear_selector HeatmapGraphic.link HeatmapGraphic.reset_feature + HeatmapGraphic.rotate HeatmapGraphic.set_feature diff --git a/docs/source/api/graphics/ImageGraphic.rst b/docs/source/api/graphics/ImageGraphic.rst index 871462701..00b27340d 100644 --- a/docs/source/api/graphics/ImageGraphic.rst +++ b/docs/source/api/graphics/ImageGraphic.rst @@ -21,10 +21,12 @@ Properties :toctree: ImageGraphic_api ImageGraphic.children + ImageGraphic.name ImageGraphic.position ImageGraphic.position_x ImageGraphic.position_y ImageGraphic.position_z + ImageGraphic.rotation ImageGraphic.visible ImageGraphic.world_object @@ -37,5 +39,6 @@ Methods ImageGraphic.add_linear_selector ImageGraphic.link ImageGraphic.reset_feature + ImageGraphic.rotate ImageGraphic.set_feature diff --git a/docs/source/api/graphics/LineCollection.rst b/docs/source/api/graphics/LineCollection.rst index 3f67feed9..8d10d8376 100644 --- a/docs/source/api/graphics/LineCollection.rst +++ b/docs/source/api/graphics/LineCollection.rst @@ -24,10 +24,12 @@ Properties LineCollection.cmap LineCollection.cmap_values LineCollection.graphics + LineCollection.name LineCollection.position LineCollection.position_x LineCollection.position_y LineCollection.position_z + LineCollection.rotation LineCollection.visible LineCollection.world_object @@ -42,5 +44,6 @@ Methods LineCollection.link LineCollection.remove_graphic LineCollection.reset_feature + LineCollection.rotate LineCollection.set_feature diff --git a/docs/source/api/graphics/LineGraphic.rst b/docs/source/api/graphics/LineGraphic.rst index 4aae4bbee..8b6fedf22 100644 --- a/docs/source/api/graphics/LineGraphic.rst +++ b/docs/source/api/graphics/LineGraphic.rst @@ -21,10 +21,12 @@ Properties :toctree: LineGraphic_api LineGraphic.children + LineGraphic.name LineGraphic.position LineGraphic.position_x LineGraphic.position_y LineGraphic.position_z + LineGraphic.rotation LineGraphic.visible LineGraphic.world_object @@ -37,5 +39,6 @@ Methods LineGraphic.add_linear_selector LineGraphic.link LineGraphic.reset_feature + LineGraphic.rotate LineGraphic.set_feature diff --git a/docs/source/api/graphics/LineStack.rst b/docs/source/api/graphics/LineStack.rst index 36ae6808e..a39db46f8 100644 --- a/docs/source/api/graphics/LineStack.rst +++ b/docs/source/api/graphics/LineStack.rst @@ -24,10 +24,12 @@ Properties LineStack.cmap LineStack.cmap_values LineStack.graphics + LineStack.name LineStack.position LineStack.position_x LineStack.position_y LineStack.position_z + LineStack.rotation LineStack.visible LineStack.world_object @@ -42,5 +44,6 @@ Methods LineStack.link LineStack.remove_graphic LineStack.reset_feature + LineStack.rotate LineStack.set_feature diff --git a/docs/source/api/graphics/ScatterGraphic.rst b/docs/source/api/graphics/ScatterGraphic.rst index 3c4bf3909..44d87d008 100644 --- a/docs/source/api/graphics/ScatterGraphic.rst +++ b/docs/source/api/graphics/ScatterGraphic.rst @@ -21,10 +21,12 @@ Properties :toctree: ScatterGraphic_api ScatterGraphic.children + ScatterGraphic.name ScatterGraphic.position ScatterGraphic.position_x ScatterGraphic.position_y ScatterGraphic.position_z + ScatterGraphic.rotation ScatterGraphic.visible ScatterGraphic.world_object @@ -33,4 +35,5 @@ Methods .. autosummary:: :toctree: ScatterGraphic_api + ScatterGraphic.rotate diff --git a/docs/source/api/graphics/TextGraphic.rst b/docs/source/api/graphics/TextGraphic.rst index 6290dcc2e..23425cf41 100644 --- a/docs/source/api/graphics/TextGraphic.rst +++ b/docs/source/api/graphics/TextGraphic.rst @@ -22,12 +22,14 @@ Properties TextGraphic.children TextGraphic.face_color + TextGraphic.name TextGraphic.outline_color TextGraphic.outline_size TextGraphic.position TextGraphic.position_x TextGraphic.position_y TextGraphic.position_z + TextGraphic.rotation TextGraphic.text TextGraphic.text_size TextGraphic.visible @@ -38,4 +40,5 @@ Methods .. autosummary:: :toctree: TextGraphic_api + TextGraphic.rotate diff --git a/docs/source/api/layouts/figure.rst b/docs/source/api/layouts/figure.rst new file mode 100644 index 000000000..a2d5e5758 --- /dev/null +++ b/docs/source/api/layouts/figure.rst @@ -0,0 +1,44 @@ +.. _api.Figure: + +Figure +****** + +====== +Figure +====== +.. currentmodule:: fastplotlib + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: Figure_api + + Figure + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: Figure_api + + Figure.cameras + Figure.canvas + Figure.controllers + Figure.names + Figure.output + Figure.renderer + Figure.shape + Figure.toolbar + +Methods +~~~~~~~ +.. autosummary:: + :toctree: Figure_api + + Figure.add_animations + Figure.clear + Figure.close + Figure.remove_animation + Figure.render + Figure.show + Figure.start_render + diff --git a/docs/source/api/layouts/gridplot.rst b/docs/source/api/layouts/gridplot.rst deleted file mode 100644 index b5b03bfa4..000000000 --- a/docs/source/api/layouts/gridplot.rst +++ /dev/null @@ -1,42 +0,0 @@ -.. _api.GridPlot: - -GridPlot -******** - -======== -GridPlot -======== -.. currentmodule:: fastplotlib - -Constructor -~~~~~~~~~~~ -.. autosummary:: - :toctree: GridPlot_api - - GridPlot - -Properties -~~~~~~~~~~ -.. autosummary:: - :toctree: GridPlot_api - - GridPlot.canvas - GridPlot.renderer - GridPlot.toolbar - GridPlot.widget - -Methods -~~~~~~~ -.. autosummary:: - :toctree: GridPlot_api - - GridPlot.add_animations - GridPlot.clear - GridPlot.close - GridPlot.record_start - GridPlot.record_stop - GridPlot.remove_animation - GridPlot.render - GridPlot.show - GridPlot.start_render - diff --git a/docs/source/api/layouts/plot.rst b/docs/source/api/layouts/plot.rst deleted file mode 100644 index bd38720b4..000000000 --- a/docs/source/api/layouts/plot.rst +++ /dev/null @@ -1,73 +0,0 @@ -.. _api.Plot: - -Plot -**** - -==== -Plot -==== -.. currentmodule:: fastplotlib - -Constructor -~~~~~~~~~~~ -.. autosummary:: - :toctree: Plot_api - - Plot - -Properties -~~~~~~~~~~ -.. autosummary:: - :toctree: Plot_api - - Plot.camera - Plot.canvas - Plot.controller - Plot.docks - Plot.graphics - Plot.name - Plot.parent - Plot.position - Plot.renderer - Plot.scene - Plot.selectors - Plot.toolbar - Plot.viewport - Plot.widget - -Methods -~~~~~~~ -.. autosummary:: - :toctree: Plot_api - - Plot.add_animations - Plot.add_graphic - Plot.add_heatmap - Plot.add_image - Plot.add_line - Plot.add_line_collection - Plot.add_line_stack - Plot.add_scatter - Plot.add_text - Plot.auto_scale - Plot.center_graphic - Plot.center_scene - Plot.center_title - Plot.clear - Plot.close - Plot.delete_graphic - Plot.get_rect - Plot.insert_graphic - Plot.map_screen_to_world - Plot.record_start - Plot.record_stop - Plot.remove_animation - Plot.remove_graphic - Plot.render - Plot.set_axes_visibility - Plot.set_grid_visibility - Plot.set_title - Plot.set_viewport_rect - Plot.show - Plot.start_render - diff --git a/docs/source/api/layouts/subplot.rst b/docs/source/api/layouts/subplot.rst index c61c46e05..91884557a 100644 --- a/docs/source/api/layouts/subplot.rst +++ b/docs/source/api/layouts/subplot.rst @@ -25,7 +25,9 @@ Properties Subplot.controller Subplot.docks Subplot.graphics + Subplot.legends Subplot.name + Subplot.objects Subplot.parent Subplot.position Subplot.renderer @@ -54,6 +56,7 @@ Methods Subplot.clear Subplot.delete_graphic Subplot.get_rect + Subplot.get_refcounts Subplot.insert_graphic Subplot.map_screen_to_world Subplot.remove_animation diff --git a/docs/source/api/selectors/LinearRegionSelector.rst b/docs/source/api/selectors/LinearRegionSelector.rst index ce0d8d9b6..1b59e80c9 100644 --- a/docs/source/api/selectors/LinearRegionSelector.rst +++ b/docs/source/api/selectors/LinearRegionSelector.rst @@ -22,10 +22,12 @@ Properties LinearRegionSelector.children LinearRegionSelector.limits + LinearRegionSelector.name LinearRegionSelector.position LinearRegionSelector.position_x LinearRegionSelector.position_y LinearRegionSelector.position_z + LinearRegionSelector.rotation LinearRegionSelector.visible LinearRegionSelector.world_object @@ -39,4 +41,5 @@ Methods LinearRegionSelector.get_selected_index LinearRegionSelector.get_selected_indices LinearRegionSelector.make_ipywidget_slider + LinearRegionSelector.rotate diff --git a/docs/source/api/selectors/LinearSelector.rst b/docs/source/api/selectors/LinearSelector.rst index 4056bcc46..3278559d0 100644 --- a/docs/source/api/selectors/LinearSelector.rst +++ b/docs/source/api/selectors/LinearSelector.rst @@ -22,10 +22,12 @@ Properties LinearSelector.children LinearSelector.limits + LinearSelector.name LinearSelector.position LinearSelector.position_x LinearSelector.position_y LinearSelector.position_z + LinearSelector.rotation LinearSelector.visible LinearSelector.world_object @@ -39,4 +41,5 @@ Methods LinearSelector.get_selected_index LinearSelector.get_selected_indices LinearSelector.make_ipywidget_slider + LinearSelector.rotate diff --git a/docs/source/api/selectors/PolygonSelector.rst b/docs/source/api/selectors/PolygonSelector.rst index aaa434dbf..8de87ec74 100644 --- a/docs/source/api/selectors/PolygonSelector.rst +++ b/docs/source/api/selectors/PolygonSelector.rst @@ -21,10 +21,12 @@ Properties :toctree: PolygonSelector_api PolygonSelector.children + PolygonSelector.name PolygonSelector.position PolygonSelector.position_x PolygonSelector.position_y PolygonSelector.position_z + PolygonSelector.rotation PolygonSelector.visible PolygonSelector.world_object @@ -37,4 +39,5 @@ Methods PolygonSelector.get_selected_index PolygonSelector.get_selected_indices PolygonSelector.get_vertices + PolygonSelector.rotate diff --git a/docs/source/api/widgets/ImageWidget.rst b/docs/source/api/widgets/ImageWidget.rst index 2b4708007..3ca384968 100644 --- a/docs/source/api/widgets/ImageWidget.rst +++ b/docs/source/api/widgets/ImageWidget.rst @@ -23,8 +23,8 @@ Properties ImageWidget.cmap ImageWidget.current_index ImageWidget.data + ImageWidget.figure ImageWidget.frame_apply - ImageWidget.gridplot ImageWidget.managed_graphics ImageWidget.n_img_dims ImageWidget.n_scrollable_dims diff --git a/docs/source/generate_api.py b/docs/source/generate_api.py index 19b739d1b..a5f668130 100644 --- a/docs/source/generate_api.py +++ b/docs/source/generate_api.py @@ -142,17 +142,10 @@ def generate_page( def main(): generate_page( - page_name="Plot", - classes=[fastplotlib.Plot], + page_name="Figure", + classes=[fastplotlib.Figure], modules=["fastplotlib"], - source_path=LAYOUTS_DIR.joinpath("plot.rst"), - ) - - generate_page( - page_name="GridPlot", - classes=[fastplotlib.GridPlot], - modules=["fastplotlib"], - source_path=LAYOUTS_DIR.joinpath("gridplot.rst"), + source_path=LAYOUTS_DIR.joinpath("figure.rst"), ) generate_page( diff --git a/docs/source/index.rst b/docs/source/index.rst index 9dbd30783..0ceb146e4 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -22,8 +22,7 @@ Welcome to fastplotlib's documentation! :maxdepth: 1 :caption: API - Plot - Gridplot + Figure Subplot Graphics Graphic Features diff --git a/docs/source/quickstart.ipynb b/docs/source/quickstart.ipynb index 0de4667bf..6a892399e 100644 --- a/docs/source/quickstart.ipynb +++ b/docs/source/quickstart.ipynb @@ -79,17 +79,17 @@ }, "outputs": [], "source": [ - "# create a `Plot` instance\n", - "plot = fpl.Plot()\n", + "# create a `Figure` instance\n", + "fig = fpl.Figure()\n", "\n", "# get a grayscale image\n", "data = iio.imread(\"imageio:camera.png\")\n", "\n", "# plot the image data\n", - "image_graphic = plot.add_image(data=data, name=\"sample-image\")\n", + "image_graphic = fig[0, 0].add_image(data=data, name=\"sample-image\")\n", "\n", "# show the plot\n", - "plot.show()" + "fig.show()" ] }, { @@ -117,7 +117,7 @@ }, "outputs": [], "source": [ - "plot.canvas.snapshot()" + "fig.canvas.snapshot()" ] }, { @@ -149,7 +149,7 @@ }, "outputs": [], "source": [ - "plot.canvas.snapshot()" + "fig.canvas.snapshot()" ] }, { @@ -198,7 +198,7 @@ }, "outputs": [], "source": [ - "plot.canvas.snapshot()" + "fig.canvas.snapshot()" ] }, { @@ -230,7 +230,7 @@ }, "outputs": [], "source": [ - "plot.canvas.snapshot()" + "fig.canvas.snapshot()" ] }, { @@ -263,7 +263,7 @@ }, "outputs": [], "source": [ - "plot.canvas.snapshot()" + "fig.canvas.snapshot()" ] }, { @@ -331,7 +331,7 @@ }, "outputs": [], "source": [ - "plot.canvas.snapshot()" + "fig.canvas.snapshot()" ] }, { @@ -363,7 +363,7 @@ }, "outputs": [], "source": [ - "plot.canvas.snapshot()" + "fig.canvas.snapshot()" ] }, { @@ -391,7 +391,7 @@ }, "outputs": [], "source": [ - "plot" + "fig" ] }, { @@ -403,7 +403,7 @@ }, "outputs": [], "source": [ - "plot[\"sample-image\"]" + "fig[0, 0][\"sample-image\"]" ] }, { @@ -423,7 +423,7 @@ }, "outputs": [], "source": [ - "plot.graphics" + "fig[0, 0].graphics" ] }, { @@ -435,7 +435,7 @@ }, "outputs": [], "source": [ - "plot.graphics[0]" + "fig[0, 0].graphics[0]" ] }, { @@ -467,7 +467,7 @@ }, "outputs": [], "source": [ - "image_graphic == plot[\"sample-image\"]" + "image_graphic is fig[0, 0][\"sample-image\"]" ] }, { @@ -489,11 +489,11 @@ }, "outputs": [], "source": [ - "plot_rgb = fpl.Plot()\n", + "fig_rgb = fpl.Figure()\n", "\n", - "plot_rgb.add_image(new_data, name=\"rgb-image\")\n", + "fig_rgb[0, 0].add_image(new_data, name=\"rgb-image\")\n", "\n", - "plot_rgb.show()" + "fig_rgb.show()" ] }, { @@ -505,7 +505,7 @@ }, "outputs": [], "source": [ - "plot_rgb.canvas.snapshot()" + "fig_rgb.canvas.snapshot()" ] }, { @@ -525,7 +525,7 @@ }, "outputs": [], "source": [ - "plot_rgb[\"rgb-image\"].cmap.vmin = 100" + "fig_rgb[0, 0][\"rgb-image\"].cmap.vmin = 100" ] }, { @@ -537,7 +537,7 @@ }, "outputs": [], "source": [ - "plot_rgb.canvas.snapshot()" + "fig_rgb.canvas.snapshot()" ] }, { @@ -561,28 +561,26 @@ }, "outputs": [], "source": [ - "# create another `Plot` instance\n", - "plot_v = fpl.Plot()\n", - "\n", - "plot.canvas.max_buffered_frames = 1\n", + "# create another `Figure` instance\n", + "fig_vid = fpl.Figure()\n", "\n", "# make some random data again\n", "data = np.random.rand(512, 512)\n", "\n", "# plot the data\n", - "plot_v.add_image(data=data, name=\"random-image\")\n", + "fig_vid[0, 0].add_image(data=data, name=\"random-image\")\n", "\n", "# a function to update the image_graphic\n", - "# a plot will pass its plot instance to the animation function as an argument\n", - "def update_data(plot_instance):\n", + "# a subplot will pass its instance to the animation function as an argument\n", + "def update_data(subplot):\n", " new_data = np.random.rand(512, 512)\n", - " plot_instance[\"random-image\"].data = new_data\n", + " subplot[\"random-image\"].data = new_data\n", "\n", - "#add this as an animation function\n", - "plot_v.add_animations(update_data)\n", + "#add this as an animation function to the subplot\n", + "fig_vid[0, 0].add_animations(update_data)\n", "\n", "# show the plot\n", - "plot_v.show()" + "fig_vid.show()" ] }, { @@ -602,11 +600,11 @@ "metadata": {}, "outputs": [], "source": [ - "plot_sync = fpl.Plot(controller=plot_v.controller)\n", + "fig_sync = fpl.Figure(controllers=fig_vid.controllers)\n", "\n", "data = np.random.rand(512, 512)\n", "\n", - "image_graphic_instance = plot_sync.add_image(data=data, cmap=\"viridis\")\n", + "image_graphic_instance = fig_sync[0, 0].add_image(data=data, cmap=\"viridis\")\n", "\n", "# you will need to define a new animation function for this graphic\n", "def update_data_2():\n", @@ -614,9 +612,10 @@ " # alternatively, you can use the stored reference to the graphic as well instead of indexing the Plot\n", " image_graphic_instance.data = new_data\n", "\n", - "plot_sync.add_animations(update_data_2)\n", + "# add the animation function to the figure instead of the subplot\n", + "fig_sync.add_animations(update_data_2)\n", "\n", - "plot_sync.show()" + "fig_sync.show()" ] }, { @@ -718,19 +717,19 @@ "outputs": [], "source": [ "# Create a plot instance\n", - "plot_l = fpl.Plot()\n", + "fig_line = fpl.Figure()\n", "\n", "# plot sine wave, use a single color\n", - "sine_graphic = plot_l.add_line(data=sine, thickness=5, colors=\"magenta\")\n", + "sine_graphic = fig_line[0, 0].add_line(data=sine, thickness=5, colors=\"magenta\")\n", "\n", "# you can also use colormaps for lines!\n", - "cosine_graphic = plot_l.add_line(data=cosine, thickness=12, cmap=\"autumn\")\n", + "cosine_graphic = fig_line[0, 0].add_line(data=cosine, thickness=12, cmap=\"autumn\")\n", "\n", "# or a list of colors for each datapoint\n", "colors = [\"r\"] * 25 + [\"purple\"] * 25 + [\"y\"] * 25 + [\"b\"] * 25\n", - "sinc_graphic = plot_l.add_line(data=sinc, thickness=5, colors = colors)\n", + "sinc_graphic = fig_line[0, 0].add_line(data=sinc, thickness=5, colors = colors)\n", "\n", - "plot_l.show()" + "fig_line.show()" ] }, { @@ -754,7 +753,7 @@ }, "outputs": [], "source": [ - "plot_l.camera.maintain_aspect = False" + "fig_line[0, 0].camera.maintain_aspect = False" ] }, { @@ -774,7 +773,7 @@ }, "outputs": [], "source": [ - "plot_l.auto_scale(maintain_aspect=True)" + "fig_line[0, 0].auto_scale(maintain_aspect=True)" ] }, { @@ -822,7 +821,7 @@ }, "outputs": [], "source": [ - "plot_l.canvas.snapshot()" + "fig_line.canvas.snapshot()" ] }, { @@ -868,7 +867,7 @@ }, "outputs": [], "source": [ - "plot_l.canvas.snapshot()" + "fig_line.canvas.snapshot()" ] }, { @@ -909,7 +908,7 @@ }, "outputs": [], "source": [ - "plot_l.canvas.snapshot()" + "fig_line.canvas.snapshot()" ] }, { @@ -939,7 +938,7 @@ }, "outputs": [], "source": [ - "plot_l.canvas.snapshot()" + "fig_line.canvas.snapshot()" ] }, { @@ -961,7 +960,7 @@ }, "outputs": [], "source": [ - "plot_l.canvas.snapshot()" + "fig_line.canvas.snapshot()" ] }, { @@ -979,7 +978,7 @@ "metadata": {}, "outputs": [], "source": [ - "sinc_graphic.present.add_event_handler(plot_l.auto_scale)" + "sinc_graphic.present.add_event_handler(fig_line[0, 0].auto_scale)" ] }, { @@ -1001,7 +1000,7 @@ }, "outputs": [], "source": [ - "plot_l.canvas.snapshot()" + "fig_line.canvas.snapshot()" ] }, { @@ -1023,7 +1022,7 @@ }, "outputs": [], "source": [ - "plot_l.canvas.snapshot()" + "fig_line.canvas.snapshot()" ] }, { @@ -1045,11 +1044,11 @@ "source": [ "img = np.random.rand(20, 100)\n", "\n", - "plot_l.add_image(img, name=\"image\", cmap=\"gray\")\n", + "fig_line[0, 0].add_image(img, name=\"image\", cmap=\"gray\")\n", "\n", "# z axis position -1 so it is below all the lines\n", - "plot_l[\"image\"].position_z = -1\n", - "plot_l[\"image\"].position_x = -50" + "fig_line[0, 0][\"image\"].position_z = -1\n", + "fig_line[0, 0][\"image\"].position_x = -50" ] }, { @@ -1061,7 +1060,7 @@ }, "outputs": [], "source": [ - "plot_l.canvas.snapshot()" + "fig_line.canvas.snapshot()" ] }, { @@ -1080,7 +1079,7 @@ "outputs": [], "source": [ "# just set the camera as \"3d\", the rest is basically the same :D \n", - "plot_l3d = fpl.Plot(camera='3d')\n", + "fig_line_3d = fpl.Figure(cameras='3d')\n", "\n", "# create a spiral\n", "phi = np.linspace(0, 30, 200)\n", @@ -1093,9 +1092,9 @@ "# note: you usually mix 3D and 2D graphics on the same plot\n", "spiral = np.dstack([xs, ys, zs])[0]\n", "\n", - "plot_l3d.add_line(data=spiral, thickness=2, cmap='winter')\n", + "fig_line_3d[0, 0].add_line(data=spiral, thickness=2, cmap='winter')\n", "\n", - "plot_l3d.show()" + "fig_line_3d.show()" ] }, { @@ -1107,7 +1106,7 @@ }, "outputs": [], "source": [ - "plot_l3d.auto_scale(maintain_aspect=True)" + "fig_line_3d[0, 0].auto_scale(maintain_aspect=True)" ] }, { @@ -1157,12 +1156,12 @@ "colors = [\"yellow\"] * n_points + [\"cyan\"] * n_points + [\"magenta\"] * n_points\n", "\n", "# create plot\n", - "plot_s = fpl.Plot()\n", + "fig_scatter = fpl.Figure()\n", "\n", "# use an alpha value since this will be a lot of points\n", - "scatter_graphic = plot_s.add_scatter(data=cloud, sizes=3, colors=colors, alpha=0.7)\n", + "scatter_graphic = fig_scatter[0, 0].add_scatter(data=cloud, sizes=3, colors=colors, alpha=0.7)\n", "\n", - "plot_s.show()" + "fig_scatter.show()" ] }, { @@ -1193,7 +1192,7 @@ }, "outputs": [], "source": [ - "plot_s.canvas.snapshot()" + "fig_scatter.canvas.snapshot()" ] }, { @@ -1216,7 +1215,7 @@ }, "outputs": [], "source": [ - "plot_s.canvas.snapshot()" + "fig_scatter.canvas.snapshot()" ] }, { @@ -1239,7 +1238,7 @@ }, "outputs": [], "source": [ - "plot_s.canvas.snapshot()" + "fig_scatter.canvas.snapshot()" ] }, { @@ -1262,7 +1261,7 @@ }, "outputs": [], "source": [ - "plot_s.canvas.snapshot()" + "fig_scatter.canvas.snapshot()" ] }, { @@ -1285,7 +1284,7 @@ }, "outputs": [], "source": [ - "plot_s.canvas.snapshot()" + "fig_scatter.canvas.snapshot()" ] }, { @@ -1318,11 +1317,7 @@ "id": "a26c0063-b7e0-4f36-bb14-db06bafa31aa", "metadata": {}, "source": [ - "## Gridplot\n", - "\n", - "Subplots within a `GridPlot` behave the same as simple `Plot` instances! \n", - "\n", - "💡 `Plot` is actually a subclass of `Subplot`!" + "## More subplots" ] }, { @@ -1334,11 +1329,11 @@ }, "outputs": [], "source": [ - "# GridPlot of shape 2 x 3 with all controllers synced\n", - "grid_plot = fpl.GridPlot(shape=(2, 3), controller_ids=\"sync\")\n", + "# Figure of shape 2 x 3 with all controllers synced\n", + "figure_grid = fpl.Figure(shape=(2, 3), controller_ids=\"sync\")\n", "\n", "# Make a random image graphic for each subplot\n", - "for subplot in grid_plot:\n", + "for subplot in figure_grid:\n", " # create image data\n", " data = np.random.rand(512, 512)\n", " # add an image to the subplot\n", @@ -1346,17 +1341,17 @@ "\n", "# Define a function to update the image graphics with new data\n", "# add_animations will pass the gridplot to the animation function\n", - "def update_data(gp):\n", - " for sp in gp:\n", + "def update_data(f):\n", + " for subplot in f:\n", " new_data = np.random.rand(512, 512)\n", " # index the image graphic by name and set the data\n", - " sp[\"rand-img\"].data = new_data\n", + " subplot[\"rand-img\"].data = new_data\n", " \n", "# add the animation function\n", - "grid_plot.add_animations(update_data)\n", + "figure_grid.add_animations(update_data)\n", "\n", "# show the gridplot \n", - "grid_plot.show()" + "figure_grid.show()" ] }, { @@ -1378,7 +1373,7 @@ "source": [ "# positional indexing\n", "# row 0 and col 0\n", - "grid_plot[0, 0]" + "figure_grid[0, 0]" ] }, { @@ -1398,7 +1393,7 @@ }, "outputs": [], "source": [ - "grid_plot[0, 1].graphics" + "figure_grid[0, 1].graphics" ] }, { @@ -1418,7 +1413,7 @@ }, "outputs": [], "source": [ - "grid_plot[0, 1].graphics[0].vmax = 0.5" + "figure_grid[0, 1].graphics[0].vmax = 0.5" ] }, { @@ -1439,7 +1434,7 @@ "outputs": [], "source": [ "# you can give subplots human-readable string names\n", - "grid_plot[0, 2].name = \"top-right-plot\"" + "figure_grid[0, 2].name = \"top-right-plot\"" ] }, { @@ -1451,7 +1446,7 @@ }, "outputs": [], "source": [ - "grid_plot[\"top-right-plot\"]" + "figure_grid[\"top-right-plot\"]" ] }, { @@ -1464,7 +1459,7 @@ "outputs": [], "source": [ "# view its position\n", - "grid_plot[\"top-right-plot\"].position" + "figure_grid[\"top-right-plot\"].position" ] }, { @@ -1477,7 +1472,7 @@ "outputs": [], "source": [ "# these are really the same\n", - "grid_plot[\"top-right-plot\"] is grid_plot[0, 2]" + "figure_grid[\"top-right-plot\"] is figure_grid[0, 2]" ] }, { @@ -1497,7 +1492,7 @@ }, "outputs": [], "source": [ - "grid_plot[\"top-right-plot\"][\"rand-img\"].vmin = 0.5" + "figure_grid[\"top-right-plot\"][\"rand-img\"].vmin = 0.5" ] }, { @@ -1505,7 +1500,7 @@ "id": "6a5b4368-ae4d-442c-a11f-45c70267339b", "metadata": {}, "source": [ - "## GridPlot customization" + "## Figure subplot customization" ] }, { @@ -1517,8 +1512,8 @@ }, "outputs": [], "source": [ - "# grid with 2 rows and 3 columns\n", - "grid_shape = (2, 3)\n", + "# 2 rows and 3 columns\n", + "shape = (2, 3)\n", "\n", "# pan-zoom controllers for each view\n", "# views are synced if they have the \n", @@ -1536,15 +1531,15 @@ "]\n", "\n", "# Create the grid plot\n", - "grid_plot = fpl.GridPlot(\n", - " shape=grid_shape,\n", + "figure_grid = fpl.Figure(\n", + " shape=shape,\n", " controller_ids=controller_ids,\n", " names=names,\n", ")\n", "\n", "\n", "# Make a random image graphic for each subplot\n", - "for subplot in grid_plot:\n", + "for subplot in figure_grid:\n", " data = np.random.rand(512, 512)\n", " # create and add an ImageGraphic\n", " subplot.add_image(data=data, name=\"rand-image\")\n", @@ -1558,8 +1553,8 @@ " subplot[\"rand-image\"].data = new_data\n", "\n", "# add the animation\n", - "grid_plot.add_animations(set_random_frame)\n", - "grid_plot.show()" + "figure_grid.add_animations(set_random_frame)\n", + "figure_grid.show()" ] }, { @@ -1580,7 +1575,7 @@ "outputs": [], "source": [ "# can access subplot by name\n", - "grid_plot[\"subplot0\"]" + "figure_grid[\"subplot0\"]" ] }, { @@ -1593,7 +1588,7 @@ "outputs": [], "source": [ "# can access subplot by index\n", - "grid_plot[0, 0]" + "figure_grid[0, 0]" ] }, { @@ -1616,7 +1611,7 @@ "outputs": [], "source": [ "# can access graphic directly via name\n", - "grid_plot[\"subplot0\"][\"rand-image\"]" + "figure_grid[\"subplot0\"][\"rand-image\"]" ] }, { @@ -1628,8 +1623,8 @@ }, "outputs": [], "source": [ - "grid_plot[\"subplot0\"][\"rand-image\"].vmin = 0.6\n", - "grid_plot[\"subplot0\"][\"rand-image\"].vmax = 0.8" + "figure_grid[\"subplot0\"][\"rand-image\"].vmin = 0.6\n", + "figure_grid[\"subplot0\"][\"rand-image\"].vmax = 0.8" ] }, { @@ -1649,8 +1644,8 @@ }, "outputs": [], "source": [ - "grid_plot[1, 0][\"rand-image\"].vim = 0.1\n", - "grid_plot[1, 0][\"rand-image\"].vmax = 0.3" + "figure_grid[1, 0][\"rand-image\"].vim = 0.1\n", + "figure_grid[1, 0][\"rand-image\"].vmax = 0.3" ] } ], diff --git a/examples/desktop/gridplot/gridplot.py b/examples/desktop/gridplot/gridplot.py index 3acf6a8ba..2669dd49b 100644 --- a/examples/desktop/gridplot/gridplot.py +++ b/examples/desktop/gridplot/gridplot.py @@ -10,25 +10,23 @@ import imageio.v3 as iio -plot = fpl.GridPlot(shape=(2, 2)) -# to force a specific framework such as glfw: -# plot = fpl.GridPlot(canvas="glfw") +fig = fpl.Figure(shape=(2, 2)) im = iio.imread("imageio:clock.png") im2 = iio.imread("imageio:astronaut.png") im3 = iio.imread("imageio:coffee.png") im4 = iio.imread("imageio:hubble_deep_field.png") -plot[0, 0].add_image(data=im) -plot[0, 1].add_image(data=im2) -plot[1, 0].add_image(data=im3) -plot[1, 1].add_image(data=im4) +fig[0, 0].add_image(data=im) +fig[0, 1].add_image(data=im2) +fig[1, 0].add_image(data=im3) +fig[1, 1].add_image(data=im4) -plot.show() +fig.show() -plot.canvas.set_logical_size(800, 800) +fig.canvas.set_logical_size(800, 800) -for subplot in plot: +for subplot in fig: subplot.auto_scale() if __name__ == "__main__": diff --git a/examples/desktop/gridplot/gridplot_non_square.py b/examples/desktop/gridplot/gridplot_non_square.py index fe43a3c04..ea93096dc 100644 --- a/examples/desktop/gridplot/gridplot_non_square.py +++ b/examples/desktop/gridplot/gridplot_non_square.py @@ -10,23 +10,21 @@ import imageio.v3 as iio -plot = fpl.GridPlot(shape=(2, 2), controller_ids="sync") -# to force a specific framework such as glfw: -# plot = fpl.GridPlot(canvas="glfw") +fig = fpl.Figure(shape=(2, 2), controller_ids="sync") im = iio.imread("imageio:clock.png") im2 = iio.imread("imageio:astronaut.png") im3 = iio.imread("imageio:coffee.png") -plot[0, 0].add_image(data=im) -plot[0, 1].add_image(data=im2) -plot[1, 0].add_image(data=im3) +fig[0, 0].add_image(data=im) +fig[0, 1].add_image(data=im2) +fig[1, 0].add_image(data=im3) -plot.show() +fig.show() -plot.canvas.set_logical_size(800, 800) +fig.canvas.set_logical_size(800, 800) -for subplot in plot: +for subplot in fig: subplot.auto_scale() if __name__ == "__main__": diff --git a/examples/desktop/heatmap/heatmap.py b/examples/desktop/heatmap/heatmap.py index 45c340cbd..fa5ec6715 100644 --- a/examples/desktop/heatmap/heatmap.py +++ b/examples/desktop/heatmap/heatmap.py @@ -9,9 +9,8 @@ import fastplotlib as fpl import numpy as np -plot = fpl.Plot() -# to force a specific framework such as glfw: -# plot = fpl.Plot(canvas="glfw") + +fig = fpl.Figure() xs = np.linspace(0, 1_000, 10_000) @@ -24,13 +23,13 @@ data[1::2] = cosine # plot the image data -heatmap_graphic = plot.add_heatmap(data=data, name="heatmap") +heatmap_graphic = fig[0, 0].add_heatmap(data=data, name="heatmap") -plot.show() +fig.show() -plot.canvas.set_logical_size(1500, 1500) +fig.canvas.set_logical_size(1500, 1500) -plot.auto_scale() +fig[0, 0].auto_scale() if __name__ == "__main__": print(__doc__) diff --git a/examples/desktop/heatmap/heatmap_cmap.py b/examples/desktop/heatmap/heatmap_cmap.py index afc67f5b8..a1434bb0e 100644 --- a/examples/desktop/heatmap/heatmap_cmap.py +++ b/examples/desktop/heatmap/heatmap_cmap.py @@ -9,9 +9,8 @@ import fastplotlib as fpl import numpy as np -plot = fpl.Plot() -# to force a specific framework such as glfw: -# plot = fpl.Plot(canvas="glfw") + +fig = fpl.Figure() xs = np.linspace(0, 1_000, 10_000) @@ -24,13 +23,13 @@ data[1::2] = cosine # plot the image data -heatmap_graphic = plot.add_heatmap(data=data, name="heatmap") +heatmap_graphic = fig[0, 0].add_heatmap(data=data, name="heatmap") -plot.show() +fig.show() -plot.canvas.set_logical_size(1500, 1500) +fig.canvas.set_logical_size(1500, 1500) -plot.auto_scale() +fig[0, 0].auto_scale() heatmap_graphic.cmap = "viridis" diff --git a/examples/desktop/heatmap/heatmap_data.py b/examples/desktop/heatmap/heatmap_data.py index 78e819ab8..67aee1668 100644 --- a/examples/desktop/heatmap/heatmap_data.py +++ b/examples/desktop/heatmap/heatmap_data.py @@ -9,9 +9,8 @@ import fastplotlib as fpl import numpy as np -plot = fpl.Plot() -# to force a specific framework such as glfw: -# plot = fpl.Plot(canvas="glfw") + +fig = fpl.Figure() xs = np.linspace(0, 1_000, 10_000) @@ -24,13 +23,13 @@ data[1::2] = cosine # plot the image data -heatmap_graphic = plot.add_heatmap(data=data, name="heatmap") +heatmap_graphic = fig[0, 0].add_heatmap(data=data, name="heatmap") -plot.show() +fig.show() -plot.canvas.set_logical_size(1500, 1500) +fig.canvas.set_logical_size(1500, 1500) -plot.auto_scale() +fig[0, 0].auto_scale() heatmap_graphic.data[:5_000] = sine heatmap_graphic.data[5_000:] = cosine diff --git a/examples/desktop/heatmap/heatmap_vmin_vmax.py b/examples/desktop/heatmap/heatmap_vmin_vmax.py index 7aae1d6d3..6fe8a08b8 100644 --- a/examples/desktop/heatmap/heatmap_vmin_vmax.py +++ b/examples/desktop/heatmap/heatmap_vmin_vmax.py @@ -9,9 +9,8 @@ import fastplotlib as fpl import numpy as np -plot = fpl.Plot() -# to force a specific framework such as glfw: -# plot = fpl.Plot(canvas="glfw") + +fig = fpl.Figure() xs = np.linspace(0, 1_000, 10_000) @@ -24,13 +23,13 @@ data[1::2] = cosine # plot the image data -heatmap_graphic = plot.add_heatmap(data=data, name="heatmap") +heatmap_graphic = fig[0, 0].add_heatmap(data=data, name="heatmap") -plot.show() +fig.show() -plot.canvas.set_logical_size(1500, 1500) +fig.canvas.set_logical_size(1500, 1500) -plot.auto_scale() +fig[0, 0].auto_scale() heatmap_graphic.cmap.vmin = -0.5 heatmap_graphic.cmap.vmax = 0.5 diff --git a/examples/desktop/image/image_cmap.py b/examples/desktop/image/image_cmap.py index b7f7b39af..bb8e9f9d8 100644 --- a/examples/desktop/image/image_cmap.py +++ b/examples/desktop/image/image_cmap.py @@ -9,21 +9,18 @@ import fastplotlib as fpl import imageio.v3 as iio - -plot = fpl.Plot() -# to force a specific framework such as glfw: -# plot = fpl.Plot(canvas="glfw") - im = iio.imread("imageio:camera.png") +fig = fpl.Figure() + # plot the image data -image_graphic = plot.add_image(data=im, name="random-image") +image_graphic = fig[0, 0].add_image(data=im, name="random-image") -plot.show() +fig.show() -plot.canvas.set_logical_size(800, 800) +fig.canvas.set_logical_size(800, 800) -plot.auto_scale() +fig[0, 0].auto_scale() image_graphic.cmap = "viridis" diff --git a/examples/desktop/image/image_rgb.py b/examples/desktop/image/image_rgb.py index 2642962fd..ce7e151d0 100644 --- a/examples/desktop/image/image_rgb.py +++ b/examples/desktop/image/image_rgb.py @@ -10,20 +10,18 @@ import imageio.v3 as iio -plot = fpl.Plot() -# to force a specific framework such as glfw: -# plot = fpl.Plot(canvas="glfw") - im = iio.imread("imageio:astronaut.png") +fig = fpl.Figure() + # plot the image data -image_graphic = plot.add_image(data=im, name="iio astronaut") +image_graphic = fig[0, 0].add_image(data=im, name="iio astronaut") -plot.show() +fig.show() -plot.canvas.set_logical_size(800, 800) +fig.canvas.set_logical_size(800, 800) -plot.auto_scale() +fig[0, 0].auto_scale() if __name__ == "__main__": diff --git a/examples/desktop/image/image_rgbvminvmax.py b/examples/desktop/image/image_rgbvminvmax.py index e5c4af531..9725c038a 100644 --- a/examples/desktop/image/image_rgbvminvmax.py +++ b/examples/desktop/image/image_rgbvminvmax.py @@ -10,20 +10,18 @@ import imageio.v3 as iio -plot = fpl.Plot() -# to force a specific framework such as glfw: -# plot = fpl.Plot(canvas="glfw") - im = iio.imread("imageio:astronaut.png") +fig = fpl.Figure() + # plot the image data -image_graphic = plot.add_image(data=im, name="iio astronaut") +image_graphic = fig[0, 0].add_image(data=im, name="iio astronaut") -plot.show() +fig.show() -plot.canvas.set_logical_size(800, 800) +fig.canvas.set_logical_size(800, 800) -plot.auto_scale() +fig[0, 0].auto_scale() image_graphic.cmap.vmin = 0.5 image_graphic.cmap.vmax = 0.75 diff --git a/examples/desktop/image/image_simple.py b/examples/desktop/image/image_simple.py index 2d273ad68..a640974ed 100644 --- a/examples/desktop/image/image_simple.py +++ b/examples/desktop/image/image_simple.py @@ -10,20 +10,18 @@ import imageio.v3 as iio -plot = fpl.Plot() -# to force a specific framework such as glfw: -# plot = fpl.Plot(canvas="glfw") +fig = fpl.Figure() data = iio.imread("imageio:camera.png") # plot the image data -image_graphic = plot.add_image(data=data, name="iio camera") +image_graphic = fig[0, 0].add_image(data=data, name="iio camera") -plot.show() +fig.show() -plot.canvas.set_logical_size(800, 800) +fig.canvas.set_logical_size(800, 800) -plot.auto_scale() +fig[0, 0].auto_scale() if __name__ == "__main__": print(__doc__) diff --git a/examples/desktop/image/image_vminvmax.py b/examples/desktop/image/image_vminvmax.py index e764f6775..3c8607aef 100644 --- a/examples/desktop/image/image_vminvmax.py +++ b/examples/desktop/image/image_vminvmax.py @@ -10,20 +10,18 @@ import imageio.v3 as iio -plot = fpl.Plot() -# to force a specific framework such as glfw: -# plot = fpl.Plot(canvas="glfw") +fig = fpl.Figure() data = iio.imread("imageio:astronaut.png") # plot the image data -image_graphic = plot.add_image(data=data, name="iio astronaut") +image_graphic = fig[0, 0].add_image(data=data, name="iio astronaut") -plot.show() +fig.show() -plot.canvas.set_logical_size(800, 800) +fig.canvas.set_logical_size(800, 800) -plot.auto_scale() +fig[0, 0].auto_scale() image_graphic.cmap.vmin = 0.5 image_graphic.cmap.vmax = 0.75 diff --git a/examples/desktop/image/image_widget.py b/examples/desktop/image/image_widget.py index c50d914d3..80aafe0b1 100644 --- a/examples/desktop/image/image_widget.py +++ b/examples/desktop/image/image_widget.py @@ -5,13 +5,12 @@ When run in a notebook, or with the Qt GUI backend, sliders are also shown. """ -import numpy as np import fastplotlib as fpl import imageio.v3 as iio # not a fastplotlib dependency, only used for examples a = iio.imread("imageio:camera.png") -iw = fpl.widgets.ImageWidget(data=a, cmap="viridis") +iw = fpl.ImageWidget(data=a, cmap="viridis") iw.show() diff --git a/examples/desktop/line/line.py b/examples/desktop/line/line.py index 8cab1954f..56575a810 100644 --- a/examples/desktop/line/line.py +++ b/examples/desktop/line/line.py @@ -10,9 +10,7 @@ import numpy as np -plot = fpl.Plot() -# to force a specific framework such as glfw: -# plot = fpl.Plot(canvas="glfw") +fig = fpl.Figure() xs = np.linspace(-10, 10, 100) # sine wave @@ -28,20 +26,20 @@ ys = np.sinc(xs) * 3 + 8 sinc = np.dstack([xs, ys])[0] -sine_graphic = plot.add_line(data=sine, thickness=5, colors="magenta") +sine_graphic = fig[0, 0].add_line(data=sine, thickness=5, colors="magenta") # you can also use colormaps for lines! -cosine_graphic = plot.add_line(data=cosine, thickness=12, cmap="autumn") +cosine_graphic = fig[0, 0].add_line(data=cosine, thickness=12, cmap="autumn") # or a list of colors for each datapoint colors = ["r"] * 25 + ["purple"] * 25 + ["y"] * 25 + ["b"] * 25 -sinc_graphic = plot.add_line(data=sinc, thickness=5, colors=colors) +sinc_graphic = fig[0, 0].add_line(data=sinc, thickness=5, colors=colors) -plot.show() +fig.show() -plot.canvas.set_logical_size(800, 800) +fig.canvas.set_logical_size(800, 800) -plot.auto_scale() +fig[0, 0].auto_scale() if __name__ == "__main__": diff --git a/examples/desktop/line/line_cmap.py b/examples/desktop/line/line_cmap.py index b196132ed..7d8e1e7d6 100644 --- a/examples/desktop/line/line_cmap.py +++ b/examples/desktop/line/line_cmap.py @@ -10,9 +10,7 @@ import numpy as np -plot = fpl.Plot() -# to force a specific framework such as glfw: -# plot = fpl.Plot(canvas="glfw") +fig = fpl.Figure() xs = np.linspace(-10, 10, 100) # sine wave @@ -24,7 +22,7 @@ cosine = np.dstack([xs, ys])[0] # cmap_values from an array, so the colors on the sine line will be based on the sine y-values -sine_graphic = plot.add_line( +sine_graphic = fig[0, 0].add_line( data=sine, thickness=10, cmap="plasma", @@ -33,16 +31,16 @@ # qualitative colormaps, useful for cluster labels or other types of categorical labels cmap_values = [0] * 25 + [5] * 10 + [1] * 35 + [2] * 30 -cosine_graphic = plot.add_line( +cosine_graphic = fig[0, 0].add_line( data=cosine, thickness=10, cmap="tab10", cmap_values=cmap_values ) -plot.show() +fig.show() -plot.canvas.set_logical_size(800, 800) +fig.canvas.set_logical_size(800, 800) if __name__ == "__main__": print(__doc__) diff --git a/examples/desktop/line/line_colorslice.py b/examples/desktop/line/line_colorslice.py index f2aca8125..4df666531 100644 --- a/examples/desktop/line/line_colorslice.py +++ b/examples/desktop/line/line_colorslice.py @@ -10,9 +10,7 @@ import numpy as np -plot = fpl.Plot() -# to force a specific framework such as glfw: -# plot = fpl.Plot(canvas="glfw") +fig = fpl.Figure() xs = np.linspace(-10, 10, 100) # sine wave @@ -28,16 +26,16 @@ ys = np.sinc(xs) * 3 + 8 sinc = np.dstack([xs, ys])[0] -sine_graphic = plot.add_line(data=sine, thickness=5, colors="magenta") +sine_graphic = fig[0, 0].add_line(data=sine, thickness=5, colors="magenta") # you can also use colormaps for lines! -cosine_graphic = plot.add_line(data=cosine, thickness=12, cmap="autumn") +cosine_graphic = fig[0, 0].add_line(data=cosine, thickness=12, cmap="autumn") # or a list of colors for each datapoint colors = ["r"] * 25 + ["purple"] * 25 + ["y"] * 25 + ["b"] * 25 -sinc_graphic = plot.add_line(data=sinc, thickness=5, colors=colors) +sinc_graphic = fig[0, 0].add_line(data=sinc, thickness=5, colors=colors) -plot.show() +fig.show() # indexing of colors cosine_graphic.colors[:15] = "magenta" @@ -58,9 +56,9 @@ key2 = np.array([True, False, True, False, True, True, True, True]) cosine_graphic.colors[key2] = "Green" -plot.canvas.set_logical_size(800, 800) +fig.canvas.set_logical_size(800, 800) -plot.auto_scale() +fig[0, 0].auto_scale() if __name__ == "__main__": diff --git a/examples/desktop/line/line_dataslice.py b/examples/desktop/line/line_dataslice.py index ea87ba552..12a5f0f04 100644 --- a/examples/desktop/line/line_dataslice.py +++ b/examples/desktop/line/line_dataslice.py @@ -10,9 +10,7 @@ import numpy as np -plot = fpl.Plot() -# to force a specific framework such as glfw: -# plot = fpl.Plot(canvas="glfw") +fig = fpl.Figure() xs = np.linspace(-10, 10, 100) # sine wave @@ -28,16 +26,16 @@ ys = np.sinc(xs) * 3 + 8 sinc = np.dstack([xs, ys])[0] -sine_graphic = plot.add_line(data=sine, thickness=5, colors="magenta") +sine_graphic = fig[0, 0].add_line(data=sine, thickness=5, colors="magenta") # you can also use colormaps for lines! -cosine_graphic = plot.add_line(data=cosine, thickness=12, cmap="autumn") +cosine_graphic = fig[0, 0].add_line(data=cosine, thickness=12, cmap="autumn") # or a list of colors for each datapoint colors = ["r"] * 25 + ["purple"] * 25 + ["y"] * 25 + ["b"] * 25 -sinc_graphic = plot.add_line(data=sinc, thickness=5, colors=colors) +sinc_graphic = fig[0, 0].add_line(data=sinc, thickness=5, colors=colors) -plot.show() +fig.show() cosine_graphic.data[10:50:5, :2] = sine[10:50:5] cosine_graphic.data[90:, 1] = 7 @@ -47,9 +45,9 @@ key2 = np.array([True, False, True, False, True, True, True, True]) sinc_graphic.data[key2] = np.array([[5, 1, 2]]) -plot.canvas.set_logical_size(800, 800) +fig.canvas.set_logical_size(800, 800) -plot.auto_scale() +fig[0, 0].auto_scale() if __name__ == "__main__": diff --git a/examples/desktop/line/line_present_scaling.py b/examples/desktop/line/line_present_scaling.py index 327186c16..d334e6fbd 100644 --- a/examples/desktop/line/line_present_scaling.py +++ b/examples/desktop/line/line_present_scaling.py @@ -10,9 +10,7 @@ import numpy as np -plot = fpl.Plot() -# to force a specific framework such as glfw: -# plot = fpl.Plot(canvas="glfw") +fig = fpl.Figure() xs = np.linspace(-10, 10, 100) # sine wave @@ -28,22 +26,22 @@ ys = np.sinc(xs) * 3 + 8 sinc = np.dstack([xs, ys])[0] -sine_graphic = plot.add_line(data=sine, thickness=5, colors="magenta") +sine_graphic = fig[0, 0].add_line(data=sine, thickness=5, colors="magenta") # you can also use colormaps for lines! -cosine_graphic = plot.add_line(data=cosine, thickness=12, cmap="autumn") +cosine_graphic = fig[0, 0].add_line(data=cosine, thickness=12, cmap="autumn") # or a list of colors for each datapoint colors = ["r"] * 25 + ["purple"] * 25 + ["y"] * 25 + ["b"] * 25 -sinc_graphic = plot.add_line(data=sinc, thickness=5, colors=colors) +sinc_graphic = fig[0, 0].add_line(data=sinc, thickness=5, colors=colors) -plot.show() +fig.show() sinc_graphic.present = False -plot.canvas.set_logical_size(800, 800) +fig.canvas.set_logical_size(800, 800) -plot.auto_scale() +fig[0, 0].auto_scale() if __name__ == "__main__": diff --git a/examples/desktop/line_collection/line_collection.py b/examples/desktop/line_collection/line_collection.py index 071da2e2e..dd6f3ca33 100644 --- a/examples/desktop/line_collection/line_collection.py +++ b/examples/desktop/line_collection/line_collection.py @@ -27,15 +27,13 @@ def make_circle(center, radius: float, n_points: int = 75) -> np.ndarray: pos_xy = np.vstack(circles) -plot = fpl.Plot() -# to force a specific framework such as glfw: -# plot = fpl.Plot(canvas="glfw") +fig = fpl.Figure() -plot.add_line_collection(circles, cmap="jet", thickness=5) +fig[0, 0].add_line_collection(circles, cmap="jet", thickness=5) -plot.show() +fig.show() -plot.canvas.set_logical_size(800, 800) +fig.canvas.set_logical_size(800, 800) if __name__ == "__main__": print(__doc__) diff --git a/examples/desktop/line_collection/line_collection_cmap_values.py b/examples/desktop/line_collection/line_collection_cmap_values.py index 3623c20c3..9eeef40f8 100644 --- a/examples/desktop/line_collection/line_collection_cmap_values.py +++ b/examples/desktop/line_collection/line_collection_cmap_values.py @@ -33,20 +33,18 @@ def make_circle(center, radius: float, n_points: int = 75) -> np.ndarray: # highest values, lowest values, mid-high values, mid values cmap_values = [10] * 4 + [0] * 4 + [7] * 4 + [5] * 4 -plot = fpl.Plot() -# to force a specific framework such as glfw: -# plot = fpl.Plot(canvas="glfw") +fig = fpl.Figure() -plot.add_line_collection( +fig[0, 0].add_line_collection( circles, cmap="bwr", cmap_values=cmap_values, thickness=10 ) -plot.show() +fig.show() -plot.canvas.set_logical_size(800, 800) +fig.canvas.set_logical_size(800, 800) if __name__ == "__main__": print(__doc__) diff --git a/examples/desktop/line_collection/line_collection_cmap_values_qualitative.py b/examples/desktop/line_collection/line_collection_cmap_values_qualitative.py index f56d2ca02..85f0724d8 100644 --- a/examples/desktop/line_collection/line_collection_cmap_values_qualitative.py +++ b/examples/desktop/line_collection/line_collection_cmap_values_qualitative.py @@ -39,20 +39,18 @@ def make_circle(center, radius: float, n_points: int = 75) -> np.ndarray: 1, 1, 1, 5 ] -plot = fpl.Plot() -# to force a specific framework such as glfw: -# plot = fpl.Plot(canvas="glfw") +fig = fpl.Figure() -plot.add_line_collection( +fig[0, 0].add_line_collection( circles, cmap="tab10", cmap_values=cmap_values, thickness=10 ) -plot.show() +fig.show() -plot.canvas.set_logical_size(800, 800) +fig.canvas.set_logical_size(800, 800) if __name__ == "__main__": print(__doc__) diff --git a/examples/desktop/line_collection/line_collection_colors.py b/examples/desktop/line_collection/line_collection_colors.py index d74f65d82..d53afcd5b 100644 --- a/examples/desktop/line_collection/line_collection_colors.py +++ b/examples/desktop/line_collection/line_collection_colors.py @@ -31,15 +31,13 @@ def make_circle(center, radius: float, n_points: int = 75) -> np.ndarray: # this will produce 16 circles so we will define 16 colors colors = ["blue"] * 4 + ["red"] * 4 + ["yellow"] * 4 + ["w"] * 4 -plot = fpl.Plot() -# to force a specific framework such as glfw: -# plot = fpl.Plot(canvas="glfw") +fig = fpl.Figure() -plot.add_line_collection(circles, colors=colors, thickness=10) +fig[0, 0].add_line_collection(circles, colors=colors, thickness=10) -plot.show() +fig.show() -plot.canvas.set_logical_size(800, 800) +fig.canvas.set_logical_size(800, 800) if __name__ == "__main__": print(__doc__) diff --git a/examples/desktop/line_collection/line_stack.py b/examples/desktop/line_collection/line_stack.py index 5a94caee7..cf5d933e3 100644 --- a/examples/desktop/line_collection/line_stack.py +++ b/examples/desktop/line_collection/line_stack.py @@ -17,16 +17,14 @@ # make 25 lines data = np.vstack([ys] * 25) -plot = fpl.Plot() -# to force a specific framework such as glfw: -# plot = fpl.Plot(canvas="glfw") +fig = fpl.Figure() # line stack takes all the same arguments as line collection and behaves similarly -plot.add_line_stack(data, cmap="jet") +fig[0, 0].add_line_stack(data, cmap="jet") -plot.show(maintain_aspect=False) +fig.show(maintain_aspect=False) -plot.canvas.set_logical_size(900, 600) +fig.canvas.set_logical_size(900, 600) if __name__ == "__main__": print(__doc__) diff --git a/examples/desktop/scatter/scatter.py b/examples/desktop/scatter/scatter.py index 778f37deb..c47306722 100644 --- a/examples/desktop/scatter/scatter.py +++ b/examples/desktop/scatter/scatter.py @@ -10,9 +10,7 @@ import numpy as np from pathlib import Path -plot = fpl.Plot() -# to force a specific framework such as glfw: -# plot = fpl.Plot(canvas="glfw") +fig = fpl.Figure() data_path = Path(__file__).parent.parent.joinpath("data", "iris.npy") data = np.load(data_path) @@ -20,13 +18,13 @@ n_points = 50 colors = ["yellow"] * n_points + ["cyan"] * n_points + ["magenta"] * n_points -scatter_graphic = plot.add_scatter(data=data[:, :-1], sizes=6, alpha=0.7, colors=colors) +scatter_graphic = fig[0, 0].add_scatter(data=data[:, :-1], sizes=6, alpha=0.7, colors=colors) -plot.show() +fig.show() -plot.canvas.set_logical_size(800, 800) +fig.canvas.set_logical_size(800, 800) -plot.auto_scale() +fig[0, 0].auto_scale() if __name__ == "__main__": diff --git a/examples/desktop/scatter/scatter_cmap.py b/examples/desktop/scatter/scatter_cmap.py index 3e986d5d5..f1bba98c3 100644 --- a/examples/desktop/scatter/scatter_cmap.py +++ b/examples/desktop/scatter/scatter_cmap.py @@ -12,28 +12,23 @@ from sklearn.cluster import AgglomerativeClustering -plot = fpl.Plot() -# to force a specific framework such as glfw: -# plot = fpl.Plot(canvas="glfw") +fig = fpl.Figure() data_path = Path(__file__).parent.parent.joinpath("data", "iris.npy") data = np.load(data_path) - agg = AgglomerativeClustering(n_clusters=3) - agg.fit_predict(data) - -scatter_graphic = plot.add_scatter( +scatter_graphic = fig[0, 0].add_scatter( data=data[:, :-1], sizes=15, alpha=0.7, cmap="Set1", cmap_values=agg.labels_ ) -plot.show() +fig.show() -plot.canvas.set_logical_size(800, 800) +fig.canvas.set_logical_size(800, 800) -plot.auto_scale() +fig[0, 0].auto_scale() scatter_graphic.cmap = "tab10" diff --git a/examples/desktop/scatter/scatter_colorslice.py b/examples/desktop/scatter/scatter_colorslice.py index d752cacbd..43f405b06 100644 --- a/examples/desktop/scatter/scatter_colorslice.py +++ b/examples/desktop/scatter/scatter_colorslice.py @@ -11,9 +11,7 @@ from pathlib import Path -plot = fpl.Plot() -# to force a specific framework such as glfw: -# plot = fpl.Plot(canvas="glfw") +fig = fpl.Figure() data_path = Path(__file__).parent.parent.joinpath("data", "iris.npy") data = np.load(data_path) @@ -21,13 +19,13 @@ n_points = 50 colors = ["yellow"] * n_points + ["cyan"] * n_points + ["magenta"] * n_points -scatter_graphic = plot.add_scatter(data=data[:, :-1], sizes=6, alpha=0.7, colors=colors) +scatter_graphic = fig[0, 0].add_scatter(data=data[:, :-1], sizes=6, alpha=0.7, colors=colors) -plot.show() +fig.show() -plot.canvas.set_logical_size(800, 800) +fig.canvas.set_logical_size(800, 800) -plot.auto_scale() +fig[0, 0].auto_scale() scatter_graphic.colors[0:75] = "red" scatter_graphic.colors[75:150] = "white" diff --git a/examples/desktop/scatter/scatter_dataslice.py b/examples/desktop/scatter/scatter_dataslice.py index 3008aab61..989b7c21c 100644 --- a/examples/desktop/scatter/scatter_dataslice.py +++ b/examples/desktop/scatter/scatter_dataslice.py @@ -11,9 +11,7 @@ from pathlib import Path -plot = fpl.Plot() -# to force a specific framework such as glfw: -# plot = fpl.Plot(canvas="glfw") +fig = fpl.Figure() data_path = Path(__file__).parent.parent.joinpath("data", "iris.npy") data = np.load(data_path) @@ -21,13 +19,13 @@ n_points = 50 colors = ["yellow"] * n_points + ["cyan"] * n_points + ["magenta"] * n_points -scatter_graphic = plot.add_scatter(data=data[:, :-1], sizes=6, alpha=0.7, colors=colors) +scatter_graphic = fig[0, 0].add_scatter(data=data[:, :-1], sizes=6, alpha=0.7, colors=colors) -plot.show() +fig.show() -plot.canvas.set_logical_size(800, 800) +fig.canvas.set_logical_size(800, 800) -plot.auto_scale() +fig[0, 0].auto_scale() scatter_graphic.data[0] = np.array([[5, 3, 1.5]]) scatter_graphic.data[1] = np.array([[4.3, 3.2, 1.3]]) diff --git a/examples/desktop/scatter/scatter_present.py b/examples/desktop/scatter/scatter_present.py index ad4be837f..5da4610bd 100644 --- a/examples/desktop/scatter/scatter_present.py +++ b/examples/desktop/scatter/scatter_present.py @@ -11,7 +11,7 @@ from pathlib import Path -plot = fpl.Plot() +fig = fpl.Figure() data_path = Path(__file__).parent.parent.joinpath("data", "iris.npy") data = np.load(data_path) @@ -19,16 +19,16 @@ n_points = 50 colors = ["yellow"] * n_points + ["cyan"] * n_points + ["magenta"] * n_points -scatter_graphic = plot.add_scatter(data=data[:, :-1], sizes=6, alpha=0.7, colors=colors) +scatter_graphic = fig[0, 0].add_scatter(data=data[:, :-1], sizes=6, alpha=0.7, colors=colors) colors = ["red"] * n_points + ["white"] * n_points + ["blue"] * n_points -scatter_graphic2 = plot.add_scatter(data=data[:, 1:], sizes=6, alpha=0.7, colors=colors) +scatter_graphic2 = fig[0, 0].add_scatter(data=data[:, 1:], sizes=6, alpha=0.7, colors=colors) -plot.show() +fig.show() -plot.canvas.set_logical_size(800, 800) +fig.canvas.set_logical_size(800, 800) -plot.auto_scale() +fig[0, 0].auto_scale() scatter_graphic.present = False diff --git a/examples/desktop/scatter/scatter_size.py b/examples/desktop/scatter/scatter_size.py index 5c1f97703..41a97ad53 100644 --- a/examples/desktop/scatter/scatter_size.py +++ b/examples/desktop/scatter/scatter_size.py @@ -8,14 +8,14 @@ import numpy as np import fastplotlib as fpl -# grid with 2 rows and 3 columns -grid_shape = (2, 1) +# figure with 2 rows and 3 columns +shape = (2, 1) # you can give string names for each subplot within the gridplot names = [["scalar_size"], ["array_size"]] # Create the grid plot -plot = fpl.GridPlot(shape=grid_shape, names=names, size=(1000, 1000)) +fig = fpl.Figure(shape=shape, names=names, size=(1000, 1000)) # get y_values using sin function angles = np.arange(0, 20 * np.pi + 0.001, np.pi / 20) @@ -24,17 +24,17 @@ data = np.column_stack([x_values, y_values]) -plot["scalar_size"].add_scatter( +fig["scalar_size"].add_scatter( data=data, sizes=5, colors="blue" ) # add a set of scalar sizes non_scalar_sizes = np.abs((y_values / np.pi)) # ensure minimum size of 5 -plot["array_size"].add_scatter(data=data, sizes=non_scalar_sizes, colors="red") +fig["array_size"].add_scatter(data=data, sizes=non_scalar_sizes, colors="red") -for graph in plot: +for graph in fig: graph.auto_scale(maintain_aspect=True) -plot.show() +fig.show() if __name__ == "__main__": print(__doc__) diff --git a/examples/notebooks/heatmap.ipynb b/examples/notebooks/heatmap.ipynb index 82583b1df..90c07a3cb 100644 --- a/examples/notebooks/heatmap.ipynb +++ b/examples/notebooks/heatmap.ipynb @@ -70,11 +70,11 @@ }, "outputs": [], "source": [ - "plot = fpl.Plot()\n", + "fig = fpl.Figure()\n", "\n", - "plot.add_heatmap(data, cmap=\"viridis\")\n", + "fig[0, 0].add_heatmap(data, cmap=\"viridis\")\n", "\n", - "plot.show(maintain_aspect=False)" + "fig.show(maintain_aspect=False)" ] }, { diff --git a/examples/notebooks/image_widget.ipynb b/examples/notebooks/image_widget.ipynb index a7527601a..5136ba028 100644 --- a/examples/notebooks/image_widget.ipynb +++ b/examples/notebooks/image_widget.ipynb @@ -78,7 +78,7 @@ }, "outputs": [], "source": [ - "iw.gridplot[0, 0][\"image_widget_managed\"].cmap = \"gnuplot2\"" + "iw.figure[0, 0][\"image_widget_managed\"].cmap = \"gnuplot2\"" ] }, { @@ -231,7 +231,7 @@ "outputs": [], "source": [ "iw_movie.set_data(new_data=new_data)\n", - "iw_movie.gridplot[0, 0].auto_scale()# sidecar is optional" + "iw_movie.figure[0, 0].auto_scale()# sidecar is optional" ] }, { @@ -340,7 +340,7 @@ }, "outputs": [], "source": [ - "iw_zfish.gridplot[\"plane-2\"]" + "iw_zfish.figure[\"plane-2\"]" ] }, { diff --git a/examples/notebooks/image_widget_test.ipynb b/examples/notebooks/image_widget_test.ipynb index 39cf0b887..321f7b84f 100644 --- a/examples/notebooks/image_widget_test.ipynb +++ b/examples/notebooks/image_widget_test.ipynb @@ -60,7 +60,7 @@ "iw = ImageWidget(\n", " data=a,\n", " cmap=\"viridis\",\n", - " grid_plot_kwargs={\"size\": (900, 600)},\n", + " figure_kwargs={\"size\": (900, 600)},\n", ")" ] }, @@ -81,9 +81,9 @@ "metadata": {}, "outputs": [], "source": [ - "plot_test(\"image-widget-single\", iw.gridplot)\n", - "iw.gridplot[0, 0][\"image_widget_managed\"].cmap = \"gnuplot2\"\n", - "plot_test(\"image-widget-single-gnuplot2\", iw.gridplot)" + "plot_test(\"image-widget-single\", iw.figure)\n", + "iw.figure[0, 0][\"image_widget_managed\"].cmap = \"gnuplot2\"\n", + "plot_test(\"image-widget-single-gnuplot2\", iw.figure)" ] }, { @@ -131,7 +131,7 @@ "iw_movie = ImageWidget(\n", " data=gray_movie, \n", " cmap=\"gray\",\n", - " grid_plot_kwargs={\"size\": (900, 600)},\n", + " figure_kwargs={\"size\": (900, 600)},\n", ")" ] }, @@ -157,23 +157,23 @@ "# testing cell ignore\n", "assert iw_movie.sliders[\"t\"].max == gray_movie.shape[0] - 1\n", "assert iw_movie.sliders[\"t\"].min == 0\n", - "plot_test(\"image-widget-movie-single-0\", iw_movie.gridplot)\n", + "plot_test(\"image-widget-movie-single-0\", iw_movie.figure)\n", "iw_movie.sliders[\"t\"].value = 50\n", - "plot_test(\"image-widget-movie-single-50\", iw_movie.gridplot)\n", + "plot_test(\"image-widget-movie-single-50\", iw_movie.figure)\n", "iw_movie.sliders[\"t\"].value = 279\n", - "plot_test(\"image-widget-movie-single-279\", iw_movie.gridplot)\n", + "plot_test(\"image-widget-movie-single-279\", iw_movie.figure)\n", "iw_movie.sliders[\"t\"].value = 0\n", - "plot_test(\"image-widget-movie-single-0-reset\", iw_movie.gridplot)\n", + "plot_test(\"image-widget-movie-single-0-reset\", iw_movie.figure)\n", "iw_movie.sliders[\"t\"].value = 50\n", "iw_movie.window_funcs = {\"t\": (np.mean, 13)}\n", "# testing cell ignore\n", - "plot_test(\"image-widget-movie-single-50-window-mean-13\", iw_movie.gridplot)\n", + "plot_test(\"image-widget-movie-single-50-window-mean-13\", iw_movie.figure)\n", "iw_movie.window_funcs[\"t\"].window_size = 33\n", - "plot_test(\"image-widget-movie-single-50-window-mean-33\", iw_movie.gridplot)\n", + "plot_test(\"image-widget-movie-single-50-window-mean-33\", iw_movie.figure)\n", "iw_movie.window_funcs[\"t\"].func = np.max\n", - "plot_test(\"image-widget-movie-single-50-window-max-33\", iw_movie.gridplot)\n", + "plot_test(\"image-widget-movie-single-50-window-max-33\", iw_movie.figure)\n", "iw_movie.window_funcs = None\n", - "plot_test(\"image-widget-movie-single-50-window-reset\", iw_movie.gridplot)\n", + "plot_test(\"image-widget-movie-single-50-window-reset\", iw_movie.figure)\n", "iw_movie.sliders[\"t\"].value = 0" ] }, @@ -205,7 +205,7 @@ "outputs": [], "source": [ "iw_movie.set_data(new_data=new_data)\n", - "iw_movie.gridplot[0, 0].auto_scale()" + "iw_movie.figure[0, 0].auto_scale()" ] }, { @@ -215,7 +215,7 @@ "metadata": {}, "outputs": [], "source": [ - "plot_test(\"image-widget-movie-set_data\", iw_movie.gridplot)" + "plot_test(\"image-widget-movie-set_data\", iw_movie.figure)" ] }, { @@ -283,7 +283,7 @@ " window_funcs={\"t\": (np.mean, 5)},\n", " names=[f\"plane-{i}\" for i in range(n_planes)],\n", " cmap=\"gnuplot2\", \n", - " grid_plot_kwargs={\"size\": (900, 600)},\n", + " figure_kwargs={\"size\": (900, 600)},\n", ")" ] }, @@ -309,25 +309,25 @@ "# testing cell ignore\n", "assert iw_zfish.sliders[\"t\"].max == zfish_data.shape[0] - 1\n", "assert iw_zfish.sliders[\"t\"].min == 0\n", - "plot_test(\"image-widget-zfish-grid-init-mean-window-5\", iw_zfish.gridplot)\n", + "plot_test(\"image-widget-zfish-grid-init-mean-window-5\", iw_zfish.figure)\n", "iw_zfish.sliders[\"t\"].value = 50\n", - "plot_test(\"image-widget-zfish-grid-frame-50-mean-window-5\", iw_zfish.gridplot)\n", + "plot_test(\"image-widget-zfish-grid-frame-50-mean-window-5\", iw_zfish.figure)\n", "iw_zfish.window_funcs[\"t\"].window_size = 13\n", - "plot_test(\"image-widget-zfish-grid-frame-50-mean-window-13\", iw_zfish.gridplot)\n", + "plot_test(\"image-widget-zfish-grid-frame-50-mean-window-13\", iw_zfish.figure)\n", "iw_zfish.window_funcs = None\n", - "plot_test(\"image-widget-zfish-grid-frame-50\", iw_zfish.gridplot)\n", + "plot_test(\"image-widget-zfish-grid-frame-50\", iw_zfish.figure)\n", "iw_zfish.sliders[\"t\"].value = 99\n", - "plot_test(\"image-widget-zfish-grid-frame-99\", iw_zfish.gridplot)\n", + "plot_test(\"image-widget-zfish-grid-frame-99\", iw_zfish.figure)\n", "iw_zfish.sliders[\"t\"].value = 50\n", "iw_zfish.window_funcs = {\"t\": (np.max, 13)}\n", - "plot_test(\"image-widget-zfish-grid-frame-50-max-window-13\", iw_zfish.gridplot)\n", + "plot_test(\"image-widget-zfish-grid-frame-50-max-window-13\", iw_zfish.figure)\n", "iw_zfish.window_funcs = None\n", "iw_zfish.frame_apply = lambda frame: gaussian_filter(frame.astype(np.float32), sigma=3)\n", "iw_zfish.reset_vmin_vmax()\n", - "plot_test(\"image-widget-zfish-grid-frame-50-frame-apply-gaussian\", iw_zfish.gridplot)\n", + "plot_test(\"image-widget-zfish-grid-frame-50-frame-apply-gaussian\", iw_zfish.figure)\n", "iw_zfish.frame_apply = None\n", "iw_zfish.reset_vmin_vmax()\n", - "plot_test(\"image-widget-zfish-grid-frame-50-frame-apply-reset\", iw_zfish.gridplot)" + "plot_test(\"image-widget-zfish-grid-frame-50-frame-apply-reset\", iw_zfish.figure)" ] }, { @@ -343,13 +343,13 @@ " reset_indices=False\n", ")\n", "\n", - "plot_test(\"image-widget-zfish-grid-set_data-reset-indices-false\", iw_zfish.gridplot)\n", + "plot_test(\"image-widget-zfish-grid-set_data-reset-indices-false\", iw_zfish.figure)\n", "\n", "iw_zfish.set_data(\n", " [zfish_data[:, i] for i in range(n_planes - 1, -1, -1)],\n", " reset_indices=True\n", ")\n", - "plot_test(\"image-widget-zfish-grid-set_data-reset-indices-true\", iw_zfish.gridplot)" + "plot_test(\"image-widget-zfish-grid-set_data-reset-indices-true\", iw_zfish.figure)" ] }, { @@ -383,7 +383,7 @@ " data=zfish_data, # you can also provide a list of tzxy arrays\n", " window_funcs={\"t\": (np.mean, 5)},\n", " cmap=\"gnuplot2\", \n", - " grid_plot_kwargs={\"size\": (900, 600)},\n", + " figure_kwargs={\"size\": (900, 600)},\n", ")" ] }, @@ -406,28 +406,28 @@ "metadata": {}, "outputs": [], "source": [ - "# same tests as with the gridplot\n", + "# same tests as with the figure\n", "assert iw_z.sliders[\"t\"].max == zfish_data.shape[0] - 1\n", "assert iw_z.sliders[\"t\"].min == 0\n", - "plot_test(\"image-widget-zfish-init-mean-window-5\", iw_z.gridplot)\n", + "plot_test(\"image-widget-zfish-init-mean-window-5\", iw_z.figure)\n", "iw_z.sliders[\"t\"].value = 50\n", - "plot_test(\"image-widget-zfish-frame-50-mean-window-5\", iw_z.gridplot)\n", + "plot_test(\"image-widget-zfish-frame-50-mean-window-5\", iw_z.figure)\n", "iw_z.window_funcs[\"t\"].window_size = 13\n", - "plot_test(\"image-widget-zfish-frame-50-mean-window-13\", iw_z.gridplot)\n", + "plot_test(\"image-widget-zfish-frame-50-mean-window-13\", iw_z.figure)\n", "iw_z.window_funcs = None\n", - "plot_test(\"image-widget-zfish-frame-50\", iw_z.gridplot)\n", + "plot_test(\"image-widget-zfish-frame-50\", iw_z.figure)\n", "iw_z.sliders[\"t\"].value = 99\n", - "plot_test(\"image-widget-zfish-frame-99\", iw_z.gridplot)\n", + "plot_test(\"image-widget-zfish-frame-99\", iw_z.figure)\n", "iw_z.sliders[\"t\"].value = 50\n", "iw_z.window_funcs = {\"t\": (np.max, 13)}\n", - "plot_test(\"image-widget-zfish-frame-50-max-window-13\", iw_z.gridplot)\n", + "plot_test(\"image-widget-zfish-frame-50-max-window-13\", iw_z.figure)\n", "iw_z.window_funcs = None\n", "iw_z.frame_apply = lambda frame: gaussian_filter(frame.astype(np.float32), sigma=3)\n", "iw_z.reset_vmin_vmax()\n", - "plot_test(\"image-widget-zfish-frame-50-frame-apply-gaussian\", iw_z.gridplot)\n", + "plot_test(\"image-widget-zfish-frame-50-frame-apply-gaussian\", iw_z.figure)\n", "iw_z.frame_apply = None\n", "iw_z.reset_vmin_vmax()\n", - "plot_test(\"image-widget-zfish-frame-50-frame-apply-reset\", iw_z.gridplot)" + "plot_test(\"image-widget-zfish-frame-50-frame-apply-reset\", iw_z.figure)" ] }, { @@ -465,7 +465,7 @@ " rgb=[False, True],\n", " histogram_widget=True,\n", " cmap=\"gnuplot2\", \n", - " grid_plot_kwargs = {\"controller_ids\": None},\n", + " figure_kwargs = {\"controller_ids\": None},\n", ")\n", "\n", "iw_mixed_shapes.show()" @@ -479,16 +479,16 @@ "outputs": [], "source": [ "iw_mixed_shapes.sliders[\"t\"].value = 50\n", - "plot_test(\"image-widget-zfish-mixed-rgb-cockatoo-frame-50\", iw_mixed_shapes.gridplot)\n", + "plot_test(\"image-widget-zfish-mixed-rgb-cockatoo-frame-50\", iw_mixed_shapes.figure)\n", "\n", "#Set the data, changing the first array and also the size of the \"T\" slider\n", "iw_mixed_shapes.set_data([zfish_frame_2, movie[:200, :, :, :]], reset_indices=True)\n", - "plot_test(\"image-widget-zfish-mixed-rgb-cockatoo-set-data\", iw_mixed_shapes.gridplot)\n", + "plot_test(\"image-widget-zfish-mixed-rgb-cockatoo-set-data\", iw_mixed_shapes.figure)\n", "\n", "#Check how a window function might work on the RGB data\n", "iw_mixed_shapes.window_funcs = {\"t\": (np.mean, 4)}\n", "iw_mixed_shapes.sliders[\"t\"].value = 20\n", - "plot_test(\"image-widget-zfish-mixed-rgb-cockatoo-windowrgb\", iw_mixed_shapes.gridplot)" + "plot_test(\"image-widget-zfish-mixed-rgb-cockatoo-windowrgb\", iw_mixed_shapes.figure)" ] }, { diff --git a/examples/notebooks/linear_region_selector.ipynb b/examples/notebooks/linear_region_selector.ipynb index 43cea4f81..2ba40ed54 100644 --- a/examples/notebooks/linear_region_selector.ipynb +++ b/examples/notebooks/linear_region_selector.ipynb @@ -19,7 +19,7 @@ "import numpy as np\n", "from ipywidgets import IntRangeSlider, FloatRangeSlider, VBox\n", "\n", - "gp = fpl.GridPlot((2, 2))\n", + "fig = fpl.Figure((2, 2))\n", "\n", "# preallocated size for zoomed data\n", "zoomed_prealloc = 1_000\n", @@ -29,14 +29,14 @@ "sine = np.sin(xs) * 20\n", "\n", "# make sine along x axis\n", - "sine_graphic_x = gp[0, 0].add_line(sine)\n", + "sine_graphic_x = fig[0, 0].add_line(sine)\n", "\n", "# just something that looks different for line along y-axis\n", "sine_y = sine\n", "sine_y[sine_y > 0] = 0\n", "\n", "# sine along y axis\n", - "sine_graphic_y = gp[0, 1].add_line(np.column_stack([sine_y, xs]))\n", + "sine_graphic_y = fig[0, 1].add_line(np.column_stack([sine_y, xs]))\n", "\n", "# offset the position of the graphic to demonstrate `get_selected_data()` later\n", "sine_graphic_y.position_x = 50\n", @@ -50,8 +50,8 @@ "zoomed_init = np.column_stack([np.arange(zoomed_prealloc), np.random.rand(zoomed_prealloc)])\n", "\n", "# make line graphics for displaying zoomed data\n", - "zoomed_x = gp[1, 0].add_line(zoomed_init)\n", - "zoomed_y = gp[1, 1].add_line(zoomed_init)\n", + "zoomed_x = fig[1, 0].add_line(zoomed_init)\n", + "zoomed_y = fig[1, 1].add_line(zoomed_init)\n", "\n", "\n", "def interpolate(subdata: np.ndarray, axis: int):\n", @@ -67,21 +67,21 @@ " \"\"\"sets zoomed x selector data\"\"\"\n", " selected_data = ev.pick_info[\"selected_data\"]\n", " zoomed_x.data = interpolate(selected_data, axis=1) # use the y-values\n", - " gp[1, 0].auto_scale()\n", + " fig[1, 0].auto_scale()\n", "\n", "\n", "def set_zoom_y(ev):\n", " \"\"\"sets zoomed y selector data\"\"\"\n", " selected_data = ev.pick_info[\"selected_data\"]\n", " zoomed_y.data = -interpolate(selected_data, axis=0) # use the x-values\n", - " gp[1, 1].auto_scale()\n", + " fig[1, 1].auto_scale()\n", "\n", "\n", "# update zoomed plots when bounds change\n", "ls_x.selection.add_event_handler(set_zoom_x)\n", "ls_y.selection.add_event_handler(set_zoom_y)\n", "\n", - "gp.show()" + "fig.show()" ] }, { @@ -147,28 +147,25 @@ "metadata": {}, "outputs": [], "source": [ - "import fastplotlib as fpl\n", - "import numpy as np\n", - "\n", "# data to plot\n", "xs = np.linspace(0, 100, 1_000)\n", "sine = np.sin(xs) * 20\n", "cosine = np.cos(xs) * 20\n", "\n", - "plot = fpl.GridPlot((5, 1))\n", + "fig_stack = fpl.Figure((5, 1))\n", "\n", "# sines and cosines\n", "sines = [sine] * 2\n", "cosines = [cosine] * 2\n", "\n", "# make line stack\n", - "line_stack = plot[0, 0].add_line_stack(sines + cosines, separation=50)\n", + "line_stack = fig_stack[0, 0].add_line_stack(sines + cosines, separation=50)\n", "\n", "# make selector\n", "selector = line_stack.add_linear_region_selector()\n", "\n", "# populate subplots with preallocated graphics\n", - "for i, subplot in enumerate(plot):\n", + "for i, subplot in enumerate(fig_stack):\n", " if i == 0:\n", " # skip the first one\n", " continue\n", @@ -182,12 +179,12 @@ " \n", " for i in range(len(zoomed_data)):\n", " data = interpolate(zoomed_data[i], axis=1)\n", - " plot[i + 1, 0][\"zoomed\"].data = data\n", - " plot[i + 1, 0].auto_scale()\n", + " fig_stack[i + 1, 0][\"zoomed\"].data = data\n", + " fig_stack[i + 1, 0].auto_scale()\n", "\n", "\n", "selector.selection.add_event_handler(update_zoomed_subplots)\n", - "plot.show()" + "fig_stack.show()" ] }, { @@ -213,19 +210,19 @@ "sine = np.sin(xs) * 20\n", "cosine = np.cos(xs) * 20\n", "\n", - "plot = fpl.GridPlot((1, 2))\n", + "fig_stack_large = fpl.Figure((1, 2))\n", "\n", "# sines and cosines\n", "sines = [sine] * 1_00\n", "cosines = [cosine] * 1_00\n", "\n", "# make line stack\n", - "line_stack = plot[0, 0].add_line_stack(sines + cosines, separation=50)\n", + "line_stack = fig_stack_large[0, 0].add_line_stack(sines + cosines, separation=50)\n", "\n", "# make selector\n", "stack_selector = line_stack.add_linear_region_selector(padding=200)\n", "\n", - "zoomed_line_stack = plot[0, 1].add_line_stack([zoomed_init] * 2_000, separation=50, name=\"zoomed\")\n", + "zoomed_line_stack = fig_stack_large[0, 1].add_line_stack([zoomed_init] * 2_000, separation=50, name=\"zoomed\")\n", " \n", "def update_zoomed_stack(ev):\n", " \"\"\"update the zoomed subplots\"\"\"\n", @@ -235,11 +232,11 @@ " data = interpolate(zoomed_data[i], axis=1)\n", " zoomed_line_stack.graphics[i].data = data\n", " \n", - " plot[0, 1].auto_scale()\n", + " fig_stack_large[0, 1].auto_scale()\n", "\n", "\n", "stack_selector.selection.add_event_handler(update_zoomed_stack)\n", - "plot.show()" + "fig_stack_large.show()" ] }, { diff --git a/examples/notebooks/linear_selector.ipynb b/examples/notebooks/linear_selector.ipynb index 0f81bc36b..e9c8e664a 100644 --- a/examples/notebooks/linear_selector.ipynb +++ b/examples/notebooks/linear_selector.ipynb @@ -21,14 +21,14 @@ "import numpy as np\n", "from ipywidgets import VBox, IntSlider, FloatSlider\n", "\n", - "plot = fpl.Plot()\n", + "fig = fpl.Figure()\n", "\n", "# data to plot\n", "xs = np.linspace(0, 100, 1000)\n", "sine = np.sin(xs) * 20\n", "\n", "# make sine along x axis\n", - "sine_graphic = plot.add_line(np.column_stack([xs, sine]).astype(np.float32))\n", + "sine_graphic = fig[0, 0].add_line(np.column_stack([xs, sine]).astype(np.float32))\n", "\n", "# make some selectors\n", "selector = sine_graphic.add_linear_selector()\n", @@ -56,8 +56,8 @@ "selector2.add_ipywidget_handler(ipywidget_slider2, step=5)\n", "selector3.add_ipywidget_handler(ipywidget_slider3, step=0.1)\n", "\n", - "plot.auto_scale()\n", - "plot.show(add_widgets=[ipywidget_slider])" + "fig[0, 0].auto_scale()\n", + "fig.show(add_widgets=[ipywidget_slider])" ] }, { @@ -95,9 +95,9 @@ "source": [ "sines = [sine] * 10\n", "\n", - "plot = fpl.Plot()\n", + "fig = fpl.Figure()\n", "\n", - "sine_stack = plot.add_line_stack(sines)\n", + "sine_stack = fig[0, 0].add_line_stack(sines)\n", "\n", "colors = \"y\", \"blue\", \"red\", \"green\"\n", "\n", @@ -108,7 +108,7 @@ " \n", "ss = Synchronizer(*selectors)\n", "\n", - "plot.show()" + "fig.show()" ] }, { diff --git a/examples/notebooks/lineplot.ipynb b/examples/notebooks/lineplot.ipynb index 667cae178..85ebb60f5 100644 --- a/examples/notebooks/lineplot.ipynb +++ b/examples/notebooks/lineplot.ipynb @@ -7,7 +7,7 @@ "tags": [] }, "source": [ - "# A more complex example combing different graphics, gridplot and multiple perspectives" + "# A more complex example combing different graphics, subplots and multiple perspectives" ] }, { @@ -18,7 +18,7 @@ "outputs": [], "source": [ "import numpy as np\n", - "from fastplotlib import GridPlot" + "import fastplotlib as fpl" ] }, { @@ -46,11 +46,11 @@ "metadata": {}, "outputs": [], "source": [ - "# grid with 2 rows and 2 columns\n", + "# figure with 2 rows and 2 columns\n", "shape = (2, 2)\n", "\n", - "# pan-zoom controllers for each view\n", - "# views are synced if they have the \n", + "# pan-zoom controllers for each subplot\n", + "# subplots are synced if they have the\n", "# same controller ID\n", "# in this example the first view has its own controller\n", "# and the last 3 views are synced\n", @@ -59,14 +59,14 @@ " [1, 1]\n", "]\n", "\n", - "# create the grid plot\n", - "grid_plot = GridPlot(\n", + "# create the figure\n", + "fig = fpl.Figure(\n", " shape=shape,\n", - " cameras='3d', # 3D view for all subplots within the grid\n", + " cameras='3d', # 3D view for all subplots within the figure\n", " controller_ids=controller_ids\n", ")\n", "\n", - "for i, subplot in enumerate(grid_plot):\n", + "for i, subplot in enumerate(fig):\n", " # create and add the LineGraphic\n", " line_graphic = subplot.add_line(data=spiral, thickness=3, cmap='jet')\n", " \n", @@ -87,13 +87,13 @@ " if marker_index == spiral.shape[0]:\n", " marker_index = 0\n", " \n", - " for subplot in grid_plot:\n", + " for subplot in fig:\n", " subplot[\"marker\"].data = spiral[marker_index]\n", " \n", "# add `move_marker` to the animations\n", - "grid_plot.add_animations(move_marker)\n", + "fig.add_animations(move_marker)\n", "\n", - "grid_plot.show()" + "fig.show()" ] }, { diff --git a/examples/notebooks/lines_cmap.ipynb b/examples/notebooks/lines_cmap.ipynb index c6dc604b4..dbcbb3e16 100644 --- a/examples/notebooks/lines_cmap.ipynb +++ b/examples/notebooks/lines_cmap.ipynb @@ -55,11 +55,11 @@ }, "outputs": [], "source": [ - "plot = fpl.Plot()\n", + "fig = fpl.Figure()\n", "\n", - "plot.add_line(sine, thickness=10)\n", + "fig[0, 0].add_line(sine, thickness=10)\n", "\n", - "plot.show()" + "fig.show()" ] }, { @@ -71,7 +71,7 @@ }, "outputs": [], "source": [ - "plot_test(\"lines-cmap-white\", plot)" + "plot_test(\"lines-cmap-white\", fig)" ] }, { @@ -91,7 +91,7 @@ }, "outputs": [], "source": [ - "plot.graphics[0].cmap = \"jet\"" + "fig[0, 0].graphics[0].cmap = \"jet\"" ] }, { @@ -104,7 +104,7 @@ "outputs": [], "source": [ "# for testing, ignore\n", - "plot_test(\"lines-cmap-jet\", plot)" + "plot_test(\"lines-cmap-jet\", fig)" ] }, { @@ -116,7 +116,7 @@ }, "outputs": [], "source": [ - "plot.graphics[0].cmap.values = sine[:, 1]" + "fig[0, 0].graphics[0].cmap.values = sine[:, 1]" ] }, { @@ -129,7 +129,7 @@ "outputs": [], "source": [ "# for testing, ignore\n", - "plot_test(\"lines-cmap-jet-values\", plot)" + "plot_test(\"lines-cmap-jet-values\", fig)" ] }, { @@ -141,7 +141,7 @@ }, "outputs": [], "source": [ - "plot.graphics[0].cmap.values = cosine[:, 1]" + "fig[0, 0].graphics[0].cmap.values = cosine[:, 1]" ] }, { @@ -154,7 +154,7 @@ "outputs": [], "source": [ "# for testing, ignore\n", - "plot_test(\"lines-cmap-jet-values-cosine\", plot)" + "plot_test(\"lines-cmap-jet-values-cosine\", fig)" ] }, { @@ -166,7 +166,7 @@ }, "outputs": [], "source": [ - "plot.graphics[0].cmap = \"viridis\"" + "fig[0, 0].graphics[0].cmap = \"viridis\"" ] }, { @@ -179,7 +179,7 @@ "outputs": [], "source": [ "# for testing, ignore\n", - "plot_test(\"lines-cmap-viridis\", plot)" + "plot_test(\"lines-cmap-viridis\", fig)" ] }, { @@ -203,7 +203,7 @@ }, "outputs": [], "source": [ - "plot.graphics[0].cmap.values = cmap_values" + "fig[0, 0].graphics[0].cmap.values = cmap_values" ] }, { @@ -216,7 +216,7 @@ "outputs": [], "source": [ "# for testing, ignore\n", - "plot_test(\"lines-cmap-viridis-values\", plot)" + "plot_test(\"lines-cmap-viridis-values\", fig)" ] }, { @@ -228,7 +228,7 @@ }, "outputs": [], "source": [ - "plot.graphics[0].cmap = \"tab10\"" + "fig[0, 0].graphics[0].cmap = \"tab10\"" ] }, { @@ -239,7 +239,7 @@ "outputs": [], "source": [ "# for testing, ignore\n", - "plot_test(\"lines-cmap-tab-10\", plot)" + "plot_test(\"lines-cmap-tab-10\", fig)" ] }, { diff --git a/examples/notebooks/multiprocessing_zmq/multiprocessing_zmq_plot.ipynb b/examples/notebooks/multiprocessing_zmq/multiprocessing_zmq_plot.ipynb index 2c6e93d8f..564512451 100644 --- a/examples/notebooks/multiprocessing_zmq/multiprocessing_zmq_plot.ipynb +++ b/examples/notebooks/multiprocessing_zmq/multiprocessing_zmq_plot.ipynb @@ -60,13 +60,13 @@ "metadata": {}, "outputs": [], "source": [ - "plot = fpl.Plot()\n", + "fig = fpl.Figure()\n", "\n", "# initialize some data, must be of same dtype and shape as data sent by publisher\n", "data = np.random.rand(512, 512).astype(np.float32)\n", - "plot.add_image(data, name=\"image\")\n", + "fig[0, 0].add_image(data, name=\"image\")\n", "\n", - "def update_frame(p):\n", + "def update_frame(subplot):\n", " # recieve bytes\n", " b = get_bytes()\n", " \n", @@ -75,10 +75,10 @@ " a = np.frombuffer(b, dtype=np.float32).reshape(512, 512)\n", " \n", " # set graphic data\n", - " p[\"image\"].data = a\n", + " subplot[\"image\"].data = a\n", "\n", - "plot.add_animations(update_frame)\n", - "plot.show()" + "fig[0, 0].add_animations(update_frame)\n", + "fig.show()" ] }, { diff --git a/examples/notebooks/nb_test_utils.py b/examples/notebooks/nb_test_utils.py index 90b7158ad..cb84a5271 100644 --- a/examples/notebooks/nb_test_utils.py +++ b/examples/notebooks/nb_test_utils.py @@ -5,7 +5,7 @@ import imageio.v3 as iio import numpy as np -from fastplotlib import Plot, GridPlot +import fastplotlib as fpl # make dirs for screenshots and diffs current_dir = Path(__file__).parent @@ -93,11 +93,11 @@ def _run_tests(): return False -def plot_test(name, plot: Union[Plot, GridPlot]): +def plot_test(name, fig: fpl.Figure): if not _run_tests(): return - snapshot = plot.canvas.snapshot() + snapshot = fig.canvas.snapshot() rgb_img = rgba_to_rgb(snapshot.data) if "REGENERATE_SCREENSHOTS" in os.environ.keys(): diff --git a/examples/notebooks/scatter.ipynb b/examples/notebooks/scatter.ipynb index 9d7ff099f..b78521064 100644 --- a/examples/notebooks/scatter.ipynb +++ b/examples/notebooks/scatter.ipynb @@ -20,7 +20,7 @@ "outputs": [], "source": [ "import numpy as np\n", - "from fastplotlib import GridPlot" + "import fastplotlib as fpl" ] }, { @@ -63,7 +63,7 @@ }, "outputs": [], "source": [ - "# grid with 2 rows and 2 columns\n", + "# figure with 2 rows and 2 columns\n", "shape = (2, 2)\n", "\n", "# define the camera\n", @@ -73,8 +73,8 @@ " ['3d', '2d']\n", "]\n", "\n", - "# pan-zoom controllers for each view\n", - "# views are synced if they have the \n", + "# pan-zoom controllers for each subplot\n", + "# subplots are synced if they have the\n", "# same controller ID\n", "# you can only sync controllers that use the same camera type\n", "# i.e. you cannot sync between 2d and 3d subplots\n", @@ -83,21 +83,21 @@ " [1, 0]\n", "]\n", "\n", - "# create the grid plot\n", - "grid_plot = GridPlot(\n", + "# create the figure\n", + "fig = fpl.Figure(\n", " shape=shape,\n", " cameras=cameras,\n", " controller_ids=controller_ids\n", ")\n", "\n", - "for subplot in grid_plot:\n", + "for subplot in fig:\n", " subplot.add_scatter(data=cloud, colors=colors, alpha=0.7, sizes=5)\n", " \n", " subplot.set_axes_visibility(True)\n", " subplot.set_grid_visibility(True)\n", "\n", "\n", - "grid_plot.show()" + "fig.show()" ] }, { @@ -109,7 +109,7 @@ }, "outputs": [], "source": [ - "grid_plot[0, 1].graphics[0].colors[n_points:int(n_points * 1.5)] = \"r\"" + "fig[0, 1].graphics[0].colors[n_points:int(n_points * 1.5)] = \"r\"" ] }, { @@ -121,7 +121,7 @@ }, "outputs": [], "source": [ - "grid_plot[0, 1].graphics[0].colors[:n_points:10] = \"blue\"" + "fig[0, 1].graphics[0].colors[:n_points:10] = \"blue\"" ] }, { @@ -133,7 +133,7 @@ }, "outputs": [], "source": [ - "grid_plot[0, 1].graphics[0].colors[n_points:] = \"green\"" + "fig[0, 1].graphics[0].colors[n_points:] = \"green\"" ] }, { @@ -145,7 +145,7 @@ }, "outputs": [], "source": [ - "grid_plot[0, 1].graphics[0].colors[n_points:, -1] = 0" + "fig[0, 1].graphics[0].colors[n_points:, -1] = 0" ] }, { @@ -157,7 +157,7 @@ }, "outputs": [], "source": [ - "grid_plot[0, 1].graphics[0].data[:n_points] = grid_plot[0, 1].graphics[0].data[n_points * 2:]" + "fig[0, 1].graphics[0].data[:n_points] = fig[0, 1].graphics[0].data[n_points * 2:]" ] }, { diff --git a/examples/notebooks/scatter_sizes_animation.ipynb b/examples/notebooks/scatter_sizes_animation.ipynb index 06a6b11a2..9ca067bee 100644 --- a/examples/notebooks/scatter_sizes_animation.ipynb +++ b/examples/notebooks/scatter_sizes_animation.ipynb @@ -2,67 +2,37 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "5d9f9913391a42af95d4d43d07c17b19", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "RFBOutputContext()" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "9cd08c319b814934a09fd266a1b6322b", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "JupyterOutputContext(children=(JupyterWgpuCanvas(), IpywidgetToolBar(children=(Button(icon='expand-arrows-alt'…" - ] - }, - "execution_count": 1, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "from time import time\n", "\n", "import numpy as np\n", "import fastplotlib as fpl\n", "\n", - "plot = fpl.Plot()\n", + "fig = fpl.Figure()\n", "\n", "points = np.array([[-1,0,1],[-1,0,1]], dtype=np.float32).swapaxes(0,1)\n", "size_delta_scales = np.array([10, 40, 100], dtype=np.float32)\n", "min_sizes = 6\n", "\n", - "def update_positions():\n", + "def update_positions(subplot):\n", " current_time = time()\n", " newPositions = points + np.sin(((current_time / 4) % 1)*np.pi)\n", - " plot.graphics[0].data = newPositions\n", + " subplot.graphics[0].data = newPositions\n", "\n", - "def update_sizes():\n", + "def update_sizes(subplot):\n", " current_time = time()\n", " sin_sample = np.sin(((current_time / 4) % 1)*np.pi)\n", " size_delta = sin_sample*size_delta_scales\n", - " plot.graphics[0].sizes = min_sizes + size_delta\n", + " subplot.graphics[0].sizes = min_sizes + size_delta\n", "\n", - "scatter = plot.add_scatter(points, colors=[\"red\", \"green\", \"blue\"], sizes=12)\n", - "plot.add_animations(update_positions, update_sizes)\n", + "scatter = fig[0, 0].add_scatter(points, colors=[\"red\", \"green\", \"blue\"], sizes=12)\n", + "fig[0, 0].add_animations(update_positions, update_sizes)\n", "\n", - "plot.camera.width = 12\n", - "plot.show(autoscale=False)" + "fig[0, 0].camera.width = 12\n", + "fig.show(autoscale=False)" ] }, { diff --git a/examples/notebooks/scatter_sizes_grid.ipynb b/examples/notebooks/scatter_sizes_grid.ipynb index e152056c9..21985f189 100644 --- a/examples/notebooks/scatter_sizes_grid.ipynb +++ b/examples/notebooks/scatter_sizes_grid.ipynb @@ -16,18 +16,18 @@ "import numpy as np\n", "import fastplotlib as fpl\n", "\n", - "# grid with 2 rows and 3 columns\n", - "grid_shape = (2,1)\n", + "# figure with 2 rows and 3 columns\n", + "shape = (2, 1)\n", "\n", - "# you can give string names for each subplot within the gridplot\n", + "# you can give string names for each subplot\n", "names = [\n", " [\"scalar_size\"],\n", " [\"array_size\"]\n", "]\n", "\n", - "# Create the grid plot\n", - "plot = fpl.GridPlot(\n", - " shape=grid_shape,\n", + "# Create the figure\n", + "fig = fpl.Figure(\n", + " shape=shape,\n", " names=names,\n", " size=(1000, 1000)\n", ")\n", @@ -39,15 +39,15 @@ "\n", "data = np.column_stack([x_values, y_values])\n", "\n", - "plot[\"scalar_size\"].add_scatter(data=data, sizes=5, colors=\"blue\") # add a set of scalar sizes\n", + "fig[\"scalar_size\"].add_scatter(data=data, sizes=5, colors=\"blue\") # add a set of scalar sizes\n", "\n", "non_scalar_sizes = np.abs((y_values / np.pi)) # ensure minimum size of 5\n", - "plot[\"array_size\"].add_scatter(data=data, sizes=non_scalar_sizes, colors=\"red\")\n", + "fig[\"array_size\"].add_scatter(data=data, sizes=non_scalar_sizes, colors=\"red\")\n", "\n", - "for graph in plot:\n", - " graph.auto_scale(maintain_aspect=True)\n", + "for subplot in fig:\n", + " subplot.auto_scale(maintain_aspect=True)\n", "\n", - "plot.show()" + "fig.show()" ] }, { diff --git a/examples/notebooks/simple.ipynb b/examples/notebooks/simple.ipynb index f5901080b..3b42385c8 100644 --- a/examples/notebooks/simple.ipynb +++ b/examples/notebooks/simple.ipynb @@ -53,7 +53,7 @@ }, "outputs": [], "source": [ - "from fastplotlib import Plot\n", + "import fastplotlib as fpl\n", "from ipywidgets import VBox, HBox, IntSlider\n", "import numpy as np" ] @@ -90,17 +90,18 @@ }, "outputs": [], "source": [ - "# create a `Plot` instance\n", - "plot = Plot()\n", + "# create a `Figure` instance\n", + "# by default the figure will have 1 subplot\n", + "fig = fpl.Figure()\n", "\n", "# get a grayscale image\n", "data = iio.imread(\"imageio:camera.png\")\n", "\n", "# plot the image data\n", - "image_graphic = plot.add_image(data=data, name=\"sample-image\")\n", + "image_graphic = fig[0, 0].add_image(data=data, name=\"sample-image\")\n", "\n", "# show the plot\n", - "plot.show(sidecar=True)" + "fig.show(sidecar=True)" ] }, { @@ -221,7 +222,7 @@ "outputs": [], "source": [ "# testing cell, ignore\n", - "plot_test(\"camera\", plot)" + "plot_test(\"camera\", fig)" ] }, { @@ -312,7 +313,7 @@ "outputs": [], "source": [ "# testing cell, ignore\n", - "plot_test(\"astronaut\", plot)" + "plot_test(\"astronaut\", fig)" ] }, { @@ -324,7 +325,7 @@ }, "outputs": [], "source": [ - "plot.canvas.get_logical_size()" + "fig.canvas.get_logical_size()" ] }, { @@ -332,7 +333,7 @@ "id": "b53bc11a-ddf1-4786-8dca-8f3d2eaf993d", "metadata": {}, "source": [ - "### Indexing plots" + "### Indexing subplots" ] }, { @@ -340,7 +341,7 @@ "id": "67b92ffd-40cc-43fe-9df9-0e0d94763d8e", "metadata": {}, "source": [ - "**Plots are indexable and give you their graphics by name**" + "**Subplots are indexable and give you their graphics by name**" ] }, { @@ -352,27 +353,37 @@ }, "outputs": [], "source": [ - "plot" + "fig[0, 0]" ] }, { - "cell_type": "code", - "execution_count": null, - "id": "5b18f4e3-e13b-46d5-af1f-285c5a7fdc12", + "cell_type": "markdown", + "id": "e6eccef1", "metadata": { - "tags": [] + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } }, - "outputs": [], "source": [ - "plot[\"sample-image\"]" + "Access graphics in a subplot" ] }, { - "cell_type": "markdown", - "id": "a64314bf-a737-4858-803b-ea2adbd3578c", - "metadata": {}, + "cell_type": "code", + "execution_count": null, + "id": "7d8eaaf3", + "metadata": { + "collapsed": false, + "is_executing": true, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], "source": [ - "**You can also use numerical indexing on `plot.graphics`**" + "# by name\n", + "fig[0, 0][\"sample-image\"]" ] }, { @@ -384,7 +395,8 @@ }, "outputs": [], "source": [ - "plot.graphics" + "# or through the .graphics property of a subplot\n", + "fig[0, 0].graphics" ] }, { @@ -396,7 +408,8 @@ }, "outputs": [], "source": [ - "plot.graphics[0]" + "# these are the same!\n", + "fig[0, 0].graphics[0] is fig[0, 0][\"sample-image\"]" ] }, { @@ -428,7 +441,7 @@ }, "outputs": [], "source": [ - "image_graphic == plot[\"sample-image\"]" + "image_graphic == fig[0, 0][\"sample-image\"]" ] }, { @@ -438,8 +451,8 @@ "metadata": {}, "outputs": [], "source": [ - "# close the plot\n", - "plot.close()" + "# close the figure\n", + "fig.close()" ] }, { @@ -461,12 +474,12 @@ }, "outputs": [], "source": [ - "plot_rgb = Plot()\n", + "fig_rgb = fpl.Figure()\n", "\n", - "plot_rgb.add_image(new_data, name=\"rgb-image\")\n", + "fig_rgb[0, 0].add_image(new_data, name=\"rgb-image\")\n", "\n", - "# show the plot\n", - "plot_rgb.show()" + "# show the figure\n", + "fig_rgb.show()" ] }, { @@ -486,7 +499,7 @@ }, "outputs": [], "source": [ - "plot_rgb[\"rgb-image\"].cmap.vmin = 100" + "fig_rgb[0, 0][\"rgb-image\"].cmap.vmin = 100" ] }, { @@ -499,7 +512,7 @@ "outputs": [], "source": [ "# testing cell, ignore\n", - "plot_test(\"astronaut_RGB\", plot_rgb)" + "plot_test(\"astronaut_RGB\", fig_rgb)" ] }, { @@ -509,8 +522,8 @@ "metadata": {}, "outputs": [], "source": [ - "# close plot\n", - "plot_rgb.close()" + "# close figure\n", + "fig_rgb.close()" ] }, { @@ -534,28 +547,35 @@ }, "outputs": [], "source": [ - "# create another `Plot` instance\n", - "plot_v = Plot()\n", + "# create a figure\n", + "fig_v = fpl.Figure()\n", "\n", - "plot.canvas.max_buffered_frames = 1\n", + "fig.canvas.max_buffered_frames = 1\n", "\n", "# make some random data again\n", "data = np.random.rand(512, 512)\n", "\n", "# plot the data\n", - "plot_v.add_image(data=data, name=\"random-image\")\n", + "fig_v[0, 0].add_image(data=data, name=\"random-image\")\n", "\n", "# a function to update the image_graphic\n", - "# a plot will pass its plot instance to the animation function as an argument\n", - "def update_data(plot_instance):\n", + "# a figure-level animation function will optionally take the figure as an argument\n", + "def update_data(figure_instance):\n", " new_data = np.random.rand(512, 512)\n", - " plot_instance[\"random-image\"].data = new_data\n", + " figure_instance[0, 0][\"random-image\"].data = new_data\n", + "\n", + "# you can also add animation functions to individual subplots\n", + "def update_data_subplot(subplot_instance):\n", + " pass\n", "\n", - "#add this as an animation function\n", - "plot_v.add_animations(update_data)\n", + "# add this as an animation function\n", + "fig_v.add_animations(update_data)\n", + "\n", + "# similarly you can add animation function to a subplot\n", + "# fig_v[0, 0].add_animations(update_data_subplot)\n", "\n", "# show the plot\n", - "plot_v.show()" + "fig_v.show()" ] }, { @@ -565,7 +585,7 @@ "source": [ "### We can share controllers across plots\n", "\n", - "This example creates a new plot, but it synchronizes the pan-zoom controller" + "This example creates a new figure, but it share the pan-zoom controllers from the previous figure!" ] }, { @@ -575,21 +595,21 @@ "metadata": {}, "outputs": [], "source": [ - "plot_sync = Plot(controller=plot_v.controller)\n", + "fig_sync = fpl.Figure(controllers=fig_v.controllers)\n", "\n", "data = np.random.rand(512, 512)\n", "\n", - "image_graphic_instance = plot_sync.add_image(data=data, cmap=\"viridis\")\n", + "image_graphic_instance = fig_sync[0, 0].add_image(data=data, cmap=\"viridis\")\n", "\n", "# you will need to define a new animation function for this graphic\n", "def update_data_2():\n", " new_data = np.random.rand(512, 512)\n", - " # alternatively, you can use the stored reference to the graphic as well instead of indexing the Plot\n", + " # alternatively, you can use the stored reference to the graphic as well instead of indexing the subplot\n", " image_graphic_instance.data = new_data\n", "\n", - "plot_sync.add_animations(update_data_2)\n", + "fig_sync.add_animations(update_data_2)\n", "\n", - "plot_sync.show()" + "fig_sync.show()" ] }, { @@ -605,7 +625,7 @@ "id": "d11fabb7-7c76-4e94-893d-80ed9ee3be3d", "metadata": {}, "source": [ - "### You can also use `ipywidgets.VBox` and `HBox` to stack plots. See the `gridplot` notebooks for a proper gridplot interface for more automated subplotting" + "### You can also use `ipywidgets.VBox` and `HBox` to stack plots. See the `subplot` notebooks for more automated subplotting" ] }, { @@ -615,7 +635,7 @@ "metadata": {}, "outputs": [], "source": [ - "HBox([plot_v.show(), plot_sync.show()])" + "HBox([fig_v.show(), fig_sync.show()])" ] }, { @@ -625,9 +645,9 @@ "metadata": {}, "outputs": [], "source": [ - "# close plot\n", - "plot_v.close()\n", - "plot_sync.close()" + "# close figures\n", + "fig_v.close()\n", + "fig_sync.close()" ] }, { @@ -690,21 +710,24 @@ "metadata": {}, "outputs": [], "source": [ - "# Create a plot instance\n", - "plot_l = Plot()\n", + "# Create a figure\n", + "fig_lines = fpl.Figure()\n", + "\n", + "# we will add all the lines to the same subplot\n", + "subplot = fig_lines[0, 0]\n", "\n", "# plot sine wave, use a single color\n", - "sine_graphic = plot_l.add_line(data=sine, thickness=5, colors=\"magenta\")\n", + "sine_graphic = subplot.add_line(data=sine, thickness=5, colors=\"magenta\")\n", "\n", "# you can also use colormaps for lines!\n", - "cosine_graphic = plot_l.add_line(data=cosine, thickness=12, cmap=\"autumn\")\n", + "cosine_graphic = subplot.add_line(data=cosine, thickness=12, cmap=\"autumn\")\n", "\n", "# or a list of colors for each datapoint\n", "colors = [\"r\"] * 25 + [\"purple\"] * 25 + [\"y\"] * 25 + [\"b\"] * 25\n", - "sinc_graphic = plot_l.add_line(data=sinc, thickness=5, colors = colors)\n", + "sinc_graphic = subplot.add_line(data=sinc, thickness=5, colors = colors)\n", "\n", "# show the plot\n", - "plot_l.show(sidecar=True, sidecar_kwargs={\"title\": \"lines\"})" + "fig_lines.show(sidecar=True, sidecar_kwargs={\"title\": \"lines\"})" ] }, { @@ -717,7 +740,7 @@ "outputs": [], "source": [ "# testing cell, ignore\n", - "plot_test(\"lines\", plot_l)" + "plot_test(\"lines\", fig_lines)" ] }, { @@ -729,7 +752,7 @@ "\n", "Set `maintain_aspect = False` on a camera, and then use the right mouse button and move the mouse to stretch and squeeze the view!\n", "\n", - "You can also click the **`1:1`** button to toggle this, or use `plot.camera.maintain_aspect`" + "You can also click the **`1:1`** button to toggle this, or use `subplot.camera.maintain_aspect`" ] }, { @@ -749,7 +772,7 @@ }, "outputs": [], "source": [ - "plot_l.auto_scale(maintain_aspect=True)" + "subplot.auto_scale(maintain_aspect=True)" ] }, { @@ -824,7 +847,7 @@ "outputs": [], "source": [ "# testing cell, ignore\n", - "plot_test(\"lines-colors\", plot_l)" + "plot_test(\"lines-colors\", fig_lines)" ] }, { @@ -832,7 +855,7 @@ "id": "c29f81f9-601b-49f4-b20c-575c56e58026", "metadata": {}, "source": [ - "## Graphic _data_ is also indexable" + "## Graphic _data_ is also slicable and settable" ] }, { @@ -866,7 +889,7 @@ "outputs": [], "source": [ "# testing cell, ignore\n", - "plot_test(\"lines-data\", plot_l)" + "plot_test(\"lines-data\", fig_lines)" ] }, { @@ -912,7 +935,7 @@ "metadata": {}, "outputs": [], "source": [ - "sinc_graphic.present.add_event_handler(plot_l.auto_scale)" + "sinc_graphic.present.add_event_handler(subplot.auto_scale)" ] }, { @@ -954,12 +977,12 @@ "source": [ "img = iio.imread(\"imageio:camera.png\")\n", "\n", - "plot_l.add_image(img[::20, ::20], name=\"image\", cmap=\"gray\")\n", + "subplot.add_image(img[::20, ::20], name=\"image\", cmap=\"gray\")\n", "\n", "# z axis position -1 so it is below all the lines\n", - "plot_l[\"image\"].position_z = -1\n", - "plot_l[\"image\"].position_x = -8\n", - "plot_l[\"image\"].position_y = -8" + "subplot[\"image\"].position_z = -1\n", + "subplot[\"image\"].position_x = -8\n", + "subplot[\"image\"].position_y = -8" ] }, { @@ -972,7 +995,7 @@ "outputs": [], "source": [ "# testing cell, ignore\n", - "plot_test(\"lines-underlay\", plot_l)" + "plot_test(\"lines-underlay\", fig_lines)" ] }, { @@ -983,7 +1006,7 @@ "outputs": [], "source": [ "# close plot\n", - "plot_l.close()" + "fig_lines.close()" ] }, { @@ -1001,8 +1024,8 @@ "metadata": {}, "outputs": [], "source": [ - "# just set the camera as \"3d\", the rest is basically the same :D \n", - "plot_l3d = Plot(camera='3d')\n", + "# just set the camera as \"3d\", the rest is basically the same :D\n", + "fig_l3d = fpl.Figure(cameras=\"3d\")\n", "\n", "# create a spiral\n", "phi = np.linspace(0, 30, 200)\n", @@ -1015,9 +1038,9 @@ "# note: you usually mix 3D and 2D graphics on the same plot\n", "spiral = np.dstack([xs, ys, zs])[0]\n", "\n", - "plot_l3d.add_line(data=spiral, thickness=2, cmap='winter')\n", + "fig_l3d[0, 0].add_line(data=spiral, thickness=2, cmap='winter')\n", "\n", - "plot_l3d.show()" + "fig_l3d.show()" ] }, { @@ -1037,7 +1060,7 @@ }, "outputs": [], "source": [ - "plot_l3d.auto_scale(maintain_aspect=True)" + "fig_l3d[0, 0].auto_scale(maintain_aspect=True)" ] }, { @@ -1050,7 +1073,7 @@ "outputs": [], "source": [ "# testing cell, ignore\n", - "plot_test(\"lines-3d\", plot_l3d)" + "plot_test(\"lines-3d\", fig_l3d)" ] }, { @@ -1061,7 +1084,23 @@ "outputs": [], "source": [ "# change the FOV of the persepctive camera\n", - "plot_l3d.camera.fov = 70" + "fig_l3d[0, 0].camera.fov = 70" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9e126e6c", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "# change the controller, ex. from the current \"fly\" controller to a \"panzoom\" controller\n", + "fig_l3d[0, 0].controller = \"panzoom\"" ] }, { @@ -1074,7 +1113,7 @@ "outputs": [], "source": [ "# close plot\n", - "plot_l3d.close()" + "fig_l3d.close()" ] }, { @@ -1089,18 +1128,6 @@ "#### There might be a small delay for a few seconds before the plot shows, this is due to shaders being compiled and a few other things. The plot should be very fast and responsive once it is displayed and future modifications should also be fast!" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "2ecb2385-8fa4-4239-881c-b754c24aed9f", - "metadata": {}, - "outputs": [], - "source": [ - "from fastplotlib import Plot\n", - "from ipywidgets import VBox, HBox, IntSlider\n", - "import numpy as np" - ] - }, { "cell_type": "code", "execution_count": null, @@ -1135,12 +1162,12 @@ "colors = [\"yellow\"] * n_points + [\"cyan\"] * n_points + [\"magenta\"] * n_points\n", "\n", "# create plot\n", - "plot_s = Plot()\n", - "\n", + "fig_scatter = fpl.Figure()\n", + "subplot_scatter = fig_scatter[0, 0]\n", "# use an alpha value since this will be a lot of points\n", - "scatter_graphic = plot_s.add_scatter(data=cloud, sizes=3, colors=colors, alpha=0.7)\n", + "scatter_graphic = subplot_scatter.add_scatter(data=cloud, sizes=3, colors=colors, alpha=0.7)\n", "\n", - "plot_s.show()" + "fig_scatter.show()" ] }, { @@ -1221,8 +1248,8 @@ "metadata": {}, "outputs": [], "source": [ - "plot_s.camera = \"3d\"\n", - "plot_s.controller = \"fly\"" + "subplot_scatter.camera = \"3d\"\n", + "subplot_scatter.controller = \"fly\"" ] }, { @@ -1233,7 +1260,7 @@ "outputs": [], "source": [ "# close plot\n", - "plot_s.close()" + "fig_scatter.close()" ] }, { diff --git a/examples/notebooks/gridplot.ipynb b/examples/notebooks/subplots.ipynb similarity index 72% rename from examples/notebooks/gridplot.ipynb rename to examples/notebooks/subplots.ipynb index f1ceb2180..72b4b3007 100644 --- a/examples/notebooks/gridplot.ipynb +++ b/examples/notebooks/subplots.ipynb @@ -5,7 +5,7 @@ "id": "3cfc2d9f-6a09-42f4-a47c-3ba51f1a1801", "metadata": {}, "source": [ - "### More in-depth on `GridPlot`" + "### More in-depth on subplots with a Figure" ] }, { @@ -16,7 +16,7 @@ "outputs": [], "source": [ "import numpy as np\n", - "from fastplotlib import GridPlot" + "import fastplotlib as fpl" ] }, { @@ -27,10 +27,10 @@ "outputs": [], "source": [ "# grid with 2 rows and 3 columns\n", - "grid_shape = (2, 3)\n", + "shape = (2, 3)\n", "\n", - "# pan-zoom controllers for each view\n", - "# views are synced if they have the \n", + "# pan-zoom controllers for each subplot\n", + "# subplots are synced if they have the\n", "# same controller ID\n", "controller_ids = [\n", " [0, -3, 1], # id each controller with an integer\n", @@ -44,22 +44,22 @@ "]\n", "\n", "\n", - "# you can give string names for each subplot within the gridplot\n", + "# you can give string names for each subplot within the figure\n", "names = [\n", " [\"subplot0\", \"subplot1\", \"subplot2\"],\n", " [\"subplot3\", \"subplot4\", \"subplot5\"]\n", "]\n", "\n", - "# Create the grid plot\n", - "grid_plot = GridPlot(\n", - " shape=grid_shape,\n", + "# Create the figure\n", + "fig = fpl.Figure(\n", + " shape=shape,\n", " controller_ids=controller_ids,\n", " names=names,\n", ")\n", "\n", "\n", "# Make a random image graphic for each subplot\n", - "for subplot in grid_plot:\n", + "for subplot in fig:\n", " data = np.random.rand(512, 512)\n", " # create and add an ImageGraphic\n", " subplot.add_image(data=data, name=\"rand-image\")\n", @@ -73,8 +73,8 @@ " subplot[\"rand-image\"].data = new_data\n", "\n", "# add the animation\n", - "grid_plot.add_animations(set_random_frame)\n", - "grid_plot.show()" + "fig.add_animations(set_random_frame)\n", + "fig.show()" ] }, { @@ -82,18 +82,20 @@ "id": "2867bcd6-7691-4073-91d9-9c33e8fdb896", "metadata": {}, "source": [ - "### Indexing the gridplot to access subplots" + "### Accessing subplots" ] }, { "cell_type": "code", "execution_count": null, "id": "2a6f7eb5-776e-42a6-b6c2-c26009a26795", - "metadata": {}, + "metadata": { + "is_executing": true + }, "outputs": [], "source": [ - "# can access subplot by name\n", - "grid_plot[\"subplot0\"]" + "# by name\n", + "fig[\"subplot0\"]" ] }, { @@ -103,8 +105,8 @@ "metadata": {}, "outputs": [], "source": [ - "# can access subplot by index\n", - "grid_plot[0, 0]" + "# by index\n", + "fig[0, 0]" ] }, { @@ -112,7 +114,7 @@ "id": "3272b8b3-3063-47a4-94c8-15ceeeaecc69", "metadata": {}, "source": [ - "## subplots also support indexing!\n", + "## getting graphics within subplots!\n", "this can be used to get graphics if they are named" ] }, @@ -124,7 +126,7 @@ "outputs": [], "source": [ "# can access graphic directly via name\n", - "grid_plot[\"subplot0\"][\"rand-image\"]" + "fig[\"subplot0\"][\"rand-image\"]" ] }, { @@ -134,17 +136,39 @@ "metadata": {}, "outputs": [], "source": [ - "grid_plot[\"subplot0\"][\"rand-image\"].cmap.vmin = 0.6\n", - "grid_plot[\"subplot0\"][\"rand-image\"].cmap.vmax = 0.8" + "fig[\"subplot0\"][\"rand-image\"].cmap.vmin = 0.6\n", + "fig[\"subplot0\"][\"rand-image\"].cmap.vmax = 0.8" ] }, { "cell_type": "markdown", - "id": "516a46e1-cc53-4137-b49b-d5fb94e212d7", - "metadata": {}, "source": [ - "### positional indexing also works event if subplots have string names" - ] + "If they are not named use .graphics" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "fig[\"subplot0\"].graphics" + ], + "metadata": { + "collapsed": false, + "is_executing": true + } + }, + { + "cell_type": "markdown", + "source": [ + "### positional indexing also works" + ], + "metadata": { + "collapsed": false + } }, { "cell_type": "code", @@ -153,8 +177,8 @@ "metadata": {}, "outputs": [], "source": [ - "grid_plot[1, 0][\"rand-image\"].cmap.vim = 0.1\n", - "grid_plot[1, 0][\"rand-image\"].cmap.vmax = 0.3" + "fig[1, 0][\"rand-image\"].cmap.vim = 0.1\n", + "fig[1, 0][\"rand-image\"].cmap.vmax = 0.3" ] }, { diff --git a/examples/notebooks/gridplot_simple.ipynb b/examples/notebooks/subplots_simple.ipynb similarity index 69% rename from examples/notebooks/gridplot_simple.ipynb rename to examples/notebooks/subplots_simple.ipynb index 74807f55a..e519584d3 100644 --- a/examples/notebooks/gridplot_simple.ipynb +++ b/examples/notebooks/subplots_simple.ipynb @@ -5,12 +5,12 @@ "id": "0e42f03b-9cdf-484f-b158-78b07fdf524d", "metadata": {}, "source": [ - "## This notebook shows how you can use more of the `fastplotlib` API to create `Graphic` objects and add them to a `GridPlot`" + "## This notebook shows how you can use more of the `fastplotlib` API to create `Graphic` objects and add them to subplots" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "id": "5171a06e-1bdc-4908-9726-3c1fd45dbb9d", "metadata": { "ExecuteTime": { @@ -19,10 +19,43 @@ }, "tags": [] }, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "301d76bd4c5c42c7912cdd28651e2899", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Image(value=b'\\x89PNG\\r\\n\\x1a\\n\\x00\\x00\\x00\\rIHDR\\x00\\x00\\x01,\\x00\\x00\\x007\\x08\\x06\\x00\\x00\\x00\\xb6\\x1bw\\x99\\x…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Unable to find extension: VK_EXT_swapchain_colorspace\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Available devices:\n", + "✅ (default) | AMD RADV POLARIS10 (ACO) | DiscreteGPU | Vulkan | Mesa 20.3.5 (ACO)\n", + "❗ | llvmpipe (LLVM 11.0.1, 256 bits) | CPU | Vulkan | Mesa 20.3.5 (LLVM 11.0.1)\n", + "✅ | NVIDIA GeForce RTX 3080 | DiscreteGPU | Vulkan | 530.30.02\n", + "❗ | Radeon RX 570 Series (POLARIS10, DRM 3.40.0, 5.10.0-21-amd64, LLVM 11.0.1) | Unknown | OpenGL | \n" + ] + } + ], "source": [ "import numpy as np\n", - "from fastplotlib import GridPlot" + "import fastplotlib as fpl" ] }, { @@ -38,29 +71,29 @@ }, "outputs": [], "source": [ - "# GridPlot of shape 2 x 3 with all controllers synced\n", - "grid_plot = GridPlot(shape=(2, 3), controller_ids=\"sync\")\n", + "# Figure of shape 2 x 3 with all controllers synced\n", + "fig = fpl.Figure(shape=(2, 3), controller_ids=\"sync\")\n", "\n", "# Make a random image graphic for each subplot\n", - "for subplot in grid_plot:\n", + "for subplot in fig:\n", " # create image data\n", " data = np.random.rand(512, 512)\n", " # add an image to the subplot\n", " subplot.add_image(data, name=\"rand-img\")\n", "\n", "# Define a function to update the image graphics with new data\n", - "# add_animations will pass the gridplot to the animation function\n", - "def update_data(gp):\n", - " for sp in gp:\n", + "# add_animations will pass the figure to the animation function\n", + "def update_data(f):\n", + " for sp in f:\n", " new_data = np.random.rand(512, 512)\n", " # index the image graphic by name and set the data\n", " sp[\"rand-img\"].data = new_data\n", " \n", "# add the animation function\n", - "grid_plot.add_animations(update_data)\n", + "fig.add_animations(update_data)\n", "\n", - "# show the gridplot \n", - "grid_plot.show()" + "# show the figure\n", + "fig.show()" ] }, { @@ -68,7 +101,7 @@ "id": "e7801781-c3e9-490f-ab12-1cd2f480d3e9", "metadata": {}, "source": [ - "## Accessing subplots within `GridPlot`" + "## Accessing subplots within `Figure`" ] }, { @@ -78,7 +111,7 @@ "metadata": {}, "outputs": [], "source": [ - "grid_plot" + "fig" ] }, { @@ -92,7 +125,7 @@ "source": [ "# positional indexing\n", "# row 0 and col 0\n", - "grid_plot[0, 0]" + "fig[0, 0]" ] }, { @@ -100,7 +133,7 @@ "id": "276dfede-e9bc-4488-b9e6-3ca5cf91e4dc", "metadata": {}, "source": [ - "### You can get the graphics within a subplot, just like with simple `Plot`" + "### You can get the graphics within a subplot" ] }, { @@ -112,7 +145,7 @@ }, "outputs": [], "source": [ - "grid_plot[0, 1].graphics" + "fig[0, 1].graphics" ] }, { @@ -132,7 +165,7 @@ }, "outputs": [], "source": [ - "grid_plot[0, 1].graphics[0].cmap.vmax = 0.5" + "fig[0, 1].graphics[0].cmap.vmax = 0.5" ] }, { @@ -140,7 +173,7 @@ "id": "00506fa1-2dc0-4435-96a0-e50667d3174f", "metadata": {}, "source": [ - "### more indexing with `GridPlot`" + "### more indexing" ] }, { @@ -153,7 +186,7 @@ "outputs": [], "source": [ "# you can give subplots human-readable string names\n", - "grid_plot[0, 2].name = \"top-right-plot\"" + "fig[0, 2].name = \"top-right-plot\"" ] }, { @@ -165,7 +198,7 @@ }, "outputs": [], "source": [ - "grid_plot[\"top-right-plot\"]" + "fig[\"top-right-plot\"]" ] }, { @@ -178,7 +211,7 @@ "outputs": [], "source": [ "# view its position\n", - "grid_plot[\"top-right-plot\"].position" + "fig[\"top-right-plot\"].position" ] }, { @@ -191,7 +224,7 @@ "outputs": [], "source": [ "# these are really the same\n", - "grid_plot[\"top-right-plot\"] is grid_plot[0, 2]" + "fig[\"top-right-plot\"] is fig[0, 2]" ] }, { @@ -211,7 +244,7 @@ }, "outputs": [], "source": [ - "grid_plot[\"top-right-plot\"][\"rand-img\"].cmap.vmin = 0.5" + "fig[\"top-right-plot\"][\"rand-img\"].cmap.vmin = 0.5" ] }, { @@ -223,7 +256,7 @@ }, "outputs": [], "source": [ - "grid_plot.close()" + "fig.close()" ] }, { diff --git a/examples/notebooks/test_gc.ipynb b/examples/notebooks/test_gc.ipynb index 6caf6a9e3..39f964cf7 100644 --- a/examples/notebooks/test_gc.ipynb +++ b/examples/notebooks/test_gc.ipynb @@ -59,12 +59,12 @@ "metadata": {}, "outputs": [], "source": [ - "gp = fpl.GridPlot((2, 2))\n", + "fig = fpl.Figure((2, 2))\n", "\n", - "line = gp[0, 0].add_line(points_data, name=\"line\")\n", - "scatter = gp[0, 1].add_scatter(points_data.copy(), name=\"scatter\")\n", - "line_stack = gp[1, 0].add_line_stack(line_collection_data, name=\"line-stack\")\n", - "image = gp[1, 1].add_image(img_data, name=\"image\")\n", + "line = fig[0, 0].add_line(points_data, name=\"line\")\n", + "scatter = fig[0, 1].add_scatter(points_data.copy(), name=\"scatter\")\n", + "line_stack = fig[1, 0].add_line_stack(line_collection_data, name=\"line-stack\")\n", + "image = fig[1, 1].add_image(img_data, name=\"image\")\n", "\n", "linear_sel = line.add_linear_selector(name=\"line_linear_sel\")\n", "linear_region_sel = line.add_linear_region_selector(name=\"line_region_sel\")\n", @@ -88,7 +88,7 @@ "\n", "\n", "objects = list()\n", - "for subplot in gp:\n", + "for subplot in fig:\n", " objects += subplot.objects\n", "\n", "\n", @@ -100,7 +100,7 @@ " f = getattr(g, feature)\n", " f.add_event_handler(feature_changed_handler)\n", "\n", - "gp.show()" + "fig.show()" ] }, { @@ -110,7 +110,7 @@ "metadata": {}, "outputs": [], "source": [ - "gp.clear()" + "fig.clear()" ] }, { diff --git a/examples/tests/test_examples.py b/examples/tests/test_examples.py index a570b4f36..b8369e368 100644 --- a/examples/tests/test_examples.py +++ b/examples/tests/test_examples.py @@ -69,7 +69,7 @@ def test_example_screenshots(module, force_offscreen): example = importlib.import_module(module_name) # render a frame - img = np.asarray(example.plot.renderer.target.draw()) + img = np.asarray(example.fig.renderer.target.draw()) # check if _something_ was rendered assert img is not None and img.size > 0 diff --git a/fastplotlib/__init__.py b/fastplotlib/__init__.py index 33db8c79d..8e6341156 100644 --- a/fastplotlib/__init__.py +++ b/fastplotlib/__init__.py @@ -1,12 +1,13 @@ from pathlib import Path -from .layouts import Plot, GridPlot +from .utils.gui import run from .graphics import * from .graphics.selectors import * from .legends import * +from .layouts import Figure + from .widgets import ImageWidget from .utils import config -from .utils.gui import run import wgpu @@ -21,9 +22,8 @@ __all__ = [ - "Plot", - "GridPlot", + "Figure", "run", - "ImageWidget", + # "ImageWidget", "Legend", ] diff --git a/fastplotlib/layouts/__init__.py b/fastplotlib/layouts/__init__.py index aaed4c5a4..60111cabc 100644 --- a/fastplotlib/layouts/__init__.py +++ b/fastplotlib/layouts/__init__.py @@ -1,4 +1,3 @@ -from ._gridplot import GridPlot -from ._plot import Plot +from ._figure import Figure -__all__ = ["Plot", "GridPlot"] +__all__ = ["Figure"] diff --git a/fastplotlib/layouts/_gridplot.py b/fastplotlib/layouts/_figure.py similarity index 67% rename from fastplotlib/layouts/_gridplot.py rename to fastplotlib/layouts/_figure.py index 472d3dd2e..7e83e103a 100644 --- a/fastplotlib/layouts/_gridplot.py +++ b/fastplotlib/layouts/_figure.py @@ -1,4 +1,9 @@ +import os from itertools import product, chain +from multiprocessing import Queue +from pathlib import Path +from time import time + import numpy as np from typing import Literal, Iterable from inspect import getfullargspec @@ -8,17 +13,17 @@ from wgpu.gui import WgpuCanvasBase -from ._frame import Frame +from ._video_writer import VideoWriterAV from ._utils import make_canvas_and_renderer, create_controller, create_camera from ._utils import controller_types as valid_controller_types from ._subplot import Subplot -from ._record_mixin import RecordMixin +from .. import ImageGraphic -class GridPlot(Frame, RecordMixin): +class Figure: def __init__( self, - shape: tuple[int, int], + shape: tuple[int, int] = (1, 1), cameras: ( Literal["2d", "3d"] | Iterable[Iterable[Literal["2d", "3d"]]] @@ -46,7 +51,7 @@ def __init__( Parameters ---------- - shape: (int, int) + shape: (int, int), default (1, 1) (n_rows, n_cols) cameras: "2d", "3", list of "2d" | "3d", Iterable of camera instances, or Iterable of "2d" | "3d", optional @@ -76,7 +81,7 @@ def __init__( controllers: pygfx.Controller | list[pygfx.Controller] | np.ndarray[pygfx.Controller], optional directly provide pygfx.Controller instances(s). Useful if you want to use a controller from an existing - plot/subplot. Other controller kwargs, i.e. ``controller_Types`` and ``controller_ids`` are ignored if + plot/subplot. Other controller kwargs, i.e. ``controller_types`` and ``controller_ids`` are ignored if ``controllers`` are provided. canvas: WgpuCanvas, optional @@ -97,7 +102,7 @@ def __init__( if names is not None: if len(list(chain(*names))) != len(self): raise ValueError( - "must provide same number of subplot `names` as specified by gridplot shape" + "must provide same number of subplot `names` as specified by Figure `shape`" ) subplot_names = np.asarray(names).reshape(self.shape) @@ -141,7 +146,7 @@ def __init__( pass else: raise TypeError( - "controllers argument must be a single pygfx.Controller instance of a Iterable of " + "controllers argument must be a single pygfx.Controller instance, or a Iterable of " "pygfx.Controller instances" ) @@ -326,8 +331,22 @@ def __init__( self._starting_size = size - RecordMixin.__init__(self) - Frame.__init__(self) + self._output = None + + if self.canvas.__class__.__name__ == "JupyterWgpuCanvas": + self.recorder = FigureRecorder(self) + else: + self.recorder = None + + @property + def toolbar(self): + """ipywidget or QToolbar instance""" + return self._output.toolbar + + @property + def output(self): + """ipywidget or QWidget that contains this plot""" + return self._output @property def shape(self) -> tuple[int, int]: @@ -336,12 +355,12 @@ def shape(self) -> tuple[int, int]: @property def canvas(self) -> WgpuCanvasBase: - """The canvas associated to this GridPlot""" + """The canvas associated to this Figure""" return self._canvas @property def renderer(self) -> pygfx.WgpuRenderer: - """The renderer associated to this GridPlot""" + """The renderer associated to this Figure""" return self._renderer @property @@ -391,6 +410,114 @@ def render(self): # call post-render animate functions self._call_animate_functions(self._animate_funcs_post) + def start_render(self): + """start render cycle""" + self.canvas.request_draw(self.render) + self.canvas.set_logical_size(*self._starting_size) + + def show( + self, + autoscale: bool = True, + maintain_aspect: bool = None, + toolbar: bool = True, + sidecar: bool = False, + sidecar_kwargs: dict = None, + add_widgets: list = None, + ): + """ + Begins the rendering event loop and shows the plot in the desired output context (jupyter, qt or glfw). + + Parameters + ---------- + autoscale: bool, default ``True`` + autoscale the Scene + + maintain_aspect: bool, default ``True`` + maintain aspect ratio + + toolbar: bool, default ``True`` + show toolbar + + sidecar: bool, default ``True`` + display plot in a ``jupyterlab-sidecar``, only for jupyter output context + + sidecar_kwargs: dict, default ``None`` + kwargs for sidecar instance to display plot + i.e. title, layout + + add_widgets: list of widgets + a list of ipywidgets or QWidget that are vertically stacked below the plot + + Returns + ------- + OutputContext + In jupyter, it will display the plot in the output cell or sidecar + + In Qt, it will display the Plot, toolbar, etc. as stacked widget, use `Plot.widget` to access it. + """ + + # show was already called, return existing output context + if self._output is not None: + return self._output + + self.start_render() + + if sidecar_kwargs is None: + sidecar_kwargs = dict() + + if add_widgets is None: + add_widgets = list() + + # flip y-axis if ImageGraphics are present + for subplot in self: + for g in subplot.graphics: + if isinstance(g, ImageGraphic): + subplot.camera.local.scale_y *= -1 + break + + if autoscale: + for subplot in self: + if maintain_aspect is None: + _maintain_aspect = subplot.camera.maintain_aspect + else: + _maintain_aspect = maintain_aspect + subplot.auto_scale(maintain_aspect=_maintain_aspect, zoom=0.95) + + # used for generating images in docs using nbsphinx + if "NB_SNAPSHOT" in os.environ.keys(): + if os.environ["NB_SNAPSHOT"] == "1": + return self.canvas.snapshot() + + # return the appropriate OutputContext based on the current canvas + if self.canvas.__class__.__name__ == "JupyterWgpuCanvas": + from .output.jupyter_output import ( + JupyterOutputContext, + ) # noqa - inline import + + self._output = JupyterOutputContext( + frame=self, + make_toolbar=toolbar, + use_sidecar=sidecar, + sidecar_kwargs=sidecar_kwargs, + add_widgets=add_widgets, + ) + + elif self.canvas.__class__.__name__ == "QWgpuCanvas": + from .output.qt_output import QOutputContext # noqa - inline import + + self._output = QOutputContext( + frame=self, make_toolbar=toolbar, add_widgets=add_widgets + ) + + else: # assume GLFW, the output context is just the canvas + self._output = self.canvas + + # return the output context, this call is required for jupyter but not for Qt + return self._output + + def close(self): + self.output.close() + def _call_animate_functions(self, funcs: list[callable]): for fn in funcs: try: @@ -413,7 +540,7 @@ def add_animations( ): """ Add function(s) that are called on every render cycle. - These are called at the GridPlot level. + These are called at the Figure level. Parameters ---------- @@ -492,3 +619,157 @@ def __repr__(self): f"\t{newline.join(subplot.__str__() for subplot in self)}" f"\n" ) + + +class FigureRecorder: + def __init__(self, figure: Figure): + self._figure = figure + self._video_writer: VideoWriterAV = None + self._video_writer_queue = Queue() + self._record_fps = 25 + self._record_timer = 0 + self._record_start_time = 0 + + def _record(self): + """ + Sends frame to VideoWriter through video writer queue + """ + # current time + t = time() + + # put frame in queue only if enough time as passed according to the desired framerate + # otherwise it tries to record EVERY frame on every rendering cycle, which just blocks the rendering + if t - self._record_timer < (1 / self._record_fps): + return + + # reset timer + self._record_timer = t + + if self._video_writer is not None: + ss = self._figure.canvas.snapshot() + # exclude alpha channel + self._video_writer_queue.put(ss.data[..., :-1]) + + def start( + self, + path: str | Path, + fps: int = 25, + codec: str = "mpeg4", + pixel_format: str = "yuv420p", + options: dict = None, + ): + """ + Start a recording, experimental. Call ``record_end()`` to end a recording. + Note: playback duration does not exactly match recording duration. + + Requires PyAV: https://github.com/PyAV-Org/PyAV + + **Do not resize canvas during a recording, the width and height must remain constant!** + + Parameters + ---------- + path: str or Path + path to save the recording + + fps: int, default ``25`` + framerate, do not use > 25 within jupyter + + codec: str, default "mpeg4" + codec to use, see ``ffmpeg`` list: https://www.ffmpeg.org/ffmpeg-codecs.html . + In general, ``"mpeg4"`` should work on most systems. ``"libx264"`` is a + better option if you have it installed. + + pixel_format: str, default "yuv420p" + pixel format + + options: dict, optional + Codec options. For example, if using ``"mpeg4"`` you can use ``{"q:v": "20"}`` to set the quality between + 1-31, where "1" is highest and "31" is lowest. If using ``"libx264"``` you can use ``{"crf": "30"}`` where + the "crf" value is between "0" (highest quality) and "50" (lowest quality). See ``ffmpeg`` docs for more + info on codec options + + Examples + -------- + + With ``"mpeg4"`` + + .. code-block:: python + + # start recording video + figure.recorder.start("./video.mp4", options={"q:v": "20"} + + # do stuff like interacting with the plot, change things, etc. + + # end recording + figure.recorder.stop() + + With ``"libx264"`` + + .. code-block:: python + + # start recording video + figure.recorder.start("./vid_x264.mp4", codec="libx264", options={"crf": "25"}) + + # do stuff like interacting with the plot, change things, etc. + + # end recording + figure.recorder.stop() + + """ + + if Path(path).exists(): + raise FileExistsError(f"File already exists at given path: {path}") + + # queue for sending frames to VideoWriterAV process + self._video_writer_queue = Queue() + + # snapshot to get canvas width height + ss = self._figure.canvas.snapshot() + + # writer process + self._video_writer = VideoWriterAV( + path=str(path), + queue=self._video_writer_queue, + fps=int(fps), + width=ss.width, + height=ss.height, + codec=codec, + pixel_format=pixel_format, + options=options, + ) + + # start writer process + self._video_writer.start() + + # 1.3 seems to work well to reduce that difference between playback time and recording time + # will properly investigate later + self._record_fps = fps * 1.3 + self._record_start_time = time() + + # record timer used to maintain desired framerate + self._record_timer = time() + + self._figure.add_animations(self._record) + + def stop(self) -> float: + """ + End a current recording. Returns the real duration of the recording + + Returns + ------- + float + recording duration + """ + + # tell video writer that recording has finished + self._video_writer_queue.put(None) + + # wait for writer to finish + self._video_writer.join(timeout=5) + + self._video_writer = None + + # so self._record() is no longer called on every render cycle + self._figure.remove_animation(self._record) + + return time() - self._record_start_time diff --git a/fastplotlib/layouts/_frame/__init__.py b/fastplotlib/layouts/_frame/__init__.py deleted file mode 100644 index c34884022..000000000 --- a/fastplotlib/layouts/_frame/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from ._frame import Frame diff --git a/fastplotlib/layouts/_frame/_frame.py b/fastplotlib/layouts/_frame/_frame.py deleted file mode 100644 index 219a59082..000000000 --- a/fastplotlib/layouts/_frame/_frame.py +++ /dev/null @@ -1,152 +0,0 @@ -import os - -from ...graphics import ImageGraphic -from ._toolbar import ToolBar - - -class Frame: - """ - Mixin class for Plot and GridPlot that "frames" the plot. - - Gives them their `show()` call that returns the appropriate output context. - """ - - def __init__(self): - self._output = None - - @property - def toolbar(self) -> ToolBar: - """ipywidget or QToolbar instance""" - return self._output.toolbar - - @property - def widget(self): - """ipywidget or QWidget that contains this plot""" - # @caitlin: this is the same as the output context, but I figure widget is a simpler public name - return self._output - - def render(self): - """render call implemented in subclass""" - raise NotImplemented - - def _autoscale_init(self, maintain_aspect: bool): - """autoscale function that is called only during show()""" - if hasattr(self, "_subplots"): - for subplot in self: - if maintain_aspect is None: - _maintain_aspect = subplot.camera.maintain_aspect - else: - _maintain_aspect = maintain_aspect - subplot.auto_scale(maintain_aspect=_maintain_aspect, zoom=0.95) - else: - if maintain_aspect is None: - maintain_aspect = self.camera.maintain_aspect - self.auto_scale(maintain_aspect=maintain_aspect, zoom=0.95) - - def start_render(self): - """start render cycle""" - self.canvas.request_draw(self.render) - self.canvas.set_logical_size(*self._starting_size) - - def show( - self, - autoscale: bool = True, - maintain_aspect: bool = None, - toolbar: bool = True, - sidecar: bool = False, - sidecar_kwargs: dict = None, - add_widgets: list = None, - ): - """ - Begins the rendering event loop and shows the plot in the desired output context (jupyter, qt or glfw). - - Parameters - ---------- - autoscale: bool, default ``True`` - autoscale the Scene - - maintain_aspect: bool, default ``True`` - maintain aspect ratio - - toolbar: bool, default ``True`` - show toolbar - - sidecar: bool, default ``True`` - display plot in a ``jupyterlab-sidecar``, only for jupyter output context - - sidecar_kwargs: dict, default ``None`` - kwargs for sidecar instance to display plot - i.e. title, layout - - add_widgets: list of widgets - a list of ipywidgets or QWidget that are vertically stacked below the plot - - Returns - ------- - OutputContext - In jupyter, it will display the plot in the output cell or sidecar - - In Qt, it will display the Plot, toolbar, etc. as stacked widget, use `Plot.widget` to access it. - """ - - # show was already called, return existing output context - if self._output is not None: - return self._output - - self.start_render() - - if sidecar_kwargs is None: - sidecar_kwargs = dict() - - if add_widgets is None: - add_widgets = list() - - # flip y axis if ImageGraphics are present - if hasattr(self, "_subplots"): - for subplot in self: - for g in subplot.graphics: - if isinstance(g, ImageGraphic): - subplot.camera.local.scale_y *= -1 - break - else: - for g in self.graphics: - if isinstance(g, ImageGraphic): - self.camera.local.scale_y *= -1 - break - - if autoscale: - self._autoscale_init(maintain_aspect) - - # used for generating images in docs using nbsphinx - if "NB_SNAPSHOT" in os.environ.keys(): - if os.environ["NB_SNAPSHOT"] == "1": - return self.canvas.snapshot() - - # return the appropriate OutputContext based on the current canvas - if self.canvas.__class__.__name__ == "JupyterWgpuCanvas": - from ._jupyter_output import JupyterOutputContext # noqa - inline import - - self._output = JupyterOutputContext( - frame=self, - make_toolbar=toolbar, - use_sidecar=sidecar, - sidecar_kwargs=sidecar_kwargs, - add_widgets=add_widgets, - ) - - elif self.canvas.__class__.__name__ == "QWgpuCanvas": - from ._qt_output import QOutputContext # noqa - inline import - - self._output = QOutputContext( - frame=self, make_toolbar=toolbar, add_widgets=add_widgets - ) - - else: # assume GLFW, the output context is just the canvas - self._output = self.canvas - - # return the output context, this call is required for jupyter but not for Qt - return self._output - - def close(self): - """Close the output context""" - self._output.close() diff --git a/fastplotlib/layouts/_frame/_qt_toolbar.py b/fastplotlib/layouts/_frame/_qt_toolbar.py deleted file mode 100644 index d62994c2d..000000000 --- a/fastplotlib/layouts/_frame/_qt_toolbar.py +++ /dev/null @@ -1,250 +0,0 @@ -from datetime import datetime -from functools import partial -from math import copysign -import traceback -from typing import * - -from ...utils.gui import QtCore, QtWidgets -from ...graphics.selectors import PolygonSelector -from ._toolbar import ToolBar -from ._qtoolbar_template import Ui_QToolbar - - -class QToolbar( - ToolBar, QtWidgets.QWidget -): # inheritance order MUST be Toolbar first, QWidget second! Else breaks - """Toolbar for Qt context""" - - def __init__(self, output_context, plot): - QtWidgets.QWidget.__init__(self, parent=output_context) - ToolBar.__init__(self, plot) - - # initialize UI - self.ui = Ui_QToolbar() - self.ui.setupUi(self) - - # connect button events - self.ui.auto_scale_button.clicked.connect(self.auto_scale_handler) - self.ui.center_button.clicked.connect(self.center_scene_handler) - self.ui.panzoom_button.toggled.connect(self.panzoom_handler) - self.ui.maintain_aspect_button.toggled.connect(self.maintain_aspect_handler) - self.ui.y_direction_button.clicked.connect(self.y_direction_handler) - - # the subplot labels that update when a user click on subplots - if hasattr(self.plot, "_subplots"): - subplot = self.plot[0, 0] - # set label from first subplot name - if subplot.name is not None: - name = subplot.name - else: - name = str(subplot.position) - - # here we will just use a simple label, not a dropdown like ipywidgets - # the dropdown implementation is tedious with Qt - self.ui.current_subplot = QtWidgets.QLabel(parent=self) - self.ui.current_subplot.setText(name) - self.ui.horizontalLayout.addWidget(self.ui.current_subplot) - - # update the subplot label when a subplot is clicked into - self.plot.renderer.add_event_handler(self.update_current_subplot, "click") - - self.setMaximumHeight(35) - - # set the initial values for buttons - self.ui.maintain_aspect_button.setChecked( - self.current_subplot.camera.maintain_aspect - ) - self.ui.panzoom_button.setChecked(self.current_subplot.controller.enabled) - - if copysign(1, self.current_subplot.camera.local.scale_y) == -1: - self.ui.y_direction_button.setText("v") - else: - self.ui.y_direction_button.setText("^") - - def update_current_subplot(self, ev): - """update the text label for the current subplot""" - for subplot in self.plot: - pos = subplot.map_screen_to_world((ev.x, ev.y)) - if pos is not None: - if subplot.name is not None: - name = subplot.name - else: - name = str(subplot.position) - self.ui.current_subplot.setText(name) - - # set buttons w.r.t. current subplot - self.ui.panzoom_button.setChecked(subplot.controller.enabled) - self.ui.maintain_aspect_button.setChecked( - subplot.camera.maintain_aspect - ) - - if copysign(1, subplot.camera.local.scale_y) == -1: - self.ui.y_direction_button.setText("v") - else: - self.ui.y_direction_button.setText("^") - - def _get_subplot_dropdown_value(self) -> str: - return self.ui.current_subplot.text() - - def auto_scale_handler(self, *args): - self.current_subplot.auto_scale( - maintain_aspect=self.current_subplot.camera.maintain_aspect - ) - - def center_scene_handler(self, *args): - self.current_subplot.center_scene() - - def panzoom_handler(self, value: bool): - self.current_subplot.controller.enabled = value - - def maintain_aspect_handler(self, value: bool): - for camera in self.current_subplot.controller.cameras: - camera.maintain_aspect = value - - def y_direction_handler(self, *args): - # flip every camera under the same controller - for camera in self.current_subplot.controller.cameras: - camera.local.scale_y *= -1 - - if copysign(1, self.current_subplot.camera.local.scale_y) == -1: - self.ui.y_direction_button.setText("v") - else: - self.ui.y_direction_button.setText("^") - - def record_handler(self, ev): - if self.ui.record_button.isChecked(): - try: - self.plot.record_start( - f"./{datetime.now().isoformat(timespec='seconds').replace(':', '_')}.mp4" - ) - except Exception: - traceback.print_exc() - self.ui.record_button.setChecked(False) - else: - self.plot.record_stop() - - def add_polygon(self, *args): - ps = PolygonSelector(edge_width=3, edge_color="mageneta") - self.current_subplot.add_graphic(ps, center=False) - - -# TODO: There must be a better way to do this -# TODO: Check if an interface exists between ipywidgets and Qt -class SliderInterface: - """ - This exists so that ImageWidget has a common interface for Sliders. - - This interface makes a QSlider behave somewhat like a ipywidget IntSlider, enough for ImageWidget to function. - """ - - def __init__(self, qslider): - self.qslider = qslider - - @property - def value(self) -> int: - return self.qslider.value() - - @value.setter - def value(self, value: int): - self.qslider.setValue(value) - - @property - def max(self) -> int: - return self.qslider.maximum() - - @max.setter - def max(self, value: int): - self.qslider.setMaximum(value) - - @property - def min(self): - return self.qslider.minimum() - - @min.setter - def min(self, value: int): - self.qslider.setMinimum(value) - - -class QToolbarImageWidget(QtWidgets.QWidget): - """Toolbar for ImageWidget""" - - def __init__(self, image_widget): - QtWidgets.QWidget.__init__(self) - - # vertical layout - self.vlayout = QtWidgets.QVBoxLayout(self) - - self.image_widget = image_widget - - hlayout_buttons = QtWidgets.QHBoxLayout() - - self.reset_vmin_vmax_button = QtWidgets.QPushButton(self) - self.reset_vmin_vmax_button.setText("auto-contrast") - self.reset_vmin_vmax_button.clicked.connect(self.image_widget.reset_vmin_vmax) - hlayout_buttons.addWidget(self.reset_vmin_vmax_button) - - self.reset_vmin_vmax_hlut_button = QtWidgets.QPushButton(self) - self.reset_vmin_vmax_hlut_button.setText("reset histogram-lut") - self.reset_vmin_vmax_hlut_button.clicked.connect( - self.image_widget.reset_vmin_vmax_frame - ) - hlayout_buttons.addWidget(self.reset_vmin_vmax_hlut_button) - - self.vlayout.addLayout(hlayout_buttons) - - self.sliders: Dict[str, SliderInterface] = dict() - - # has time and/or z-volume - if self.image_widget.ndim > 2: - # create a slider, spinbox and dimension label for each dimension in the ImageWidget - for dim in self.image_widget.slider_dims: - hlayout = ( - QtWidgets.QHBoxLayout() - ) # horizontal stack for label, slider, spinbox - - # max value for current dimension - max_val = self.image_widget._dims_max_bounds[dim] - 1 - - # make slider - slider = QtWidgets.QSlider(self) - slider.setOrientation(QtCore.Qt.Orientation.Horizontal) - slider.setMinimum(0) - slider.setMaximum(max_val) - slider.setValue(0) - slider.setSingleStep(1) - slider.setPageStep(10) - - # make spinbox - spinbox = QtWidgets.QSpinBox(self) - spinbox.setMinimum(0) - spinbox.setMaximum(max_val) - spinbox.setValue(0) - spinbox.setSingleStep(1) - - # link slider and spinbox - slider.valueChanged.connect(spinbox.setValue) - spinbox.valueChanged.connect(slider.setValue) - - # connect slider to change the index within the dimension - slider.valueChanged.connect( - partial(self.image_widget._slider_value_changed, dim) - ) - - # slider dimension label - slider_label = QtWidgets.QLabel(self) - slider_label.setText(dim) - - # add the widgets to the horizontal layout - hlayout.addWidget(slider_label) - hlayout.addWidget(slider) - hlayout.addWidget(spinbox) - - # add horizontal layout to the vertical layout - self.vlayout.addLayout(hlayout) - - # add to sliders dict for easier access to users - self.sliders[dim] = SliderInterface(slider) - - max_height = 35 + (35 * len(self.sliders.keys())) - - self.setMaximumHeight(max_height) diff --git a/fastplotlib/layouts/_plot.py b/fastplotlib/layouts/_plot.py deleted file mode 100644 index 4656649e6..000000000 --- a/fastplotlib/layouts/_plot.py +++ /dev/null @@ -1,68 +0,0 @@ -from typing import * - -import pygfx -from wgpu.gui import WgpuCanvasBase - -from ._subplot import Subplot -from ._frame import Frame -from ._record_mixin import RecordMixin - - -class Plot(Subplot, Frame, RecordMixin): - def __init__( - self, - canvas: Union[str, WgpuCanvasBase] = None, - renderer: pygfx.WgpuRenderer = None, - camera: Union[str, pygfx.PerspectiveCamera] = "2d", - controller: Union[str, pygfx.Controller] = None, - size: Tuple[int, int] = (500, 300), - **kwargs, - ): - """ - Simple Plot object. - - Parameters - ---------- - canvas: WgpuCanvas, optional - Canvas for drawing - - renderer: pygfx.Renderer, optional - pygfx renderer instance - - camera: str or pygfx.PerspectiveCamera, optional - | One of ``"2d"`` or ``"3d"`` indicating 2D or 3D camera - - controller: str or pygfx.Controller, optional - Usually ``None``, you can pass an existing controller from another - ``Plot`` or ``Subplot`` to synchronize them. - - You can also pass str arguments of valid controller names, see Subplot docstring for valid names - - size: (int, int) - starting size of canvas, default (500, 300) - - kwargs - passed to Subplot, for example ``name`` - - """ - super().__init__( - parent=None, - position=(0, 0), - parent_dims=(1, 1), - canvas=canvas, - renderer=renderer, - camera=camera, - controller=controller, - **kwargs, - ) - RecordMixin.__init__(self) - Frame.__init__(self) - - self._starting_size = size - - def render(self): - """performs a single render of the plot, not for the user""" - super().render() - - self.renderer.flush() - self.canvas.request_draw() diff --git a/fastplotlib/layouts/_plot_area.py b/fastplotlib/layouts/_plot_area.py index 37a25bbcc..bbc5b0e15 100644 --- a/fastplotlib/layouts/_plot_area.py +++ b/fastplotlib/layouts/_plot_area.py @@ -92,7 +92,7 @@ def get_refcounts(self): def __init__( self, - parent: Union["PlotArea", "GridPlot"], + parent: Union["PlotArea", "Figure"], position: tuple[int, int] | str, camera: pygfx.PerspectiveCamera, controller: pygfx.Controller, @@ -103,11 +103,11 @@ def __init__( ): """ Base class for plot creation and management. ``PlotArea`` is not intended to be instantiated by users - but rather to provide functionality for ``subplot`` in ``gridplot`` and single ``plot``. + but rather to provide functionality for ``subplots`` in a user ``Figure`` Parameters ---------- - parent: PlotArea or GridPlot + parent: PlotArea or Figure parent object position: Any @@ -181,7 +181,7 @@ def parent(self): @property def position(self) -> tuple[int, int] | str: - """Position of this plot area within a larger layout (such as GridPlot) if relevant""" + """Position of this plot area within a larger layout (such as a Figure) if relevant""" return self._position @property @@ -265,7 +265,7 @@ def controller(self, new_controller: str | pygfx.Controller): # TODO: monkeypatch until we figure out a better # pygfx plans on refactoring viewports anyways if self.parent is not None: - if self.parent.__class__.__name__ == "GridPlot": + if self.parent.__class__.__name__ == "Figure": for subplot in self.parent: if subplot.camera in cameras_list: new_controller.register_events(subplot.viewport) @@ -312,7 +312,7 @@ def get_rect(self) -> tuple[float, float, float, float]: Returns the viewport rect to define the rectangle occupied by the viewport w.r.t. the Canvas. - If this is a subplot within a GridPlot, it returns the rectangle + If this is a subplot within a Figure, it returns the rectangle for only this subplot w.r.t. the parent canvas. Must return: [x_pos, y_pos, width_viewport, height_viewport] diff --git a/fastplotlib/layouts/_record_mixin.py b/fastplotlib/layouts/_record_mixin.py deleted file mode 100644 index 59a8e92e4..000000000 --- a/fastplotlib/layouts/_record_mixin.py +++ /dev/null @@ -1,241 +0,0 @@ -from pathlib import Path -from multiprocessing import Queue, Process -from time import time - - -def _get_av(): - try: - import av - except ImportError: - raise ModuleNotFoundError( - "Recording to video file requires `av`:\n" - "https://github.com/PyAV-Org/PyAV" - ) from None - else: - return av - - -class VideoWriterAV(Process): - """Video writer, uses PyAV in an external process to write frames to disk""" - - def __init__( - self, - path: Path | str, - queue: Queue, - fps: int, - width: int, - height: int, - codec: str, - pixel_format: str, - options: dict = None, - ): - super().__init__() - self.queue = queue - - av = _get_av() - self.container = av.open(path, mode="w") - - self.stream = self.container.add_stream(codec, rate=fps, options=options) - - # in case libx264, trim last rows and/or column - # because libx264 doesn't like non-even number width or height - if width % 2 != 0: - width -= 1 - if height % 2 != 0: - height -= 1 - - self.stream.width = width - self.stream.height = height - - self.stream.pix_fmt = pixel_format - - def run(self): - av = _get_av() - while True: - if self.queue.empty(): # no frame to write - continue - - frame = self.queue.get() - - # recording has ended - if frame is None: - self.container.close() - break - - frame = av.VideoFrame.from_ndarray( - frame[ - : self.stream.height, : self.stream.width - ], # trim if necessary because of x264 - format="rgb24", - ) - - for packet in self.stream.encode(frame): - self.container.mux(packet) - - # I don't exactly know what this does, copied from pyav example - for packet in self.stream.encode(): - self.container.mux(packet) - - # close file - self.container.close() - - # close process, release resources - self.close() - - -# adds recording functionality to GridPlot and Plot -class RecordMixin: - def __init__(self): - self._video_writer: VideoWriterAV = None - self._video_writer_queue = Queue() - self._record_fps = 25 - self._record_timer = 0 - self._record_start_time = 0 - - def _record(self): - """ - Sends frame to VideoWriter through video writer queue - """ - # current time - t = time() - - # put frame in queue only if enough time as passed according to the desired framerate - # otherwise it tries to record EVERY frame on every rendering cycle, which just blocks the rendering - if t - self._record_timer < (1 / self._record_fps): - return - - # reset timer - self._record_timer = t - - if self._video_writer is not None: - ss = self.canvas.snapshot() - # exclude alpha channel - self._video_writer_queue.put(ss.data[..., :-1]) - - def record_start( - self, - path: str | Path, - fps: int = 25, - codec: str = "mpeg4", - pixel_format: str = "yuv420p", - options: dict = None, - ): - """ - Start a recording, experimental. Call ``record_end()`` to end a recording. - Note: playback duration does not exactly match recording duration. - - Requires PyAV: https://github.com/PyAV-Org/PyAV - - **Do not resize canvas during a recording, the width and height must remain constant!** - - Parameters - ---------- - path: str or Path - path to save the recording - - fps: int, default ``25`` - framerate, do not use > 25 within jupyter - - codec: str, default "mpeg4" - codec to use, see ``ffmpeg`` list: https://www.ffmpeg.org/ffmpeg-codecs.html . - In general, ``"mpeg4"`` should work on most systems. ``"libx264"`` is a - better option if you have it installed. - - pixel_format: str, default "yuv420p" - pixel format - - options: dict, optional - Codec options. For example, if using ``"mpeg4"`` you can use ``{"q:v": "20"}`` to set the quality between - 1-31, where "1" is highest and "31" is lowest. If using ``"libx264"``` you can use ``{"crf": "30"}`` where - the "crf" value is between "0" (highest quality) and "50" (lowest quality). See ``ffmpeg`` docs for more - info on codec options - - Examples - -------- - - With ``"mpeg4"`` - - .. code-block:: python - - # create a plot or gridplot etc - - # start recording video - plot.record_start("./video.mp4", options={"q:v": "20"} - - # do stuff like interacting with the plot, change things, etc. - - # end recording - plot.record_end() - - With ``"libx264"`` - - .. code-block:: python - - # create a plot or gridplot etc - - # start recording video - plot.record_start("./vid_x264.mp4", codec="libx264", options={"crf": "25"}) - - # do stuff like interacting with the plot, change things, etc. - - # end recording - plot.record_end() - - """ - - if Path(path).exists(): - raise FileExistsError(f"File already exists at given path: {path}") - - # queue for sending frames to VideoWriterAV process - self._video_writer_queue = Queue() - - # snapshot to get canvas width height - ss = self.canvas.snapshot() - - # writer process - self._video_writer = VideoWriterAV( - path=str(path), - queue=self._video_writer_queue, - fps=int(fps), - width=ss.width, - height=ss.height, - codec=codec, - pixel_format=pixel_format, - options=options, - ) - - # start writer process - self._video_writer.start() - - # 1.3 seems to work well to reduce that difference between playback time and recording time - # will properly investigate later - self._record_fps = fps * 1.3 - self._record_start_time = time() - - # record timer used to maintain desired framerate - self._record_timer = time() - - self.add_animations(self._record) - - def record_stop(self) -> float: - """ - End a current recording. Returns the real duration of the recording - - Returns - ------- - float - recording duration - """ - - # tell video writer that recording has finished - self._video_writer_queue.put(None) - - # wait for writer to finish - self._video_writer.join(timeout=5) - - self._video_writer = None - - # so self._record() is no longer called on every render cycle - self.remove_animation(self._record) - - return time() - self._record_start_time diff --git a/fastplotlib/layouts/_subplot.py b/fastplotlib/layouts/_subplot.py index 4b1e92c51..a541c9d78 100644 --- a/fastplotlib/layouts/_subplot.py +++ b/fastplotlib/layouts/_subplot.py @@ -15,7 +15,7 @@ class Subplot(PlotArea, GraphicMethodsMixin): def __init__( self, - parent: Union["GridPlot", None] = None, + parent: Union["Figure", None] = None, position: tuple[int, int] = None, parent_dims: tuple[int, int] = None, camera: Literal["2d", "3d"] | pygfx.PerspectiveCamera = "2d", @@ -29,22 +29,22 @@ def __init__( name: str = None, ): """ - General plot object that composes a ``Gridplot``. Each ``Gridplot`` instance will have [n rows, n columns] + General plot object is found within a ``Figure``. Each ``Figure`` instance will have [n rows, n columns] of subplots. .. important:: - ``Subplot`` is not meant to be constructed directly, it only exists as part of a ``GridPlot`` + ``Subplot`` is not meant to be constructed directly, it only exists as part of a ``Figure`` Parameters ---------- - parent: 'GridPlot' | None - parent GridPlot instance + parent: 'Figure' | None + parent Figure instance position: (int, int), optional - corresponds to the [row, column] position of the subplot within a ``Gridplot`` + corresponds to the [row, column] position of the subplot within a ``Figure`` parent_dims: (int, int), optional - dimensions of the parent ``GridPlot`` + dimensions of the parent ``Figure`` camera: str or pygfx.PerspectiveCamera, default '2d' indicates the FOV for the camera, '2d' sets ``fov = 0``, '3d' sets ``fov = 50``. diff --git a/fastplotlib/layouts/_video_writer.py b/fastplotlib/layouts/_video_writer.py new file mode 100644 index 000000000..b7e111b50 --- /dev/null +++ b/fastplotlib/layouts/_video_writer.py @@ -0,0 +1,82 @@ +from pathlib import Path +from multiprocessing import Queue, Process + + +def _get_av(): + try: + import av + except ImportError: + raise ModuleNotFoundError( + "Recording to video file requires `av`:\n" + "https://github.com/PyAV-Org/PyAV" + ) from None + else: + return av + + +class VideoWriterAV(Process): + """Video writer, uses PyAV in an external process to write frames to disk""" + + def __init__( + self, + path: Path | str, + queue: Queue, + fps: int, + width: int, + height: int, + codec: str, + pixel_format: str, + options: dict = None, + ): + super().__init__() + self.queue = queue + + av = _get_av() + self.container = av.open(path, mode="w") + + self.stream = self.container.add_stream(codec, rate=fps, options=options) + + # in case libx264, trim last rows and/or column + # because libx264 doesn't like non-even number width or height + if width % 2 != 0: + width -= 1 + if height % 2 != 0: + height -= 1 + + self.stream.width = width + self.stream.height = height + + self.stream.pix_fmt = pixel_format + + def run(self): + av = _get_av() + while True: + if self.queue.empty(): # no frame to write + continue + + frame = self.queue.get() + + # recording has ended + if frame is None: + self.container.close() + break + + frame = av.VideoFrame.from_ndarray( + frame[ + : self.stream.height, : self.stream.width + ], # trim if necessary because of x264 + format="rgb24", + ) + + for packet in self.stream.encode(frame): + self.container.mux(packet) + + # I don't exactly know what this does, copied from pyav example + for packet in self.stream.encode(): + self.container.mux(packet) + + # close file + self.container.close() + + # close process, release resources + self.close() diff --git a/fastplotlib/layouts/output/__init__.py b/fastplotlib/layouts/output/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/fastplotlib/layouts/_frame/_ipywidget_toolbar.py b/fastplotlib/layouts/output/_ipywidget_toolbar.py similarity index 54% rename from fastplotlib/layouts/_frame/_ipywidget_toolbar.py rename to fastplotlib/layouts/output/_ipywidget_toolbar.py index 5b42c8eab..787c8d442 100644 --- a/fastplotlib/layouts/_frame/_ipywidget_toolbar.py +++ b/fastplotlib/layouts/output/_ipywidget_toolbar.py @@ -2,20 +2,14 @@ from datetime import datetime from itertools import product from math import copysign -from functools import partial from pathlib import Path from ipywidgets.widgets import ( - IntSlider, - VBox, HBox, ToggleButton, Dropdown, Layout, Button, - BoundedIntText, - Play, - jslink, Image, ) @@ -27,8 +21,8 @@ class IpywidgetToolBar(HBox, ToolBar): """Basic toolbar using ipywidgets""" - def __init__(self, plot): - ToolBar.__init__(self, plot) + def __init__(self, figure): + ToolBar.__init__(self, figure) self._auto_scale_button = Button( value=False, @@ -107,27 +101,26 @@ def __init__(self, plot): ) widgets.append(image) - if hasattr(self.plot, "_subplots"): - positions = list( - product(range(self.plot.shape[0]), range(self.plot.shape[1])) - ) - values = list() - for pos in positions: - if self.plot[pos].name is not None: - values.append(self.plot[pos].name) - else: - values.append(str(pos)) - - self._dropdown = Dropdown( - options=values, - disabled=False, - description="Subplots:", - layout=Layout(width="200px"), - ) + positions = list( + product(range(self.figure.shape[0]), range(self.figure.shape[1])) + ) + values = list() + for pos in positions: + if self.figure[pos].name is not None: + values.append(self.figure[pos].name) + else: + values.append(str(pos)) + + self._dropdown = Dropdown( + options=values, + disabled=False, + description="Subplots:", + layout=Layout(width="200px"), + ) - self.plot.renderer.add_event_handler(self.update_current_subplot, "click") + self.figure.renderer.add_event_handler(self.update_current_subplot, "click") - widgets.append(self._dropdown) + widgets.append(self._dropdown) self._panzoom_controller_button.observe(self.panzoom_handler, "value") self._auto_scale_button.on_click(self.auto_scale_handler) @@ -176,7 +169,7 @@ def y_direction_handler(self, obj): self._y_direction_button.icon = "arrow-up" def update_current_subplot(self, ev): - for subplot in self.plot: + for subplot in self.figure: pos = subplot.map_screen_to_world((ev.x, ev.y)) if pos is not None: # update self.dropdown @@ -195,137 +188,15 @@ def update_current_subplot(self, ev): def record_plot(self, obj): if self._record_button.value: try: - self.plot.record_start( + self.figure.recorder.start( f"./{datetime.now().isoformat(timespec='seconds').replace(':', '_')}.mp4" ) except Exception: traceback.print_exc() self._record_button.value = False else: - self.plot.record_stop() + self.figure.recorder.stop() def add_polygon(self, obj): ps = PolygonSelector(edge_width=3, edge_color="magenta") self.current_subplot.add_graphic(ps, center=False) - - -class IpywidgetImageWidgetToolbar(VBox): - def __init__(self, iw): - """ - Basic toolbar for a ImageWidget instance. - - Parameters - ---------- - plot: - """ - self.iw = iw - - self.reset_vminvmax_button = Button( - value=False, - disabled=False, - icon="adjust", - layout=Layout(width="auto"), - tooltip="reset vmin/vmax", - ) - - self.reset_vminvmax_hlut_button = Button( - value=False, - icon="adjust", - description="reset", - layout=Layout(width="auto"), - tooltip="reset vmin/vmax and reset histogram using current frame", - ) - - self.sliders: dict[str, IntSlider] = dict() - - # only for xy data, no time point slider needed - if self.iw.ndim == 2: - widgets = [self.reset_vminvmax_button] - # for txy, tzxy, etc. data - else: - for dim in self.iw.slider_dims: - slider = IntSlider( - min=0, - max=self.iw._dims_max_bounds[dim] - 1, - step=1, - value=0, - description=f"dimension: {dim}", - orientation="horizontal", - ) - - slider.observe( - partial(self.iw._slider_value_changed, dim), names="value" - ) - - self.sliders[dim] = slider - - self.step_size_setter = BoundedIntText( - value=1, - min=1, - max=self.sliders["t"].max, - step=1, - description="Step Size:", - disabled=False, - description_tooltip="set slider step", - layout=Layout(width="150px"), - ) - self.speed_text = BoundedIntText( - value=100, - min=1, - max=1_000, - step=50, - description="Speed", - disabled=False, - description_tooltip="Playback speed, this is NOT framerate.\nArbitrary units between 1 - 1,000", - layout=Layout(width="150px"), - ) - self.play_button = Play( - value=0, - min=self.sliders["t"].min, - max=self.sliders["t"].max, - step=self.sliders["t"].step, - description="play/pause", - disabled=False, - ) - widgets = [ - self.reset_vminvmax_button, - self.reset_vminvmax_hlut_button, - self.play_button, - self.step_size_setter, - self.speed_text, - ] - - self.play_button.interval = 10 - - self.step_size_setter.observe(self._change_stepsize, "value") - self.speed_text.observe(self._change_framerate, "value") - jslink((self.play_button, "value"), (self.sliders["t"], "value")) - jslink((self.play_button, "max"), (self.sliders["t"], "max")) - - self.reset_vminvmax_button.on_click(self._reset_vminvmax) - self.reset_vminvmax_hlut_button.on_click(self._reset_vminvmax_frame) - - self.iw.gridplot.renderer.add_event_handler(self._set_slider_layout, "resize") - - # the buttons - self.hbox = HBox(widgets) - - super().__init__((self.hbox, *list(self.sliders.values()))) - - def _reset_vminvmax(self, obj): - self.iw.reset_vmin_vmax() - - def _reset_vminvmax_frame(self, obj): - self.iw.reset_vmin_vmax_frame() - - def _change_stepsize(self, obj): - self.sliders["t"].step = self.step_size_setter.value - - def _change_framerate(self, change): - interval = int(1000 / change["new"]) - self.play_button.interval = interval - - def _set_slider_layout(self, *args): - w, h = self.iw.gridplot.renderer.logical_size - for k, v in self.sliders.items(): - v.layout = Layout(width=f"{w}px") diff --git a/fastplotlib/layouts/output/_qt_toolbar.py b/fastplotlib/layouts/output/_qt_toolbar.py new file mode 100644 index 000000000..4334f1369 --- /dev/null +++ b/fastplotlib/layouts/output/_qt_toolbar.py @@ -0,0 +1,125 @@ +from datetime import datetime +from math import copysign +import traceback + +from ...utils.gui import QtWidgets +from ...graphics.selectors import PolygonSelector +from ._toolbar import ToolBar +from ._qtoolbar_template import Ui_QToolbar + + +class QToolbar( + ToolBar, QtWidgets.QWidget +): # inheritance order MUST be Toolbar first, QWidget second! Else breaks + """Toolbar for Qt context""" + + def __init__(self, output_context, figure): + QtWidgets.QWidget.__init__(self, parent=output_context) + ToolBar.__init__(self, figure) + + # initialize UI + self.ui = Ui_QToolbar() + self.ui.setupUi(self) + + # connect button events + self.ui.auto_scale_button.clicked.connect(self.auto_scale_handler) + self.ui.center_button.clicked.connect(self.center_scene_handler) + self.ui.panzoom_button.toggled.connect(self.panzoom_handler) + self.ui.maintain_aspect_button.toggled.connect(self.maintain_aspect_handler) + self.ui.y_direction_button.clicked.connect(self.y_direction_handler) + + # subplot labels update when a user click on subplots + subplot = self.figure[0, 0] + # set label from first subplot name + if subplot.name is not None: + name = subplot.name + else: + name = str(subplot.position) + + # here we will just use a simple label, not a dropdown like ipywidgets + # the dropdown implementation is tedious with Qt + self.ui.current_subplot = QtWidgets.QLabel(parent=self) + self.ui.current_subplot.setText(name) + self.ui.horizontalLayout.addWidget(self.ui.current_subplot) + + # update the subplot label when a subplot is clicked into + self.figure.renderer.add_event_handler(self.update_current_subplot, "click") + + self.setMaximumHeight(35) + + # set the initial values for buttons + self.ui.maintain_aspect_button.setChecked( + self.current_subplot.camera.maintain_aspect + ) + self.ui.panzoom_button.setChecked(self.current_subplot.controller.enabled) + + if copysign(1, self.current_subplot.camera.local.scale_y) == -1: + self.ui.y_direction_button.setText("v") + else: + self.ui.y_direction_button.setText("^") + + def update_current_subplot(self, ev): + """update the text label for the current subplot""" + for subplot in self.figure: + pos = subplot.map_screen_to_world((ev.x, ev.y)) + if pos is not None: + if subplot.name is not None: + name = subplot.name + else: + name = str(subplot.position) + self.ui.current_subplot.setText(name) + + # set buttons w.r.t. current subplot + self.ui.panzoom_button.setChecked(subplot.controller.enabled) + self.ui.maintain_aspect_button.setChecked( + subplot.camera.maintain_aspect + ) + + if copysign(1, subplot.camera.local.scale_y) == -1: + self.ui.y_direction_button.setText("v") + else: + self.ui.y_direction_button.setText("^") + + def _get_subplot_dropdown_value(self) -> str: + return self.ui.current_subplot.text() + + def auto_scale_handler(self, *args): + self.current_subplot.auto_scale( + maintain_aspect=self.current_subplot.camera.maintain_aspect + ) + + def center_scene_handler(self, *args): + self.current_subplot.center_scene() + + def panzoom_handler(self, value: bool): + self.current_subplot.controller.enabled = value + + def maintain_aspect_handler(self, value: bool): + for camera in self.current_subplot.controller.cameras: + camera.maintain_aspect = value + + def y_direction_handler(self, *args): + # flip every camera under the same controller + for camera in self.current_subplot.controller.cameras: + camera.local.scale_y *= -1 + + if copysign(1, self.current_subplot.camera.local.scale_y) == -1: + self.ui.y_direction_button.setText("v") + else: + self.ui.y_direction_button.setText("^") + + def record_handler(self, ev): + if self.ui.record_button.isChecked(): + try: + self.figure.record_start( + f"./{datetime.now().isoformat(timespec='seconds').replace(':', '_')}.mp4" + ) + except Exception: + traceback.print_exc() + self.ui.record_button.setChecked(False) + else: + self.figure.record_stop() + + def add_polygon(self, *args): + ps = PolygonSelector(edge_width=3, edge_color="mageneta") + self.current_subplot.add_graphic(ps, center=False) diff --git a/fastplotlib/layouts/_frame/_qtoolbar_template.py b/fastplotlib/layouts/output/_qtoolbar_template.py similarity index 100% rename from fastplotlib/layouts/_frame/_qtoolbar_template.py rename to fastplotlib/layouts/output/_qtoolbar_template.py diff --git a/fastplotlib/layouts/_frame/_toolbar.py b/fastplotlib/layouts/output/_toolbar.py similarity index 82% rename from fastplotlib/layouts/_frame/_toolbar.py rename to fastplotlib/layouts/output/_toolbar.py index 6a0485655..5edd201fa 100644 --- a/fastplotlib/layouts/_frame/_toolbar.py +++ b/fastplotlib/layouts/output/_toolbar.py @@ -2,8 +2,8 @@ class ToolBar: - def __init__(self, plot): - self.plot = plot + def __init__(self, figure): + self.figure = figure def _get_subplot_dropdown_value(self) -> str: raise NotImplemented @@ -11,17 +11,17 @@ def _get_subplot_dropdown_value(self) -> str: @property def current_subplot(self) -> Subplot: """Returns current subplot""" - if hasattr(self.plot, "_subplots"): + if hasattr(self.figure, "_subplots"): # parses dropdown or label value as plot name or position current = self._get_subplot_dropdown_value() if current[0] == "(": # str representation of int tuple to tuple of int current = tuple(int(i) for i in current.strip("()").split(",")) - return self.plot[current] + return self.figure[current] else: - return self.plot[current] + return self.figure[current] else: - return self.plot + return self.figure def panzoom_handler(self, ev): raise NotImplemented diff --git a/fastplotlib/layouts/_frame/_jupyter_output.py b/fastplotlib/layouts/output/jupyter_output.py similarity index 100% rename from fastplotlib/layouts/_frame/_jupyter_output.py rename to fastplotlib/layouts/output/jupyter_output.py diff --git a/fastplotlib/layouts/_frame/_qt_output.py b/fastplotlib/layouts/output/qt_output.py similarity index 95% rename from fastplotlib/layouts/_frame/_qt_output.py rename to fastplotlib/layouts/output/qt_output.py index d7e7f2612..20aaef2d1 100644 --- a/fastplotlib/layouts/_frame/_qt_output.py +++ b/fastplotlib/layouts/output/qt_output.py @@ -37,7 +37,7 @@ def __init__( self.vlayout.addWidget(self.frame.canvas) if make_toolbar: # make toolbar and add to layout - self.toolbar = QToolbar(output_context=self, plot=frame) + self.toolbar = QToolbar(output_context=self, figure=frame) self.vlayout.addWidget(self.toolbar) for w in add_widgets: # add any additional widgets to layout diff --git a/fastplotlib/layouts/_frame/qtoolbar.ui b/fastplotlib/layouts/output/qtoolbar.ui similarity index 100% rename from fastplotlib/layouts/_frame/qtoolbar.ui rename to fastplotlib/layouts/output/qtoolbar.ui diff --git a/fastplotlib/utils/functions.py b/fastplotlib/utils/functions.py index da781b521..561863b0c 100644 --- a/fastplotlib/utils/functions.py +++ b/fastplotlib/utils/functions.py @@ -219,7 +219,7 @@ def make_pygfx_colors(colors, n_colors): return colors_array -def calculate_gridshape(n_subplots: int) -> tuple[int, int]: +def calculate_figure_shape(n_subplots: int) -> tuple[int, int]: """ Returns ``(n_rows, n_cols)`` from given number of subplots ``n_subplots`` """ diff --git a/fastplotlib/widgets/_image_widget_ipywidget_toolbar.py b/fastplotlib/widgets/_image_widget_ipywidget_toolbar.py new file mode 100644 index 000000000..24f7a6279 --- /dev/null +++ b/fastplotlib/widgets/_image_widget_ipywidget_toolbar.py @@ -0,0 +1,135 @@ +from functools import partial + +from ipywidgets import ( + VBox, + Button, + Layout, + IntSlider, + BoundedIntText, + Play, + jslink, + HBox, +) + + +class IpywidgetImageWidgetToolbar(VBox): + def __init__(self, iw): + """ + Basic toolbar for a ImageWidget instance. + + Parameters + ---------- + plot: + """ + self.iw = iw + + self.reset_vminvmax_button = Button( + value=False, + disabled=False, + icon="adjust", + layout=Layout(width="auto"), + tooltip="reset vmin/vmax", + ) + + self.reset_vminvmax_hlut_button = Button( + value=False, + icon="adjust", + description="reset", + layout=Layout(width="auto"), + tooltip="reset vmin/vmax and reset histogram using current frame", + ) + + self.sliders: dict[str, IntSlider] = dict() + + # only for xy data, no time point slider needed + if self.iw.ndim == 2: + widgets = [self.reset_vminvmax_button] + # for txy, tzxy, etc. data + else: + for dim in self.iw.slider_dims: + slider = IntSlider( + min=0, + max=self.iw._dims_max_bounds[dim] - 1, + step=1, + value=0, + description=f"dimension: {dim}", + orientation="horizontal", + ) + + slider.observe( + partial(self.iw._slider_value_changed, dim), names="value" + ) + + self.sliders[dim] = slider + + self.step_size_setter = BoundedIntText( + value=1, + min=1, + max=self.sliders["t"].max, + step=1, + description="Step Size:", + disabled=False, + description_tooltip="set slider step", + layout=Layout(width="150px"), + ) + self.speed_text = BoundedIntText( + value=100, + min=1, + max=1_000, + step=50, + description="Speed", + disabled=False, + description_tooltip="Playback speed, this is NOT framerate.\nArbitrary units between 1 - 1,000", + layout=Layout(width="150px"), + ) + self.play_button = Play( + value=0, + min=self.sliders["t"].min, + max=self.sliders["t"].max, + step=self.sliders["t"].step, + description="play/pause", + disabled=False, + ) + widgets = [ + self.reset_vminvmax_button, + self.reset_vminvmax_hlut_button, + self.play_button, + self.step_size_setter, + self.speed_text, + ] + + self.play_button.interval = 10 + + self.step_size_setter.observe(self._change_stepsize, "value") + self.speed_text.observe(self._change_framerate, "value") + jslink((self.play_button, "value"), (self.sliders["t"], "value")) + jslink((self.play_button, "max"), (self.sliders["t"], "max")) + + self.reset_vminvmax_button.on_click(self._reset_vminvmax) + self.reset_vminvmax_hlut_button.on_click(self._reset_vminvmax_frame) + + self.iw.figure.renderer.add_event_handler(self._set_slider_layout, "resize") + + # the buttons + self.hbox = HBox(widgets) + + super().__init__((self.hbox, *list(self.sliders.values()))) + + def _reset_vminvmax(self, obj): + self.iw.reset_vmin_vmax() + + def _reset_vminvmax_frame(self, obj): + self.iw.reset_vmin_vmax_frame() + + def _change_stepsize(self, obj): + self.sliders["t"].step = self.step_size_setter.value + + def _change_framerate(self, change): + interval = int(1000 / change["new"]) + self.play_button.interval = interval + + def _set_slider_layout(self, *args): + w, h = self.iw.figure.renderer.logical_size + + for k, v in self.sliders.items(): + v.layout = Layout(width=f"{w}px") diff --git a/fastplotlib/widgets/_image_widget_qt_toolbar.py b/fastplotlib/widgets/_image_widget_qt_toolbar.py new file mode 100644 index 000000000..2117f95ab --- /dev/null +++ b/fastplotlib/widgets/_image_widget_qt_toolbar.py @@ -0,0 +1,127 @@ +from functools import partial +from typing import Dict + +from fastplotlib.utils.gui import QtWidgets, QtCore + + +# TODO: There must be a better way to do this +# TODO: Check if an interface exists between ipywidgets and Qt +# TODO: Or we won't need it anyways once we have UI in pygfx +class SliderInterface: + """ + This exists so that ImageWidget has a common interface for Sliders. + + This interface makes a QSlider behave somewhat like a ipywidget IntSlider, enough for ImageWidget to function. + """ + + def __init__(self, qslider): + self.qslider = qslider + + @property + def value(self) -> int: + return self.qslider.value() + + @value.setter + def value(self, value: int): + self.qslider.setValue(value) + + @property + def max(self) -> int: + return self.qslider.maximum() + + @max.setter + def max(self, value: int): + self.qslider.setMaximum(value) + + @property + def min(self): + return self.qslider.minimum() + + @min.setter + def min(self, value: int): + self.qslider.setMinimum(value) + + +class QToolbarImageWidget(QtWidgets.QWidget): + """Toolbar for ImageWidget""" + + def __init__(self, image_widget): + QtWidgets.QWidget.__init__(self) + + # vertical layout + self.vlayout = QtWidgets.QVBoxLayout(self) + + self.image_widget = image_widget + + hlayout_buttons = QtWidgets.QHBoxLayout() + + self.reset_vmin_vmax_button = QtWidgets.QPushButton(self) + self.reset_vmin_vmax_button.setText("auto-contrast") + self.reset_vmin_vmax_button.clicked.connect(self.image_widget.reset_vmin_vmax) + hlayout_buttons.addWidget(self.reset_vmin_vmax_button) + + self.reset_vmin_vmax_hlut_button = QtWidgets.QPushButton(self) + self.reset_vmin_vmax_hlut_button.setText("reset histogram-lut") + self.reset_vmin_vmax_hlut_button.clicked.connect( + self.image_widget.reset_vmin_vmax_frame + ) + hlayout_buttons.addWidget(self.reset_vmin_vmax_hlut_button) + + self.vlayout.addLayout(hlayout_buttons) + + self.sliders: Dict[str, SliderInterface] = dict() + + # has time and/or z-volume + if self.image_widget.ndim > 2: + # create a slider, spinbox and dimension label for each dimension in the ImageWidget + for dim in self.image_widget.slider_dims: + hlayout = ( + QtWidgets.QHBoxLayout() + ) # horizontal stack for label, slider, spinbox + + # max value for current dimension + max_val = self.image_widget._dims_max_bounds[dim] - 1 + + # make slider + slider = QtWidgets.QSlider(self) + slider.setOrientation(QtCore.Qt.Orientation.Horizontal) + slider.setMinimum(0) + slider.setMaximum(max_val) + slider.setValue(0) + slider.setSingleStep(1) + slider.setPageStep(10) + + # make spinbox + spinbox = QtWidgets.QSpinBox(self) + spinbox.setMinimum(0) + spinbox.setMaximum(max_val) + spinbox.setValue(0) + spinbox.setSingleStep(1) + + # link slider and spinbox + slider.valueChanged.connect(spinbox.setValue) + spinbox.valueChanged.connect(slider.setValue) + + # connect slider to change the index within the dimension + slider.valueChanged.connect( + partial(self.image_widget._slider_value_changed, dim) + ) + + # slider dimension label + slider_label = QtWidgets.QLabel(self) + slider_label.setText(dim) + + # add the widgets to the horizontal layout + hlayout.addWidget(slider_label) + hlayout.addWidget(slider) + hlayout.addWidget(spinbox) + + # add horizontal layout to the vertical layout + self.vlayout.addLayout(hlayout) + + # add to sliders dict for easier access to users + self.sliders[dim] = SliderInterface(slider) + + max_height = 35 + (35 * len(self.sliders.keys())) + + self.setMaximumHeight(max_height) diff --git a/fastplotlib/widgets/image.py b/fastplotlib/widgets/image.py index 86671b1fc..2a4dc31b4 100644 --- a/fastplotlib/widgets/image.py +++ b/fastplotlib/widgets/image.py @@ -1,11 +1,11 @@ -from typing import * +from typing import Any, Literal, Callable from warnings import warn import numpy as np -from ..layouts import GridPlot +from ..layouts import Figure from ..graphics import ImageGraphic -from ..utils import calculate_gridshape +from ..utils import calculate_figure_shape from .histogram_lut import HistogramLUT @@ -99,11 +99,11 @@ def __repr__(self): class ImageWidget: @property - def gridplot(self) -> GridPlot: + def figure(self) -> Figure: """ - ``GridPlot`` instance within the `ImageWidget`. + ``Figure`` used by `ImageWidget`. """ - return self._gridplot + return self._figure @property def widget(self): @@ -113,17 +113,17 @@ def widget(self): return self._output @property - def managed_graphics(self) -> List[ImageGraphic]: + def managed_graphics(self) -> list[ImageGraphic]: """List of ``ImageWidget`` managed graphics.""" iw_managed = list() - for subplot in self.gridplot: + for subplot in self.figure: # empty subplots will not have any image widget data if len(subplot.graphics) > 0: iw_managed.append(subplot["image_widget_managed"]) return iw_managed @property - def cmap(self) -> List[str]: + def cmap(self) -> list[str]: cmaps = list() for g in self.managed_graphics: cmaps.append(g.cmap.name) @@ -131,7 +131,7 @@ def cmap(self) -> List[str]: return cmaps @cmap.setter - def cmap(self, names: Union[str, List[str]]): + def cmap(self, names: str | list[str]): if isinstance(names, list): if not all([isinstance(n, str) for n in names]): raise TypeError( @@ -153,7 +153,7 @@ def cmap(self, names: Union[str, List[str]]): g.cmap = names @property - def data(self) -> List[np.ndarray]: + def data(self) -> list[np.ndarray]: """data currently displayed in the widget""" return self._data @@ -163,7 +163,7 @@ def ndim(self) -> int: return self._ndim @property - def n_scrollable_dims(self) -> List[int]: + def n_scrollable_dims(self) -> list[int]: """ list indicating the number of dimenensions that are scrollable for each data array All other dimensions are frame/image data, i.e. [x, y] or [x, y, c] @@ -171,17 +171,17 @@ def n_scrollable_dims(self) -> List[int]: return self._n_scrollable_dims @property - def sliders(self) -> Dict[str, Any]: + def sliders(self) -> dict[str, Any]: """the ipywidget IntSlider or QSlider instances used by the widget for indexing the desired dimensions""" return self._image_widget_toolbar.sliders @property - def slider_dims(self) -> List[str]: + def slider_dims(self) -> list[str]: """the dimensions that the sliders index""" return self._slider_dims @property - def current_index(self) -> Dict[str, int]: + def current_index(self) -> dict[str, int]: """ Get or set the current index @@ -248,7 +248,7 @@ def _get_n_scrollable_dims(self, curr_arr: np.ndarray, rgb: bool) -> list[int]: return n_scrollable_dims @current_index.setter - def current_index(self, index: Dict[str, int]): + def current_index(self, index: dict[str, int]): # ignore if output context has not been created yet if self.widget is None: return @@ -285,15 +285,16 @@ def current_index(self, index: Dict[str, int]): def __init__( self, - data: Union[np.ndarray, List[np.ndarray]], - window_funcs: Union[int, Dict[str, int]] = None, - frame_apply: Union[callable, Dict[int, callable]] = None, - grid_shape: Tuple[int, int] = None, - names: List[str] = None, - grid_plot_kwargs: dict = None, + data: np.ndarray | list[np.ndarray], + window_funcs: dict[str, tuple[Callable, int]] = None, + frame_apply: Callable | dict[int, Callable] = None, + figure_shape: tuple[int, int] = None, + names: list[str] = None, + figure_kwargs: dict = None, histogram_widget: bool = True, rgb: list[bool] = None, - **kwargs, + cmap: str = "plasma", + graphic_kwargs: dict = None, ): """ This widget facilitates high-level navigation through image stacks, which are arrays containing one or more @@ -316,17 +317,17 @@ def __init__( data: Union[np.ndarray, List[np.ndarray] array-like or a list of array-like - window_funcs: Dict[Union[int, str], int] + window_funcs: dict[str, tuple[Callable, int]], i.e. {"t" or "z": (callable, int)} | Apply function(s) with rolling windows along "t" and/or "z" dimensions of the `data` arrays. - | Pass a dict in the form: {dimension: (func, window_size)}, `func` must take a slice of the data array as the - | first argument and must take `axis` as a kwarg. + | Pass a dict in the form: {dimension: (func, window_size)}, `func` must take a slice of the data array as + | the first argument and must take `axis` as a kwarg. | Ex: mean along "t" dimension: {"t": (np.mean, 11)}, if `current_index` of "t" is 50, it will pass frames - | 45 to 55 to `np.mean` with `axis = 0`. - | Ex2: max along z dim: {"z": (np.max, 3)}, passes current, previous and next frame to `np.max` with `axis = 1` + | 45 to 55 to `np.mean` with `axis=0`. + | Ex: max along z dim: {"z": (np.max, 3)}, passes current, previous & next frame to `np.max` with `axis=1` frame_apply: Union[callable, Dict[int, callable]] | Apply function(s) to `data` arrays before to generate final 2D image that is displayed. - | Ex: apply a spatial Gaussian filter + | Ex: apply a spatial gaussian filter | Pass a single function or a dict of functions to apply to each array individually | examples: ``{array_index: to_grayscale}``, ``{0: to_grayscale, 2: threshold_img}`` | "array_index" is the position of the corresponding array in the data list. @@ -334,10 +335,10 @@ def __init__( | this function must be a callable that returns a 2D array | example use case: converting an RGB frame from video to a 2D grayscale frame - grid_shape: Optional[Tuple[int, int]] - manually provide the shape for a gridplot, otherwise a square gridplot is approximated. + figure_shape: Optional[Tuple[int, int]] + manually provide the shape for the Figure, otherwise the number of rows and columns is estimated - grid_plot_kwargs: dict, optional + figure_kwargs: dict, optional passed to `GridPlot` names: Optional[str] @@ -350,8 +351,8 @@ def __init__( Includes a True or False for each ``array`` in the ImageWidget, indicating whether images are displayed as grayscale or RGB(A). - kwargs: Any - passed to fastplotlib.graphics.Image + graphic_kwargs: Any + passed to each ImageGraphic in the ImageWidget figure subplots """ self._names = None @@ -367,17 +368,17 @@ def __init__( if all([_is_arraylike(d) for d in data]): # Grid computations - if grid_shape is None: - grid_shape = calculate_gridshape(len(data)) + if figure_shape is None: + figure_shape = calculate_figure_shape(len(data)) - # verify that user-specified grid shape is large enough for the number of image arrays passed - elif grid_shape[0] * grid_shape[1] < len(data): - grid_shape = calculate_gridshape(len(data)) + # verify that user-specified figure shape is large enough for the number of image arrays passed + elif figure_shape[0] * figure_shape[1] < len(data): + figure_shape = calculate_figure_shape(len(data)) warn( - f"Invalid `grid_shape` passed, setting grid shape to: {grid_shape}" + f"Invalid `figure_shape` passed, setting figure shape to: {figure_shape}" ) - self._data: List[np.ndarray] = data + self._data: list[np.ndarray] = data # Establish number of image dimensions and number of scrollable dimensions for each array if rgb is None: @@ -437,8 +438,7 @@ def __init__( ) else: raise TypeError( - f"`data` must be an array-like type representing an n-dimensional image " - f"or a list of array-like representing a grid of n-dimensional images. " + f"`data` must be an array-like type or a list of array-like." f"You have passed the following type {type(data)}" ) @@ -451,14 +451,14 @@ def __init__( if dim in ALLOWED_SLIDER_DIMS.keys(): self.slider_dims.append(ALLOWED_SLIDER_DIMS[dim]) - self._frame_apply: Dict[int, callable] = dict() + self._frame_apply: dict[int, callable] = dict() if frame_apply is not None: if callable(frame_apply): self._frame_apply = frame_apply elif isinstance(frame_apply, dict): - self._frame_apply: Dict[int, callable] = dict.fromkeys( + self._frame_apply: dict[int, callable] = dict.fromkeys( list(range(len(self.data))) ) @@ -479,15 +479,15 @@ def __init__( ) # current_index stores {dimension_index: slice_index} for every dimension - self._current_index: Dict[str, int] = {sax: 0 for sax in self.slider_dims} + self._current_index: dict[str, int] = {sax: 0 for sax in self.slider_dims} self._window_funcs = None self.window_funcs = window_funcs - self._sliders: Dict[str, Any] = dict() + self._sliders: dict[str, Any] = dict() # get max bound for all data arrays for all slider dimensions and ensure compatibility across slider dims - self._dims_max_bounds: Dict[str, int] = {k: 0 for k in self.slider_dims} + self._dims_max_bounds: dict[str, int] = {k: 0 for k in self.slider_dims} for i, _dim in enumerate(list(self._dims_max_bounds.keys())): for array, partition in zip(self.data, self.n_scrollable_dims): if partition <= i: @@ -500,20 +500,23 @@ def __init__( self._dims_max_bounds[_dim], array.shape[i] ) - grid_plot_kwargs_default = {"controller_ids": "sync"} - if grid_plot_kwargs is None: - grid_plot_kwargs = dict() + figure_kwargs_default = {"controller_ids": "sync"} + if figure_kwargs is None: + figure_kwargs = dict() # update the default kwargs with any user-specified kwargs # user specified kwargs will overwrite the defaults - grid_plot_kwargs_default.update(grid_plot_kwargs) + figure_kwargs_default.update(figure_kwargs) - self._gridplot: GridPlot = GridPlot( - shape=grid_shape, **grid_plot_kwargs_default - ) + if graphic_kwargs is None: + graphic_kwargs = dict() + + graphic_kwargs.update({"cmap": cmap}) + + self._figure: Figure = Figure(shape=figure_shape, **figure_kwargs_default) self._histogram_widget = histogram_widget - for data_ix, (d, subplot) in enumerate(zip(self.data, self.gridplot)): + for data_ix, (d, subplot) in enumerate(zip(self.data, self.figure)): if self._names is not None: name = self._names[data_ix] else: @@ -521,7 +524,7 @@ def __init__( frame = self._process_indices(d, slice_indices=self._current_index) frame = self._process_frame_apply(frame, data_ix) - ig = ImageGraphic(frame, name="image_widget_managed", **kwargs) + ig = ImageGraphic(frame, name="image_widget_managed", **graphic_kwargs) subplot.add_graphic(ig) subplot.name = name subplot.set_title(name) @@ -538,11 +541,11 @@ def __init__( self._image_widget_toolbar = None @property - def frame_apply(self) -> Union[dict, None]: + def frame_apply(self) -> dict | None: return self._frame_apply @frame_apply.setter - def frame_apply(self, frame_apply: Dict[int, callable]): + def frame_apply(self, frame_apply: dict[int, callable]): if frame_apply is None: frame_apply = dict() @@ -551,7 +554,7 @@ def frame_apply(self, frame_apply: Dict[int, callable]): self.current_index = self.current_index @property - def window_funcs(self) -> Dict[str, _WindowFunctions]: + def window_funcs(self) -> dict[str, _WindowFunctions]: """ Get or set the window functions @@ -563,7 +566,7 @@ def window_funcs(self) -> Dict[str, _WindowFunctions]: return self._window_funcs @window_funcs.setter - def window_funcs(self, callable_dict: Dict[str, int]): + def window_funcs(self, callable_dict: dict[str, int]): if callable_dict is None: self._window_funcs = None # force frame to update @@ -617,7 +620,7 @@ def window_funcs(self, callable_dict: Dict[str, int]): self.current_index = self.current_index def _process_indices( - self, array: np.ndarray, slice_indices: Dict[Union[int, str], int] + self, array: np.ndarray, slice_indices: dict[str, int] ) -> np.ndarray: """ Get the 2D array from the given slice indices. If not returning a 2D slice (such as due to window_funcs) @@ -628,8 +631,8 @@ def _process_indices( array: np.ndarray array-like to get a 2D slice from - slice_indices: Dict[int, int] - dict in form of {dimension_index: slice_index} + slice_indices: Dict[str, int] + dict in form of {dimension_index: current_index} For example if an array has shape [1000, 30, 512, 512] corresponding to [t, z, x, y]: To get the 100th timepoint and 3rd z-plane pass: {"t": 100, "z": 3} @@ -738,7 +741,7 @@ def _process_frame_apply(self, array, data_ix) -> np.ndarray: return array - def _slider_value_changed(self, dimension: str, change: Union[dict, int]): + def _slider_value_changed(self, dimension: str, change: dict | int): if self.block_sliders: return if isinstance(change, dict): @@ -764,7 +767,7 @@ def reset_vmin_vmax_frame(self): TODO: We could think of applying the frame_apply funcs to a subsample of the entire array to get a better estimate of vmin vmax? """ - for subplot in self.gridplot: + for subplot in self.figure: if "histogram_lut" not in subplot.docks["right"]: continue @@ -774,7 +777,7 @@ def reset_vmin_vmax_frame(self): def set_data( self, - new_data: Union[np.ndarray, List[np.ndarray]], + new_data: np.ndarray | list[np.ndarray], reset_vmin_vmax: bool = True, reset_indices: bool = True, ): @@ -832,7 +835,7 @@ def set_data( # if checks pass, update with new data for i, (new_array, current_array, subplot) in enumerate( - zip(new_data, self._data, self.gridplot) + zip(new_data, self._data, self.figure) ): # check last two dims (x and y) to see if data shape is changing old_data_shape = self._data[i].shape[-self.n_img_dims[i] :] @@ -895,21 +898,17 @@ def show( OutputContext ImageWidget just uses the Gridplot output context """ - if self.gridplot.canvas.__class__.__name__ == "JupyterWgpuCanvas": - from ..layouts._frame._ipywidget_toolbar import ( - IpywidgetImageWidgetToolbar, - ) # noqa - inline import + if self.figure.canvas.__class__.__name__ == "JupyterWgpuCanvas": + from ._image_widget_ipywidget_toolbar import IpywidgetImageWidgetToolbar self._image_widget_toolbar = IpywidgetImageWidgetToolbar(self) - elif self.gridplot.canvas.__class__.__name__ == "QWgpuCanvas": - from ..layouts._frame._qt_toolbar import ( - QToolbarImageWidget, - ) # noqa - inline import + elif self.figure.canvas.__class__.__name__ == "QWgpuCanvas": + from ._image_widget_qt_toolbar import QToolbarImageWidget self._image_widget_toolbar = QToolbarImageWidget(self) - self._output = self.gridplot.show( + self._output = self.figure.show( toolbar=toolbar, sidecar=sidecar, sidecar_kwargs=sidecar_kwargs, @@ -920,4 +919,4 @@ def show( def close(self): """Close Widget""" - self.gridplot.close() + self.figure.close() diff --git a/tests/test_figure.py b/tests/test_figure.py new file mode 100644 index 000000000..27b74c0b6 --- /dev/null +++ b/tests/test_figure.py @@ -0,0 +1,164 @@ +import numpy as np +import pytest + +import fastplotlib as fpl +import pygfx + + +def test_cameras_controller_properties(): + cameras = [ + ["2d", "3d", "3d"], + ["3d", "3d", "3d"] + ] + + controller_types = [ + ["panzoom", "panzoom", "fly"], + ["orbit", "trackball", "panzoom"] + ] + + fig = fpl.Figure( + shape=(2, 3), + cameras=cameras, + controller_types=controller_types, + canvas="offscreen" + ) + + print(fig.canvas) + + subplot_cameras = [subplot.camera for subplot in fig] + subplot_controllers = [subplot.controller for subplot in fig] + + for c1, c2 in zip(subplot_cameras, fig.cameras.ravel()): + assert c1 is c2 + + for c1, c2 in zip(subplot_controllers, fig.controllers.ravel()): + assert c1 is c2 + + for camera_type, subplot_camera in zip(np.asarray(cameras).ravel(), fig.cameras.ravel()): + if camera_type == "2d": + assert subplot_camera.fov == 0 + else: + assert subplot_camera.fov == 50 + + for controller_type, subplot_controller in zip(np.asarray(controller_types).ravel(), fig.controllers.ravel()): + match controller_type: + case "panzoom": + assert isinstance(subplot_controller, pygfx.PanZoomController) + case "fly": + assert isinstance(subplot_controller, pygfx.FlyController) + case "orbit": + assert isinstance(subplot_controller, pygfx.OrbitController) + case "trackball": + assert isinstance(subplot_controller, pygfx.TrackballController) + + # check changing cameras + fig[0, 0].camera = "3d" + assert fig[0, 0].camera.fov == 50 + fig[1, 0].camera = "2d" + assert fig[1, 0].camera.fov == 0 + + # test changing controller + fig[1, 1].controller = "fly" + assert isinstance(fig[1, 1].controller, pygfx.FlyController) + assert fig[1, 1].controller is fig.controllers[1, 1] + fig[0, 2].controller = "panzoom" + assert isinstance(fig[0, 2].controller, pygfx.PanZoomController) + assert fig[0, 2].controller is fig.controllers[0, 2] + + +def test_controller_ids_int(): + ids = [ + [0, 1, 1], + [0, 2, 3], + [4, 1, 2] + ] + + fig = fpl.Figure(shape=(3, 3), controller_ids=ids, canvas="offscreen") + + assert fig[0, 0].controller is fig[1, 0].controller + assert fig[0, 1].controller is fig[0, 2].controller is fig[2, 1].controller + assert fig[1, 1].controller is fig[2, 2].controller + + +def test_controller_ids_int_change_controllers(): + ids = [ + [0, 1, 1], + [0, 2, 3], + [4, 1, 2] + ] + + cameras = [ + ["2d", "3d", "3d"], + ["2d", "3d", "2d"], + ["3d", "3d", "3d"] + ] + + fig = fpl.Figure(shape=(3, 3), cameras=cameras, controller_ids=ids, canvas="offscreen") + + assert isinstance(fig[0, 1].controller, pygfx.FlyController) + + # changing controller when id matches should change the others too + fig[0, 1].controller = "panzoom" + assert isinstance(fig[0, 1].controller, pygfx.PanZoomController) + assert fig[0, 1].controller is fig[0, 2].controller is fig[2, 1].controller + assert set(fig[0, 1].controller.cameras) == {fig[0, 1].camera, fig[0, 2].camera, fig[2, 1].camera} + + # change to orbit + fig[0, 1].controller = "orbit" + assert isinstance(fig[0, 1].controller, pygfx.OrbitController) + assert fig[0, 1].controller is fig[0, 2].controller is fig[2, 1].controller + assert set(fig[0, 1].controller.cameras) == {fig[0, 1].camera, fig[0, 2].camera, fig[2, 1].camera} + + +def test_controller_ids_str(): + names = [ + ["a", "b", "c"], + ["d", "e", "f"] + ] + + controller_ids = [ + ["a", "f"], + ["b", "d", "e"] + ] + + fig = fpl.Figure(shape=(2, 3), controller_ids=controller_ids, names=names, canvas="offscreen") + + assert fig[0, 0].controller is fig[1, 2].controller is fig["a"].controller is fig["f"].controller + assert fig[0, 1].controller is fig[1, 0].controller is fig[1, 1].controller is fig["b"].controller is fig["d"].controller is fig["e"].controller + + # make sure subplot c is unique + exclude_c = [fig[n].controller for n in ["a", "b", "d", "e", "f"]] + assert fig["c"] not in exclude_c + + +def test_set_controllers_from_existing_controllers(): + fig = fpl.Figure(shape=(3, 3), canvas="offscreen") + fig2 = fpl.Figure(shape=fig.shape, controllers=fig.controllers, canvas="offscreen") + + assert fig.controllers[:-1].size == 6 + with pytest.raises(ValueError): + fig3 = fpl.Figure(shape=fig.shape, controllers=fig.controllers[:-1], canvas="offscreen") + + for fig1_subplot, fig2_subplot in zip(fig, fig2): + assert fig1_subplot.controller is fig2_subplot.controller + + cameras = [ + [pygfx.PerspectiveCamera(), "3d"], + ["3d", "2d"] + ] + + controllers = [ + [pygfx.FlyController(cameras[0][0]), pygfx.TrackballController()], + [pygfx.OrbitController(), pygfx.PanZoomController()] + ] + + fig = fpl.Figure(shape=(2, 2), cameras=cameras, controllers=controllers, canvas="offscreen") + + assert fig[0, 0].controller is controllers[0][0] + assert fig[0, 1].controller is controllers[0][1] + assert fig[1, 0].controller is controllers[1][0] + assert fig[1, 1].controller is controllers[1][1] + + assert fig[0, 0].camera is cameras[0][0] + + assert fig[0, 1].camera.fov == 50 diff --git a/tests/test_gridplot.py b/tests/test_gridplot.py deleted file mode 100644 index 3814664d7..000000000 --- a/tests/test_gridplot.py +++ /dev/null @@ -1,164 +0,0 @@ -import numpy as np -import pytest - -import fastplotlib as fpl -import pygfx - - -def test_cameras_controller_properties(): - cameras = [ - ["2d", "3d", "3d"], - ["3d", "3d", "3d"] - ] - - controller_types = [ - ["panzoom", "panzoom", "fly"], - ["orbit", "trackball", "panzoom"] - ] - - gp = fpl.GridPlot( - shape=(2, 3), - cameras=cameras, - controller_types=controller_types, - canvas="offscreen" - ) - - print(gp.canvas) - - subplot_cameras = [subplot.camera for subplot in gp] - subplot_controllers = [subplot.controller for subplot in gp] - - for c1, c2 in zip(subplot_cameras, gp.cameras.ravel()): - assert c1 is c2 - - for c1, c2 in zip(subplot_controllers, gp.controllers.ravel()): - assert c1 is c2 - - for camera_type, subplot_camera in zip(np.asarray(cameras).ravel(), gp.cameras.ravel()): - if camera_type == "2d": - assert subplot_camera.fov == 0 - else: - assert subplot_camera.fov == 50 - - for controller_type, subplot_controller in zip(np.asarray(controller_types).ravel(), gp.controllers.ravel()): - match controller_type: - case "panzoom": - assert isinstance(subplot_controller, pygfx.PanZoomController) - case "fly": - assert isinstance(subplot_controller, pygfx.FlyController) - case "orbit": - assert isinstance(subplot_controller, pygfx.OrbitController) - case "trackball": - assert isinstance(subplot_controller, pygfx.TrackballController) - - # check changing cameras - gp[0, 0].camera = "3d" - assert gp[0, 0].camera.fov == 50 - gp[1, 0].camera = "2d" - assert gp[1, 0].camera.fov == 0 - - # test changing controller - gp[1, 1].controller = "fly" - assert isinstance(gp[1, 1].controller, pygfx.FlyController) - assert gp[1, 1].controller is gp.controllers[1, 1] - gp[0, 2].controller = "panzoom" - assert isinstance(gp[0, 2].controller, pygfx.PanZoomController) - assert gp[0, 2].controller is gp.controllers[0, 2] - - -def test_gridplot_controller_ids_int(): - ids = [ - [0, 1, 1], - [0, 2, 3], - [4, 1, 2] - ] - - gp = fpl.GridPlot(shape=(3, 3), controller_ids=ids, canvas="offscreen") - - assert gp[0, 0].controller is gp[1, 0].controller - assert gp[0, 1].controller is gp[0, 2].controller is gp[2, 1].controller - assert gp[1, 1].controller is gp[2, 2].controller - - -def test_gridplot_controller_ids_int_change_controllers(): - ids = [ - [0, 1, 1], - [0, 2, 3], - [4, 1, 2] - ] - - cameras = [ - ["2d", "3d", "3d"], - ["2d", "3d", "2d"], - ["3d", "3d", "3d"] - ] - - gp = fpl.GridPlot(shape=(3, 3), cameras=cameras, controller_ids=ids, canvas="offscreen") - - assert isinstance(gp[0, 1].controller, pygfx.FlyController) - - # changing controller when id matches should change the others too - gp[0, 1].controller = "panzoom" - assert isinstance(gp[0, 1].controller, pygfx.PanZoomController) - assert gp[0, 1].controller is gp[0, 2].controller is gp[2, 1].controller - assert set(gp[0, 1].controller.cameras) == {gp[0, 1].camera, gp[0, 2].camera, gp[2, 1].camera} - - # change to orbit - gp[0, 1].controller = "orbit" - assert isinstance(gp[0, 1].controller, pygfx.OrbitController) - assert gp[0, 1].controller is gp[0, 2].controller is gp[2, 1].controller - assert set(gp[0, 1].controller.cameras) == {gp[0, 1].camera, gp[0, 2].camera, gp[2, 1].camera} - - -def test_gridplot_controller_ids_str(): - names = [ - ["a", "b", "c"], - ["d", "e", "f"] - ] - - controller_ids = [ - ["a", "f"], - ["b", "d", "e"] - ] - - gp = fpl.GridPlot(shape=(2, 3), controller_ids=controller_ids, names=names, canvas="offscreen") - - assert gp[0, 0].controller is gp[1, 2].controller is gp["a"].controller is gp["f"].controller - assert gp[0, 1].controller is gp[1, 0].controller is gp[1, 1].controller is gp["b"].controller is gp["d"].controller is gp["e"].controller - - # make sure subplot c is unique - exclude_c = [gp[n].controller for n in ["a", "b", "d", "e", "f"]] - assert gp["c"] not in exclude_c - - -def test_set_gridplot_controllers_from_existing_controllers(): - gp = fpl.GridPlot(shape=(3, 3), canvas="offscreen") - gp2 = fpl.GridPlot(shape=gp.shape, controllers=gp.controllers, canvas="offscreen") - - assert gp.controllers[:-1].size == 6 - with pytest.raises(ValueError): - gp3 = fpl.GridPlot(shape=gp.shape, controllers=gp.controllers[:-1], canvas="offscreen") - - for sp_gp, sp_gp2 in zip(gp, gp2): - assert sp_gp.controller is sp_gp2.controller - - cameras = [ - [pygfx.PerspectiveCamera(), "3d"], - ["3d", "2d"] - ] - - controllers = [ - [pygfx.FlyController(cameras[0][0]), pygfx.TrackballController()], - [pygfx.OrbitController(), pygfx.PanZoomController()] - ] - - gp = fpl.GridPlot(shape=(2, 2), cameras=cameras, controllers=controllers, canvas="offscreen") - - assert gp[0, 0].controller is controllers[0][0] - assert gp[0, 1].controller is controllers[0][1] - assert gp[1, 0].controller is controllers[1][0] - assert gp[1, 1].controller is controllers[1][1] - - assert gp[0, 0].camera is cameras[0][0] - - assert gp[0, 1].camera.fov == 50 From cc6decebe78fb12bddbfbdd25e1878c444c686e8 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Fri, 12 Apr 2024 14:57:04 -0400 Subject: [PATCH 064/185] making picking default for all graphics (#484) --- fastplotlib/graphics/image.py | 12 ++++++++--- fastplotlib/graphics/line.py | 4 +++- fastplotlib/graphics/scatter.py | 4 +++- fastplotlib/graphics/selectors/_linear.py | 6 ++++-- .../graphics/selectors/_linear_region.py | 20 +++++++++++++------ fastplotlib/graphics/selectors/_polygon.py | 8 ++++++-- .../graphics/selectors/_rectangle_region.py | 2 +- fastplotlib/graphics/text.py | 1 + fastplotlib/widgets/histogram_lut.py | 4 ++++ 9 files changed, 45 insertions(+), 16 deletions(-) diff --git a/fastplotlib/graphics/image.py b/fastplotlib/graphics/image.py index 1cad33f22..ce736dab2 100644 --- a/fastplotlib/graphics/image.py +++ b/fastplotlib/graphics/image.py @@ -282,12 +282,15 @@ def __init__( # if data is RGB or RGBA if data.ndim > 2: material = pygfx.ImageBasicMaterial( - clim=(vmin, vmax), map_interpolation=filter + clim=(vmin, vmax), map_interpolation=filter, pick_write=True ) # if data is just 2D without color information, use colormap LUT else: material = pygfx.ImageBasicMaterial( - clim=(vmin, vmax), map=self.cmap(), map_interpolation=filter + clim=(vmin, vmax), + map=self.cmap(), + map_interpolation=filter, + pick_write=True, ) world_object = pygfx.Image(geometry, material) @@ -443,7 +446,10 @@ def __init__( self.cmap = HeatmapCmapFeature(self, cmap) self._material = pygfx.ImageBasicMaterial( - clim=(vmin, vmax), map=self.cmap(), map_interpolation=filter + clim=(vmin, vmax), + map=self.cmap(), + map_interpolation=filter, + pick_write=True, ) for start, stop, chunk in zip(start_ixs, stop_ixs, chunks): diff --git a/fastplotlib/graphics/line.py b/fastplotlib/graphics/line.py index cfb697dff..0371fe59b 100644 --- a/fastplotlib/graphics/line.py +++ b/fastplotlib/graphics/line.py @@ -114,7 +114,9 @@ def __init__( world_object: pygfx.Line = pygfx.Line( # self.data.feature_data because data is a Buffer geometry=pygfx.Geometry(positions=self.data(), colors=self.colors()), - material=material(thickness=self.thickness(), color_mode="vertex"), + material=material( + thickness=self.thickness(), color_mode="vertex", pick_write=True + ), ) self._set_world_object(world_object) diff --git a/fastplotlib/graphics/scatter.py b/fastplotlib/graphics/scatter.py index 2557cd637..8682df3d5 100644 --- a/fastplotlib/graphics/scatter.py +++ b/fastplotlib/graphics/scatter.py @@ -93,7 +93,9 @@ def __init__( pygfx.Geometry( positions=self.data(), sizes=self.sizes(), colors=self.colors() ), - material=pygfx.PointsMaterial(color_mode="vertex", size_mode="vertex"), + material=pygfx.PointsMaterial( + color_mode="vertex", size_mode="vertex", pick_write=True + ), ) self._set_world_object(world_object) diff --git a/fastplotlib/graphics/selectors/_linear.py b/fastplotlib/graphics/selectors/_linear.py index 4b77a6cd9..82e553f0a 100644 --- a/fastplotlib/graphics/selectors/_linear.py +++ b/fastplotlib/graphics/selectors/_linear.py @@ -125,12 +125,14 @@ def __init__( line_inner = pygfx.Line( # self.data.feature_data because data is a Buffer geometry=pygfx.Geometry(positions=line_data), - material=material(thickness=thickness, color=color), + material=material(thickness=thickness, color=color, pick_write=True), ) self.line_outer = pygfx.Line( geometry=pygfx.Geometry(positions=line_data), - material=material(thickness=thickness + 6, color=self.colors_outer), + material=material( + thickness=thickness + 6, color=self.colors_outer, pick_write=True + ), ) line_inner.world.z = self.line_outer.world.z + 1 diff --git a/fastplotlib/graphics/selectors/_linear_region.py b/fastplotlib/graphics/selectors/_linear_region.py index 47191bfb1..09c134800 100644 --- a/fastplotlib/graphics/selectors/_linear_region.py +++ b/fastplotlib/graphics/selectors/_linear_region.py @@ -139,13 +139,13 @@ def __init__( if axis == "x": mesh = pygfx.Mesh( pygfx.box_geometry(1, size, 1), - pygfx.MeshBasicMaterial(color=pygfx.Color(fill_color)), + pygfx.MeshBasicMaterial(color=pygfx.Color(fill_color), pick_write=True), ) elif axis == "y": mesh = pygfx.Mesh( pygfx.box_geometry(size, 1, 1), - pygfx.MeshBasicMaterial(color=pygfx.Color(fill_color)), + pygfx.MeshBasicMaterial(color=pygfx.Color(fill_color), pick_write=True), ) else: raise ValueError("`axis` must be one of 'x' or 'y'") @@ -169,7 +169,9 @@ def __init__( left_line = pygfx.Line( pygfx.Geometry(positions=left_line_data), - pygfx.LineMaterial(thickness=edge_thickness, color=edge_color), + pygfx.LineMaterial( + thickness=edge_thickness, color=edge_color, pick_write=True + ), ) # position data for the right edge line @@ -182,7 +184,9 @@ def __init__( right_line = pygfx.Line( pygfx.Geometry(positions=right_line_data), - pygfx.LineMaterial(thickness=edge_thickness, color=edge_color), + pygfx.LineMaterial( + thickness=edge_thickness, color=edge_color, pick_write=True + ), ) self.edges: Tuple[pygfx.Line, pygfx.Line] = (left_line, right_line) @@ -198,7 +202,9 @@ def __init__( bottom_line = pygfx.Line( pygfx.Geometry(positions=bottom_line_data), - pygfx.LineMaterial(thickness=edge_thickness, color=edge_color), + pygfx.LineMaterial( + thickness=edge_thickness, color=edge_color, pick_write=True + ), ) # position data for the right edge line @@ -211,7 +217,9 @@ def __init__( top_line = pygfx.Line( pygfx.Geometry(positions=top_line_data), - pygfx.LineMaterial(thickness=edge_thickness, color=edge_color), + pygfx.LineMaterial( + thickness=edge_thickness, color=edge_color, pick_write=True + ), ) self.edges: Tuple[pygfx.Line, pygfx.Line] = (bottom_line, top_line) diff --git a/fastplotlib/graphics/selectors/_polygon.py b/fastplotlib/graphics/selectors/_polygon.py index 3d2ee98fd..a4ecd440c 100644 --- a/fastplotlib/graphics/selectors/_polygon.py +++ b/fastplotlib/graphics/selectors/_polygon.py @@ -71,7 +71,9 @@ def _add_segment(self, ev): new_line = pygfx.Line( geometry=pygfx.Geometry(positions=data.astype(np.float32)), material=pygfx.LineMaterial( - thickness=self.edge_width, color=pygfx.Color(self.edge_color) + thickness=self.edge_width, + color=pygfx.Color(self.edge_color), + pick_write=True, ), ) @@ -126,7 +128,9 @@ def _finish_polygon(self, ev): new_line = pygfx.Line( geometry=pygfx.Geometry(positions=data.astype(np.float32)), material=pygfx.LineMaterial( - thickness=self.edge_width, color=pygfx.Color(self.edge_color) + thickness=self.edge_width, + color=pygfx.Color(self.edge_color), + pick_write=True, ), ) diff --git a/fastplotlib/graphics/selectors/_rectangle_region.py b/fastplotlib/graphics/selectors/_rectangle_region.py index 1081a49a9..bc2cad5b1 100644 --- a/fastplotlib/graphics/selectors/_rectangle_region.py +++ b/fastplotlib/graphics/selectors/_rectangle_region.py @@ -214,7 +214,7 @@ def __init__( self.fill = pygfx.Mesh( pygfx.box_geometry(width, height, 1), - pygfx.MeshBasicMaterial(color=pygfx.Color(fill_color)), + pygfx.MeshBasicMaterial(color=pygfx.Color(fill_color), pick_write=True), ) self.fill.position.set(*origin, -2) diff --git a/fastplotlib/graphics/text.py b/fastplotlib/graphics/text.py index a486b1bd2..49b4ac4be 100644 --- a/fastplotlib/graphics/text.py +++ b/fastplotlib/graphics/text.py @@ -70,6 +70,7 @@ def __init__( color=face_color, outline_color=outline_color, outline_thickness=outline_thickness, + pick_write=True, ), ) diff --git a/fastplotlib/widgets/histogram_lut.py b/fastplotlib/widgets/histogram_lut.py index 67af972b8..971bc1a28 100644 --- a/fastplotlib/widgets/histogram_lut.py +++ b/fastplotlib/widgets/histogram_lut.py @@ -80,6 +80,8 @@ def __init__( outline_thickness=1, ) + self._text_vmin.world_object.material.pick_write = False + self._text_vmax = TextGraphic( text=vmax_str, size=16, @@ -89,6 +91,8 @@ def __init__( outline_thickness=1, ) + self._text_vmax.world_object.material.pick_write = False + widget_wo = Group() widget_wo.add( self._histogram_line.world_object, From 504ddcf925bd8d43b8354444c921b014b7399aef Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Fri, 12 Apr 2024 21:50:11 -0400 Subject: [PATCH 065/185] update qt examples (#485) --- examples/qt/{video.py => embed.py} | 27 ++++++++++++--------------- examples/qt/imagewidget.py | 15 +++++++++------ examples/qt/minimal.py | 29 +++++++++-------------------- 3 files changed, 30 insertions(+), 41 deletions(-) rename examples/qt/{video.py => embed.py} (54%) diff --git a/examples/qt/video.py b/examples/qt/embed.py similarity index 54% rename from examples/qt/video.py rename to examples/qt/embed.py index 9fd77a999..a3b156021 100644 --- a/examples/qt/video.py +++ b/examples/qt/embed.py @@ -5,28 +5,22 @@ import fastplotlib as fpl import imageio.v3 as iio -# Qt app MUST be instantiated before creating any fpl objects, or any other Qt objects -app = QtWidgets.QApplication([]) video = iio.imread("imageio:cockatoo.mp4") -# force qt canvas, wgpu will sometimes pick glfw by default even if Qt is present -plot = fpl.Plot(canvas="qt") +# fastplotlib and wgpu will auto-detect if Qt is imported and then use the Qt canvas and output context +fig = fpl.Figure() -plot.add_image(video[0], name="video") -plot.camera.local.scale *= -1 +fig[0, 0].add_image(video[0], name="video") def update_frame(ix): - plot["video"].data = video[ix] - # you can also do plot.graphics[0].data = video[ix] + fig[0, 0]["video"].data = video[ix] + # you can also do fig[0, 0].graphics[0].data = video[ix] -# create a QMainWindow, set the plot canvas as the main widget -# The canvas does not have to be in a QMainWindow and it does -# not have to be the central widget, it will work like any QWidget +# create a QMainWindow main_window = QtWidgets.QMainWindow() -main_window.setCentralWidget(plot.canvas) # Create a QSlider for updating frames slider = QtWidgets.QSlider(QtCore.Qt.Orientation.Horizontal) @@ -44,8 +38,11 @@ def update_frame(ix): dock ) -# calling plot.show() is required to start the rendering loop -plot.show() +# calling fig.show() is required to start the rendering loop +qwidget = fig.show() + +# set the qwidget as the central widget +main_window.setCentralWidget(qwidget) # set window size from width and height of video main_window.resize(video.shape[2], video.shape[1]) @@ -54,4 +51,4 @@ def update_frame(ix): main_window.show() # execute Qt app -app.exec() +fpl.run() diff --git a/examples/qt/imagewidget.py b/examples/qt/imagewidget.py index ab1a055f1..f82d082a0 100644 --- a/examples/qt/imagewidget.py +++ b/examples/qt/imagewidget.py @@ -4,14 +4,13 @@ import numpy as np from PyQt6 import QtWidgets import fastplotlib as fpl +import imageio.v3 as iio -# Qt app MUST be instantiated before creating any fpl objects, or any other Qt objects -app = QtWidgets.QApplication([]) images = np.random.rand(100, 512, 512) -# create image widget, force Qt canvas so it doesn't pick glfw -iw = fpl.ImageWidget(images, grid_plot_kwargs={"canvas": "qt"}) +# fastplotlib and wgpu will auto-detect if Qt is imported and then use the Qt canvas +iw = fpl.ImageWidget(images) iw.show() iw.widget.resize(800, 800) @@ -20,10 +19,14 @@ iw_mult = fpl.ImageWidget( images_list, - grid_plot_kwargs={"canvas": "qt"}, cmap="viridis" ) iw_mult.show() iw_mult.widget.resize(800, 800) -app.exec() +# image widget with rgb data +rgb_video = iio.imread("imageio:cockatoo.mp4") +iw_rgb = fpl.ImageWidget(rgb_video, rgb=[True]) +iw_rgb.show() + +fpl.run() diff --git a/examples/qt/minimal.py b/examples/qt/minimal.py index e4e5f6c2f..0d9009ba7 100644 --- a/examples/qt/minimal.py +++ b/examples/qt/minimal.py @@ -1,35 +1,24 @@ """ Minimal PyQt example that displays an image. Press "r" key to autoscale """ +# import Qt or PySide from PyQt6 import QtWidgets import fastplotlib as fpl import imageio.v3 as iio -# Qt app MUST be instantiated before creating any fpl objects, or any other Qt objects -app = QtWidgets.QApplication([]) - img = iio.imread("imageio:astronaut.png") -# force qt canvas, wgpu will sometimes pick glfw by default even if Qt is present -plot = fpl.Plot(canvas="qt") +# fastplotlib and wgpu will auto-detect if Qt is imported and then use the Qt canvas and Qt output context +fig = fpl.Figure() -plot.add_image(img) -plot.camera.local.scale *= -1 +fig[0, 0].add_image(img) -# must call plot.show() to start rendering loop -plot.show() +# must call fig.show() to start rendering loop and show the QWidget containing the fastplotlib figure +qwidget = fig.show() # set QWidget initial size from image width and height -plot.canvas.resize(*img.shape[:2]) - - -def autoscale(ev): - if ev.key == "r": - plot.auto_scale() - - -# useful if you pan/zoom away from the image -plot.renderer.add_event_handler(autoscale, "key_down") +qwidget.resize(*img.shape[:2]) # execute Qt app -app.exec() +# if this is part of a larger Qt QApplication, you can also call app.exec() where app is the QApplication instance +fpl.run() From 9d22d624f51231c76c5b5875e62e6b53d6ba4e63 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Sat, 13 Apr 2024 02:15:39 -0400 Subject: [PATCH 066/185] Update GOVERNANCE.md --- GOVERNANCE.md | 1 - 1 file changed, 1 deletion(-) diff --git a/GOVERNANCE.md b/GOVERNANCE.md index 3ad450dcb..f57479816 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -34,7 +34,6 @@ The Advisory Committee holds a significant interest in fastplotlib as determined 1. Amol Pasarkar 1. Eric Thomson -1. Guillaume Viejo 1. Andrea Giovannucci 1. John Pearson From bac18773384e54459cd1f3260eefb6bff33dda22 Mon Sep 17 00:00:00 2001 From: kushalkolar Date: Mon, 15 Apr 2024 18:37:03 -0400 Subject: [PATCH 067/185] update gov --- GOVERNANCE.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/GOVERNANCE.md b/GOVERNANCE.md index f57479816..6c538079e 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -30,7 +30,7 @@ Responsibilities: ### Advisory Committee -The Advisory Committee holds a significant interest in fastplotlib as determined solely by the **Maintainers**. The responsibilities of the **Advisory Committee** are to 1) attend a yearly Roadmap meeting, 2) be available for conflict resolution. +The Advisory Committee holds a significant interest in fastplotlib as determined solely by the **Maintainers**. 1. Amol Pasarkar 1. Eric Thomson @@ -108,4 +108,4 @@ Governance decisions, meeting minutes, and voting outcomes are publicly document ### Until February 28, 2025 -During early stages of fastplotlib development, changes to the governance document may be made directly through unanimous approval by the original maintainers, Kushal Kolar & Caitlin Lewis, without contacting the neutral moderator or consulting with the advisory committee. They (Kushal & Caitlin) may also add new members to the advisory committee through unanimous approval. +During early stages of fastplotlib development, changes to the governance document may be made directly through unanimous approval by the original maintainers, Kushal Kolar & Caitlin Lewis. They (Kushal & Caitlin) may also add new members to the advisory committee through unanimous approval. From 41a694c632243eebdb204f4665bef49368c02781 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Mon, 15 Apr 2024 23:14:40 -0400 Subject: [PATCH 068/185] remove __all__ from top level (#490) --- fastplotlib/__init__.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/fastplotlib/__init__.py b/fastplotlib/__init__.py index 8e6341156..7a6290d95 100644 --- a/fastplotlib/__init__.py +++ b/fastplotlib/__init__.py @@ -19,11 +19,3 @@ if len(adapters) < 1: raise IndexError("No WGPU adapters found, fastplotlib will not work.") - - -__all__ = [ - "Figure", - "run", - # "ImageWidget", - "Legend", -] From 1abe5ed27249059976fcef91c623e9b787f7e5b1 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Tue, 16 Apr 2024 19:31:34 -0400 Subject: [PATCH 069/185] Select gpu (#491) * add functions to choose GPU and get adapter info --- docs/source/api/gpu.rst | 5 + docs/source/conf.py | 1 + docs/source/index.rst | 9 +- docs/source/user_guide/gpu.rst | 287 ++++++++++++++++++++++++++++++--- fastplotlib/__init__.py | 8 +- fastplotlib/utils/__init__.py | 1 + fastplotlib/utils/gpu.py | 24 +++ 7 files changed, 304 insertions(+), 31 deletions(-) create mode 100644 docs/source/api/gpu.rst create mode 100644 fastplotlib/utils/gpu.py diff --git a/docs/source/api/gpu.rst b/docs/source/api/gpu.rst new file mode 100644 index 000000000..62ffd5797 --- /dev/null +++ b/docs/source/api/gpu.rst @@ -0,0 +1,5 @@ +fastplotlib.utils +***************** + +.. automodule:: fastplotlib.utils.gpu + :members: diff --git a/docs/source/conf.py b/docs/source/conf.py index a6a9fd1f6..f681a8101 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -53,6 +53,7 @@ "python": ("https://docs.python.org/3", None), "numpy": ("https://numpy.org/doc/stable/", None), "pygfx": ("https://pygfx.readthedocs.io/en/latest", None), + "wgpu": ("https://wgpu-py.readthedocs.io/en/latest", None), } html_theme_options = { diff --git a/docs/source/index.rst b/docs/source/index.rst index 0ceb146e4..0bca839b9 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -1,8 +1,3 @@ -.. fastplotlib documentation master file, created by - sphinx-quickstart on Wed Dec 28 12:46:56 2022. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - Welcome to fastplotlib's documentation! ======================================= @@ -14,7 +9,7 @@ Welcome to fastplotlib's documentation! .. toctree:: :caption: User Guide - :maxdepth: 1 + :maxdepth: 2 GPU Info @@ -29,6 +24,7 @@ Welcome to fastplotlib's documentation! Selectors Widgets Utils + GPU Summary ======= @@ -36,7 +32,6 @@ Summary A fast plotting library built using the `pygfx `_ render engine utilizing `Vulkan `_, `DX12 `_, or `Metal `_ via `WGPU `_, so it is very fast! We also aim to be an expressive plotting library that enables rapid prototyping for large scale explorative scientific visualization. `fastplotlib` will run on any framework that ``pygfx`` runs on, this includes ``glfw``, ``Qt`` and ``jupyter lab`` - Installation ============ diff --git a/docs/source/user_guide/gpu.rst b/docs/source/user_guide/gpu.rst index 18fb81cdf..3f4ff4bb4 100644 --- a/docs/source/user_guide/gpu.rst +++ b/docs/source/user_guide/gpu.rst @@ -1,8 +1,8 @@ -GPU Info -******** +GPU Info and selection +********************** FAQ ---- +=== 1. Do I need a GPU? @@ -10,7 +10,7 @@ Technically no, you can perform limited software rendering on linux using lavapi ``fastplotlib`` is intentionally built for realtime rendering using the latest GPU technologies, so we strongly recommend that you use a GPU. -2. My kernel keeps crashing when I create visualizations. +2. My kernel keeps crashing. This can happen under the following circumstances: @@ -19,24 +19,32 @@ This can happen under the following circumstances: If you aren't able to solve it please post an issue on GitHub. :) +3. Nothing renders or rendering is weird, or I see graphical artifacts. + +- Probably driver issues (see next section). + Drivers -------- +======= See the README: https://github.com/fastplotlib/fastplotlib?tab=readme-ov-file#graphics-drivers If you notice weird graphic artifacts, things not rendering, or other glitches try updating to the latest stable drivers. +GPU Info +======== + +View available adapters +----------------------- -View available GPU ------------------- +You can get a summary of all adapters that are available to ``WGPU`` like this:: -You can view all GPUs that are available to ``WGPU`` like this:: + import fastplotlib as fpl - import wgpu + adapters = fpl.enumerate_adapters() - for adapter in wgpu.gpu.enumerate_adapters(): - print(adapter.summary) + for a in adapters: + print(a.summary) For example, on a Thinkpad AMD laptop with a dedicated nvidia GPU this returns:: @@ -45,17 +53,260 @@ For example, on a Thinkpad AMD laptop with a dedicated nvidia GPU this returns:: llvmpipe (LLVM 15.0.6, 256 bits) (CPU) on Vulkan AMD Radeon Graphics (rembrandt, LLVM 15.0.6, DRM 3.52, 6.4.0-0.deb12.2-amd64) (Unknown) on OpenGL +In jupyter all the available adapters are also listed when ``fastplotlib`` is imported. + +You can get more detailed info on each adapter like this:: + + import pprint + for a in fpl.enumerate_adapters(): + pprint.pprint(a.request_adapter_info()) -GPU currently in use --------------------- +General description of the fields: + * vendor: GPU manufacturer + * device: specific GPU model + * description: GPU driver version + * adapter_type: indicates whether this is a discrete GPU, integrated GPU, or software rendering adapter (CPU) + * backend_type: one of "Vulkan", "Metal", or "D3D12" -If you want to know the GPU that a current plot is using you can check the adapter that the renderer is using:: +For more information on the fields see: https://gpuweb.github.io/gpuweb/#gpuadapterinfo + +Adapter currently in use +------------------------ + +If you want to know the adapter that a figure is using you can check the adapter on the renderer:: # for example if we make a plot - plot = fpl.Plot() - plot.add_image(np.random.rand(100, 100)) - plot.show() + fig = fpl.Figure() + fig[0, 0].add_image(np.random.rand(100, 100)) + fig.show() # GPU that is currently in use by the renderer - print(plot.renderer.device.adapter.summary) + print(fig.renderer.device.adapter.summary) + + +Diagnostic info +--------------- + +After creating a figure you can view WGPU diagnostic info like this:: + + fpl.print_wgpu_report() + + +Example output:: + + ██ system: + + platform: Linux-5.10.0-21-amd64-x86_64-with-glibc2.31 + python_implementation: CPython + python: 3.11.3 + + ██ versions: + + wgpu: 0.15.1 + cffi: 1.15.1 + jupyter_rfb: 0.4.2 + numpy: 1.26.4 + pygfx: 0.2.0 + pylinalg: 0.4.1 + fastplotlib: 0.1.0.a16 + + ██ wgpu_native_info: + + expected_version: 0.19.3.1 + lib_version: 0.19.3.1 + lib_path: ./resources/libwgpu_native-release.so + + ██ object_counts: + + count resource_mem + + Adapter: 1 + BindGroup: 3 + BindGroupLayout: 3 + Buffer: 6 696 + CanvasContext: 1 + CommandBuffer: 0 + CommandEncoder: 0 + ComputePassEncoder: 0 + ComputePipeline: 0 + Device: 1 + PipelineLayout: 0 + QuerySet: 0 + Queue: 1 + RenderBundle: 0 + RenderBundleEncoder: 0 + RenderPassEncoder: 0 + RenderPipeline: 3 + Sampler: 2 + ShaderModule: 3 + Texture: 6 9.60M + TextureView: 6 + + total: 36 9.60M + + ██ wgpu_native_counts: + + count mem backend a k r e el_size + + Adapter: 1 1.98K vulkan: 1 1 3 0 1.98K + BindGroup: 3 1.10K vulkan: 3 3 0 0 368 + BindGroupLayout: 3 960 vulkan: 5 3 2 0 320 + Buffer: 6 1.77K vulkan: 7 6 1 0 296 + CanvasContext: 0 0 0 0 0 0 160 + CommandBuffer: 1 1.25K vulkan: 0 0 0 1 1.25K + ComputePipeline: 0 0 vulkan: 0 0 0 0 288 + Device: 1 11.8K vulkan: 1 1 0 0 11.8K + PipelineLayout: 0 0 vulkan: 3 0 3 0 200 + QuerySet: 0 0 vulkan: 0 0 0 0 80 + Queue: 1 184 vulkan: 1 1 0 0 184 + RenderBundle: 0 0 vulkan: 0 0 0 0 848 + RenderPipeline: 3 1.68K vulkan: 3 3 0 0 560 + Sampler: 2 160 vulkan: 2 2 0 0 80 + ShaderModule: 3 2.40K vulkan: 3 3 0 0 800 + Texture: 6 4.94K vulkan: 7 6 1 0 824 + TextureView: 6 1.48K vulkan: 6 6 1 0 248 + + total: 36 29.7K + + * The a, k, r, e are allocated, kept, released, and error, respectively. + * Reported memory does not include buffer/texture data. + + ██ pygfx_adapter_info: + + vendor: radv + architecture: + device: AMD RADV POLARIS10 (ACO) + description: Mesa 20.3.5 (ACO) + vendor_id: 4.09K + device_id: 26.5K + adapter_type: DiscreteGPU + backend_type: Vulkan + + ██ pygfx_features: + + adapter device + + bgra8unorm-storage: - - + depth32float-stencil8: ✓ - + depth-clip-control: ✓ - + float32-filterable: ✓ ✓ + indirect-first-instance: ✓ - + rg11b10ufloat-renderable: ✓ - + shader-f16: - - + texture-compression-astc: - - + texture-compression-bc: ✓ - + texture-compression-etc2: - - + timestamp-query: ✓ - + MultiDrawIndirect: ✓ - + MultiDrawIndirectCount: ✓ - + PushConstants: ✓ - + TextureAdapterSpecificFormatFeatures: ✓ - + VertexWritableStorage: ✓ - + + ██ pygfx_limits: + + adapter device + + max_bind_groups: 8 8 + max_bind_groups_plus_vertex_buffers: 0 0 + max_bindings_per_bind_group: 1.00K 1.00K + max_buffer_size: 2.14G 2.14G + max_color_attachment_bytes_per_sample: 0 0 + max_color_attachments: 0 0 + max_compute_invocations_per_workgroup: 1.02K 1.02K + max_compute_workgroup_size_x: 1.02K 1.02K + max_compute_workgroup_size_y: 1.02K 1.02K + max_compute_workgroup_size_z: 1.02K 1.02K + max_compute_workgroup_storage_size: 32.7K 32.7K + max_compute_workgroups_per_dimension: 65.5K 65.5K + max_dynamic_storage_buffers_per_pipeline_layout: 8 8 + max_dynamic_uniform_buffers_per_pipeline_layout: 16 16 + max_inter_stage_shader_components: 128 128 + max_inter_stage_shader_variables: 0 0 + max_sampled_textures_per_shader_stage: 8.38M 8.38M + max_samplers_per_shader_stage: 8.38M 8.38M + max_storage_buffer_binding_size: 2.14G 2.14G + max_storage_buffers_per_shader_stage: 8.38M 8.38M + max_storage_textures_per_shader_stage: 8.38M 8.38M + max_texture_array_layers: 2.04K 2.04K + max_texture_dimension1d: 16.3K 16.3K + max_texture_dimension2d: 16.3K 16.3K + max_texture_dimension3d: 2.04K 2.04K + max_uniform_buffer_binding_size: 2.14G 2.14G + max_uniform_buffers_per_shader_stage: 8.38M 8.38M + max_vertex_attributes: 32 32 + max_vertex_buffer_array_stride: 2.04K 2.04K + max_vertex_buffers: 16 16 + min_storage_buffer_offset_alignment: 32 32 + min_uniform_buffer_offset_alignment: 32 32 + + ██ pygfx_caches: + + count hits misses + + full_quad_objects: 1 0 2 + mipmap_pipelines: 0 0 0 + layouts: 1 0 3 + bindings: 1 0 1 + shader_modules: 2 0 2 + pipelines: 2 0 2 + shadow_pipelines: 0 0 0 + + ██ pygfx_resources: + + Texture: 8 + Buffer: 23 + + +Select GPU (adapter) +==================== + +You can select an adapter by passing one of the ``wgpu.GPUAdapter`` instances returned by ``fpl.enumerate_adapters()`` +to ``fpl.select_adapter()``:: + + # get info or summary of all adapters to pick an adapter + print([a.request_adapter_info() for a in fpl.enumerate_adapters()]) + + # example, pick adapter at index 2 + chosen_gpu = fpl.enumerate_adapters()[2] + fpl.select_adapter(chosen_gpu) + +**You must select an adapter before creating a** ``Figure`` **, otherwise the default adapter will be selected. Once a** +``Figure`` **is created the adapter cannot be changed.** + +Note that using this function reduces the portability of your code, because +it's highly specific for your current machine/environment. + +The order of the adapters returned by ``wgpu.gpu.enumerate_adapters()`` is +such that Vulkan adapters go first, then Metal, then D3D12, then OpenGL. +Within each category, the order as provided by the particular backend is +maintained. Note that the same device may be present via multiple backends +(e.g. vulkan/opengl). + +We cannot make guarantees about whether the order of the adapters matches +the order as reported by e.g. ``nvidia-smi``. We have found that on a Linux +multi-gpu cluster, the order does match, but we cannot promise that this is +always the case. If you want to make sure, do some testing by allocating big +buffers and checking memory usage using ``nvidia-smi`` + +Example to allocate and check GPU mem usage:: + + import subprocess + + import wgpu + import torch + + def allocate_gpu_mem_with_wgpu(idx): + a = wgpu.gpu.enumerate_adapters()[idx] + d = a.request_device() + b = d.create_buffer(size=10*2**20, usage=wgpu.BufferUsage.COPY_DST) + return b + + def allocate_gpu_mem_with_torch(idx): + d = torch.device(f"cuda:{idx}") + return torch.ones([2000, 10], dtype=torch.float32, device=d) + + def show_mem_usage(): + print(subprocess.run(["nvidia-smi"])) +See https://github.com/pygfx/wgpu-py/issues/482 for more details. diff --git a/fastplotlib/__init__.py b/fastplotlib/__init__.py index 7a6290d95..c1e4d2f2c 100644 --- a/fastplotlib/__init__.py +++ b/fastplotlib/__init__.py @@ -7,15 +7,11 @@ from .layouts import Figure from .widgets import ImageWidget -from .utils import config - -import wgpu +from .utils import config, enumerate_adapters, select_adapter, print_wgpu_report with open(Path(__file__).parent.joinpath("VERSION"), "r") as f: __version__ = f.read().split("\n")[0] -adapters = [a.summary for a in wgpu.gpu.enumerate_adapters()] - -if len(adapters) < 1: +if len(enumerate_adapters()) < 1: raise IndexError("No WGPU adapters found, fastplotlib will not work.") diff --git a/fastplotlib/utils/__init__.py b/fastplotlib/utils/__init__.py index 6759b2497..e1eef6447 100644 --- a/fastplotlib/utils/__init__.py +++ b/fastplotlib/utils/__init__.py @@ -2,6 +2,7 @@ from .functions import * +from .gpu import enumerate_adapters, select_adapter, print_wgpu_report @dataclass diff --git a/fastplotlib/utils/gpu.py b/fastplotlib/utils/gpu.py new file mode 100644 index 000000000..72d303d23 --- /dev/null +++ b/fastplotlib/utils/gpu.py @@ -0,0 +1,24 @@ +import wgpu +from pygfx.renderers.wgpu import select_adapter as pygfx_select_adapter +from pygfx import print_wgpu_report as pygfx_print_wgpu_report + + +def enumerate_adapters() -> list[wgpu.GPUAdapter]: + return wgpu.gpu.enumerate_adapters() + + +enumerate_adapters.__doc__ = wgpu.gpu.enumerate_adapters.__doc__ + + +def select_adapter(adapter: wgpu.GPUAdapter): + return pygfx_select_adapter(adapter) + + +select_adapter.__doc__ = pygfx_select_adapter.__doc__ + + +def print_wgpu_report(): + return pygfx_print_wgpu_report() + + +print_wgpu_report.__doc__ = pygfx_print_wgpu_report.__doc__ From 08733b952f292628c502436c2b64ed5064b74af7 Mon Sep 17 00:00:00 2001 From: Caitlin Lewis <69729525+clewis7@users.noreply.github.com> Date: Sun, 21 Apr 2024 12:21:55 -0400 Subject: [PATCH 070/185] modify mission statement --- GOVERNANCE.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/GOVERNANCE.md b/GOVERNANCE.md index 6c538079e..27acb1c45 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -8,11 +8,20 @@ The purpose of this document is to formalize the governance process used by the The mission of `fastplotlib` is to leverage new graphics APIs and modern GPU hardware to create fast and interactive scientific visualizations using an expressive and elegant API. +`fastplotlib` aims to provide a library that allows for the following: +- Rapid prototyping and algorithm development +- Realtime analysis and visualization +- Efficient rendering of thousands of objects +- Compatibility with lazy-loading and lazy-compute objects +- Shipping dependent packages as a distributable (ex: PyInstaller) + +Ultimately, `fastplotlib` is, and will always be, a free and open-source project that belongs to the community. Our end goal is to aid in the advancement of science, and as a result, we will guide the project in a direction that best serves our community in achieving this purpose. + ## Leadership Team ### Maintainers -The maintainers are the core developers of fastplotlib and together have a complete understanding of the codebase. They are also known as code-owners. +The maintainers are the core developers of fastplotlib and together have a complete understanding of the codebase. They are also known as code-owners. At any given time, there must be a minimum of two maintainers. The current maintainers are: From b2cf625fd5a335e27a49665fc435207a299a9e56 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Wed, 1 May 2024 02:41:05 -0400 Subject: [PATCH 071/185] Small cleanup (#498) * make TESTING var in nb_test_utils, useful to check if nb is in testing state * rename z_position to z_offset in line_collection, fix bug where manual z_offset was ignored and force to 1.0 * if camera is not orthographic, then adding new graphics to a PlotArea does not stack them along the z-axis * update graphic methods mixin * fix bug when a single controller_type is specified * update simple.ipynb, add line animation examples, add subplots * rename simple.ipynb -> quickstart.ipynb * update README * better error message when enumerate_adapters fails * black --- README.md | 35 +- examples/notebooks/nb_test_utils.py | 21 +- .../{simple.ipynb => quickstart.ipynb} | 690 +++++++++++++++++- fastplotlib/__init__.py | 16 +- fastplotlib/graphics/line_collection.py | 26 +- fastplotlib/layouts/_figure.py | 19 +- fastplotlib/layouts/_graphic_methods_mixin.py | 16 +- fastplotlib/layouts/_plot_area.py | 16 +- 8 files changed, 765 insertions(+), 74 deletions(-) rename examples/notebooks/{simple.ipynb => quickstart.ipynb} (65%) diff --git a/README.md b/README.md index 64e1649e8..5c4eb2d45 100644 --- a/README.md +++ b/README.md @@ -55,21 +55,19 @@ Questions, issues, ideas? Post an [issue](https://github.com/fastplotlib/fastplo # Installation -Install using `pip`. - ### Minimal, use with your own `Qt` or `glfw` applications ```bash pip install fastplotlib ``` -**This does not give you `Qt` or `glfw`, you will have to install one of them yourself depending on your preference**. +**This does not give you `PyQt`/`PySide` or `glfw`, you will have to install your preferred GUI framework separately**. ### Notebook ```bash pip install "fastplotlib[notebook]" ``` -**Recommended: install `simplejpeg` for much faster notebook visualization, this requires you to first install [libjpeg-turbo](https://libjpeg-turbo.org/)** +**Strongly recommended: install `simplejpeg` for much faster notebook visualization, this requires you to first install [libjpeg-turbo](https://libjpeg-turbo.org/)** ```bash pip install simplejpeg @@ -77,9 +75,12 @@ pip install simplejpeg > **Note** > -> `fastplotlib` and `pygfx` are fast evolving projects, the version available through pip might be outdated, you will need to follow the "For developers" instructions below if you want the latest features. You can find the release history on pypi here: https://pypi.org/project/fastplotlib/#history +> `fastplotlib` and `pygfx` are fast evolving projects, the version available through pip might be outdated, you will need to follow the "For developers" instructions below if you want the latest features. You can find the release history here: https://github.com/fastplotlib/fastplotlib/releases ### For developers + +Make sure you have [git-lfs](https://github.com/git-lfs/git-lfs#installing) installed. + ```bash git clone https://github.com/fastplotlib/fastplotlib.git cd fastplotlib @@ -88,16 +89,20 @@ cd fastplotlib pip install -e ".[notebook,docs,tests]" ``` +Se [Contributing](https://github.com/fastplotlib/fastplotlib?tab=readme-ov-file#heart-contributing) for more details on development + # Examples -> **Note** -> -> `fastplotlib` and `pygfx` are fast evolving, you may require the latest `pygfx` and `fastplotlib` from github to use the examples in the main branch. +> **Note:** `fastplotlib` and `pygfx` are fast evolving, you will probably require the latest `pygfx` and `fastplotlib` from github to use the examples in the main branch. + +`fastplotlib` code is identical across notebook (`jupyter`), and desktop use with `Qt`/`PySide` or `glfw`. -Note that `fastplotlib` code is basically identical between desktop and notebook usage. The differences are: +Even if you do not intend to use notebooks with `fastplotlib`, the `quickstart.ipynb` notebook is currently the best way to get familiar with the API: https://github.com/fastplotlib/fastplotlib/tree/main/examples/notebooks/quickstart.ipynb + +The specifics for running `fastplotlib` in different GUI frameworks are: - Running in `glfw` requires a `fastplotlib.run()` call (which is really just a `wgpu` `run()` call) -- To use it in `Qt` you must encapsulate it within a `QApplication`, see `examples/qt` -- Notebooks plots have ipywidget-based toolbars and widgets 😄 +- With `Qt` you can encapsulate it within a `QApplication`, see `examples/qt` +- Notebooks plots have ipywidget-based toolbars and widgets. There are plans to move toward an identical in-canvas toolbar with UI elements across all supported frameworks 😄 ### Desktop examples using `glfw` or `Qt` @@ -120,7 +125,7 @@ Notebook examples are here: https://github.com/fastplotlib/fastplotlib/tree/main/examples/notebooks -**Start with `simple.ipynb`.** +**Start with `quickstart.ipynb`.** Some of the examples require imageio: ``` @@ -135,7 +140,9 @@ Our SciPy 2023 talk walks through numerous demos: https://github.com/fastplotlib You will need a relatively modern GPU (newer integrated GPUs in CPUs are usually fine). Generally if your GPU is from 2017 or later it should be fine. -For more information see: https://wgpu-py.readthedocs.io/en/stable/start.html#platform-requirements +For more detailed information, such as use on cloud computing infrastructure, see: https://wgpu-py.readthedocs.io/en/stable/start.html#platform-requirements + +Some more information on GPUs is here: https://fastplotlib.readthedocs.io/en/latest/user_guide/gpu.html ### Windows: Vulkan drivers should be installed by default on Windows 11, but you will need to install your GPU manufacturer's driver package (Nvidia or AMD). If you have an integrated GPU within your CPU, you might still need to install a driver package too, check your CPU manufacturer's info. @@ -162,7 +169,7 @@ sudo apt install llvm-dev libturbojpeg* libgl1-mesa-dev libgl1-mesa-glx libglapi ``` ### Mac OSX: -WGPU uses Metal instead of Vulkan on Mac. You will need at least Mac OSX 10.13. The OS should come with Metal pre-installed so you should be good to go! +WGPU uses Metal instead of Vulkan on Mac. You will need at least Mac OSX 10.13. The OS should come with Metal pre-installed, so you should be good to go! # :heart: Contributing diff --git a/examples/notebooks/nb_test_utils.py b/examples/notebooks/nb_test_utils.py index cb84a5271..791640fe2 100644 --- a/examples/notebooks/nb_test_utils.py +++ b/examples/notebooks/nb_test_utils.py @@ -21,6 +21,13 @@ # store all the failures to allow the nb to proceed to test other examples FAILURES = list() +if "FASTPLOTLIB_NB_TESTS" not in os.environ.keys(): + TESTING = False + +else: + if os.environ["FASTPLOTLIB_NB_TESTS"] == "1": + TESTING = True + # TODO: consolidate testing functions into one module so we don't have this separate one for notebooks @@ -83,18 +90,8 @@ def normalize_image(img): return img -def _run_tests(): - if "FASTPLOTLIB_NB_TESTS" not in os.environ.keys(): - return False - - if os.environ["FASTPLOTLIB_NB_TESTS"] == "1": - return True - - return False - - def plot_test(name, fig: fpl.Figure): - if not _run_tests(): + if not TESTING: return snapshot = fig.canvas.snapshot() @@ -157,7 +154,7 @@ def get_diffs_rgba(slicer): def notebook_finished(): - if not _run_tests(): + if not TESTING: return if len(FAILURES) > 0: diff --git a/examples/notebooks/simple.ipynb b/examples/notebooks/quickstart.ipynb similarity index 65% rename from examples/notebooks/simple.ipynb rename to examples/notebooks/quickstart.ipynb index 3b42385c8..c40e5c9ff 100644 --- a/examples/notebooks/simple.ipynb +++ b/examples/notebooks/quickstart.ipynb @@ -7,9 +7,9 @@ "tags": [] }, "source": [ - "# Introduction to `fastplotlib`\n", + "# Introduction to `fastplotlib` 🚀\n", "\n", - "This notebook goes through the basic components of the `fastplotlib` API, image, image updates, line plots, and scatter plots. " + "This notebook goes through the basic components of the `fastplotlib` API, image, line, scatter plots, subplots and simple animations" ] }, { @@ -37,6 +37,7 @@ "execution_count": null, "id": "5c50e177-5800-4e19-a4f6-d0e0a082e4cd", "metadata": { + "is_executing": true, "tags": [] }, "outputs": [], @@ -68,7 +69,7 @@ "outputs": [], "source": [ "# this is only for testing, you do not need this to use fastplotlib\n", - "from nb_test_utils import plot_test, notebook_finished" + "from nb_test_utils import plot_test, notebook_finished, TESTING" ] }, { @@ -683,16 +684,16 @@ "xs = np.linspace(-10, 10, 100)\n", "# sine wave\n", "ys = np.sin(xs)\n", - "sine = np.dstack([xs, ys])[0]\n", + "sine = np.column_stack([xs, ys])\n", "\n", "# cosine wave\n", "ys = np.cos(xs) + 5\n", - "cosine = np.dstack([xs, ys])[0]\n", + "cosine = np.column_stack([xs, ys])\n", "\n", "# sinc function\n", "a = 0.5\n", "ys = np.sinc(xs) * 3 + 8\n", - "sinc = np.dstack([xs, ys])[0]" + "sinc = np.column_stack([xs, ys])" ] }, { @@ -1009,6 +1010,104 @@ "fig_lines.close()" ] }, + { + "cell_type": "markdown", + "id": "3ada943c-f02c-419b-b384-3865ecbe25fb", + "metadata": {}, + "source": [ + "# Animation example with lines" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8fb64bed-3b47-43e3-9ef5-f8223005b7d2", + "metadata": {}, + "outputs": [], + "source": [ + "# just another example of animations\n", + "start, stop = 0, 2 * np.pi\n", + "increment = (2 * np.pi) / 50\n", + "\n", + "# make a simple sine wave\n", + "xs = np.linspace(start, stop, 100)\n", + "ys = np.sin(xs)\n", + "\n", + "fig = fpl.Figure()\n", + "fig[0, 0].add_line(ys, name=\"sine\")\n", + "\n", + "fig.show(maintain_aspect=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3002bc55-2612-40c8-a088-c07e138b739a", + "metadata": {}, + "outputs": [], + "source": [ + "# increment along the x-axis on each render loop :D \n", + "def update_line(subplot):\n", + " global increment, start, stop\n", + " xs = np.linspace(start + increment, stop + increment, 100)\n", + " ys = np.sin(xs)\n", + " \n", + " start += increment\n", + " stop += increment\n", + "\n", + " # change only the y-axis values of the line\n", + " subplot[\"sine\"].data[:, 1] = ys\n", + "\n", + "\n", + "fig[0, 0].add_animations(update_line)" + ] + }, + { + "cell_type": "markdown", + "id": "fc8c68af-810a-4564-b97d-020054b57f37", + "metadata": {}, + "source": [ + "You can remove an animation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6321c989-60b2-4c9b-a638-a7ac1a2e4a84", + "metadata": {}, + "outputs": [], + "source": [ + "fig[0, 0].remove_animation(update_line)" + ] + }, + { + "cell_type": "markdown", + "id": "21bb17a8-cfca-4f4b-adc9-614fcffad447", + "metadata": {}, + "source": [ + "And add it back" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7749a085-d853-4859-bf99-f2bbecb50306", + "metadata": {}, + "outputs": [], + "source": [ + "fig[0, 0].add_animations(update_line)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cf02a6c-cb1a-4972-ae96-09e6ba37e9dd", + "metadata": {}, + "outputs": [], + "source": [ + "fig.close()" + ] + }, { "cell_type": "markdown", "id": "2c90862e-2f2a-451f-a468-0cf6b857e87a", @@ -1036,7 +1135,7 @@ "\n", "# use 3D data\n", "# note: you usually mix 3D and 2D graphics on the same plot\n", - "spiral = np.dstack([xs, ys, zs])[0]\n", + "spiral = np.column_stack([xs, ys, zs])\n", "\n", "fig_l3d[0, 0].add_line(data=spiral, thickness=2, cmap='winter')\n", "\n", @@ -1103,6 +1202,17 @@ "fig_l3d[0, 0].controller = \"panzoom\"" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "11577230-8268-4b1d-a384-62d4c7f2483f", + "metadata": {}, + "outputs": [], + "source": [ + "# or an orbit controller\n", + "fig_l3d[0, 0].controller = \"orbit\"" + ] + }, { "cell_type": "code", "execution_count": null, @@ -1116,6 +1226,141 @@ "fig_l3d.close()" ] }, + { + "cell_type": "markdown", + "id": "4221ecae-74dc-464c-addf-f4fe91614a26", + "metadata": {}, + "source": [ + "# A travelling electromagnetic wave :D " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "34139d49-af6e-4dc9-90cc-fbf193a64e7f", + "metadata": {}, + "outputs": [], + "source": [ + "import fastplotlib as fpl\n", + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0716a5c9-cf7b-417c-b809-032f5a217c4d", + "metadata": {}, + "outputs": [], + "source": [ + "fig_em = fpl.Figure(\n", + " cameras=\"3d\", \n", + " controller_types=\"orbit\", \n", + " size=(700, 400)\n", + ")\n", + "\n", + "start, stop = 0, 4 * np.pi\n", + "\n", + "# let's define the x, y and z axes for each with direction of wave propogation along the z-axis\n", + "# electric field in the xz plane travelling along\n", + "zs = np.linspace(start, stop, 200)\n", + "e_ys = np.zeros(200)\n", + "e_xs = np.sin(zs)\n", + "electric = np.column_stack([e_xs, e_ys, zs])\n", + "\n", + "# magnetic field in the yz plane\n", + "zs = np.linspace(start, stop, 200)\n", + "m_ys = np.sin(zs)\n", + "m_xs = np.zeros(200)\n", + "magnetic = np.column_stack([m_xs, m_ys, zs])\n", + "\n", + "# add the lines\n", + "fig_em[0, 0].add_line(electric, colors=\"blue\", thickness=2, name=\"e\")\n", + "fig_em[0, 0].add_line(magnetic, colors=\"red\", thickness=2, name=\"m\")\n", + "\n", + "# draw vector line at every 10th position\n", + "electric_vectors = [np.array([[0, 0, z], [x, 0, z]]) for (x, z) in zip(e_xs[::10], zs[::10])]\n", + "magnetic_vectors = [np.array([[0, 0, z], [0, y, z]]) for (y, z) in zip(m_ys[::10], zs[::10])]\n", + "\n", + "# add as a line collection\n", + "fig_em[0, 0].add_line_collection(electric_vectors, colors=\"blue\", thickness=1.5, name=\"e-vec\", z_offset=0)\n", + "fig_em[0, 0].add_line_collection(magnetic_vectors, colors=\"red\", thickness=1.5, name=\"m-vec\", z_offset=0)\n", + "# note that the z_offset in `add_line_collection` is not data-related\n", + "# it is the z-offset for where to place the *graphic*, by default with Orthographic cameras (i.e. 2D views)\n", + "# it will increment by 1 for each line in the collection, we want to disable this so set z_position=0\n", + "\n", + "# axes are a WIP, just draw a white line along z for now\n", + "z_axis = np.array([[0, 0, 0], [0, 0, stop]])\n", + "fig_em[0, 0].add_line(z_axis, colors=\"w\", thickness=1)\n", + "\n", + "# just a pre-saved camera state\n", + "state = {\n", + " 'position': np.array([-8.0 , 6.0, -2.0]),\n", + " 'rotation': np.array([0.09, 0.9 , 0.2, -0.5]),\n", + " 'scale': np.array([1., 1., 1.]),\n", + " 'reference_up': np.array([0., 1., 0.]),\n", + " 'fov': 50.0,\n", + " 'width': 12,\n", + " 'height': 12,\n", + " 'zoom': 1.35,\n", + " 'maintain_aspect': True,\n", + " 'depth_range': None\n", + "}\n", + "\n", + "\n", + "fig_em[0, 0].camera.set_state(state)\n", + "\n", + "fig_em.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "faa52d55-2631-422d-8836-ec371be728c0", + "metadata": {}, + "outputs": [], + "source": [ + "fig_em[0, 0].camera.zoom = 1.5" + ] + }, + { + "cell_type": "markdown", + "id": "d886c63b-7bcb-40d4-b315-dffff71f82f0", + "metadata": {}, + "source": [ + "## Animation for the EM wave" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7ae54258-0c5a-40cc-9ca0-40b47d25de9a", + "metadata": {}, + "outputs": [], + "source": [ + "increment = np.pi * 4 / 100\n", + "\n", + "# moves the wave one step along the z-axis\n", + "def tick(subplot):\n", + " global increment, start, stop, zs\n", + " new_zs = np.linspace(start, stop, 200)\n", + " new_data = np.sin(new_zs)\n", + "\n", + " # just change the x-axis vals for the electric field\n", + " subplot[\"e\"].data[:, 0] = new_data\n", + " # and y-axis vals for magnetic field\n", + " subplot[\"m\"].data[:, 1] = new_data\n", + "\n", + " # update the vector lines\n", + " for i, (value, z) in enumerate(zip(new_data[::10], zs[::10])):\n", + " subplot[\"e-vec\"].graphics[i].data = np.array([[0, 0, z], [value, 0, z]])\n", + " subplot[\"m-vec\"].graphics[i].data = np.array([[0, 0, z], [0, value, z]])\n", + " \n", + " start += increment\n", + " stop += increment\n", + "\n", + "fig_em[0, 0].add_animations(tick)" + ] + }, { "cell_type": "markdown", "id": "a202b3d0-2a0b-450a-93d4-76d0a1129d1d", @@ -1263,6 +1508,437 @@ "fig_scatter.close()" ] }, + { + "cell_type": "markdown", + "id": "b354b04d", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "## More subplots" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e0797523", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "# Figure of shape 2 x 3 with all controllers synced\n", + "figure_grid = fpl.Figure(shape=(2, 3), controller_ids=\"sync\")\n", + "\n", + "# Make a random image graphic for each subplot\n", + "for subplot in figure_grid:\n", + " # create image data\n", + " data = np.random.rand(512, 512)\n", + " # add an image to the subplot\n", + " subplot.add_image(data, name=\"rand-img\")\n", + "\n", + "# Define a function to update the image graphics with new data\n", + "# add_animations will pass the gridplot to the animation function\n", + "def update_data(f):\n", + " for subplot in f:\n", + " new_data = np.random.rand(512, 512)\n", + " # index the image graphic by name and set the data\n", + " subplot[\"rand-img\"].data = new_data\n", + "\n", + "# add the animation function\n", + "figure_grid.add_animations(update_data)\n", + "\n", + "# show the gridplot\n", + "figure_grid.show()" + ] + }, + { + "cell_type": "markdown", + "id": "0c5b20e5", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "### Slicing GridPlot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a14b7e90", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "# positional indexing\n", + "# row 0 and col 0\n", + "figure_grid[0, 0]" + ] + }, + { + "cell_type": "markdown", + "id": "45f29bed", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "You can get the graphics within a subplot, just like with simple `Plot`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fbe632aa", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "figure_grid[0, 1].graphics" + ] + }, + { + "cell_type": "markdown", + "id": "44ccf745", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "and change their properties" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "85e6bf84", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "figure_grid[0, 1].graphics[0].vmax = 0.5" + ] + }, + { + "cell_type": "markdown", + "id": "fb4155b9", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "more slicing with `GridPlot`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e6c3af07", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "# you can give subplots human-readable string names\n", + "figure_grid[0, 2].name = \"top-right-plot\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8848486b", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "figure_grid[\"top-right-plot\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb7566a5", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "# view its position\n", + "figure_grid[\"top-right-plot\"].position" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a002a426", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "# these are really the same\n", + "figure_grid[\"top-right-plot\"] is figure_grid[0, 2]" + ] + }, + { + "cell_type": "markdown", + "id": "df361421", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "Indexing with subplot name and graphic name" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c9915469", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "figure_grid[\"top-right-plot\"][\"rand-img\"].vmin = 0.5" + ] + }, + { + "cell_type": "markdown", + "id": "219648d3", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "## Figure subplot customization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1dcfe24c", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "# 2 rows and 3 columns\n", + "shape = (2, 3)\n", + "\n", + "# pan-zoom controllers for each view\n", + "# views are synced if they have the\n", + "# same controller ID\n", + "controller_ids = [\n", + " [0, 3, 1], # id each controller with an integer\n", + " [2, 2, 3]\n", + "]\n", + "\n", + "\n", + "# you can give string names for each subplot within the gridplot\n", + "names = [\n", + " [\"subplot0\", \"subplot1\", \"subplot2\"],\n", + " [\"subplot3\", \"subplot4\", \"subplot5\"]\n", + "]\n", + "\n", + "# Create the grid plot\n", + "figure_grid = fpl.Figure(\n", + " shape=shape,\n", + " controller_ids=controller_ids,\n", + " names=names,\n", + ")\n", + "\n", + "\n", + "# Make a random image graphic for each subplot\n", + "for subplot in figure_grid:\n", + " data = np.random.rand(512, 512)\n", + " # create and add an ImageGraphic\n", + " subplot.add_image(data=data, name=\"rand-image\")\n", + "\n", + "\n", + "# Define a function to update the image graphics\n", + "# with new randomly generated data\n", + "def set_random_frame(gp):\n", + " for subplot in gp:\n", + " new_data = np.random.rand(512, 512)\n", + " subplot[\"rand-image\"].data = new_data\n", + "\n", + "# add the animation\n", + "figure_grid.add_animations(set_random_frame)\n", + "figure_grid.show()" + ] + }, + { + "cell_type": "markdown", + "id": "be699284", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "Indexing the gridplot to access subplots" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "212a6e4f", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "# can access subplot by name\n", + "figure_grid[\"subplot0\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b758b240", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "# can access subplot by index\n", + "figure_grid[0, 0]" + ] + }, + { + "cell_type": "markdown", + "id": "868f0de4", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "**subplots also support indexing!**\n", + "\n", + "this can be used to get graphics if they are named" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bc14549d", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "# can access graphic directly via name\n", + "figure_grid[\"subplot0\"][\"rand-image\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "99e3726e", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "figure_grid[\"subplot0\"][\"rand-image\"].vmin = 0.6\n", + "figure_grid[\"subplot0\"][\"rand-image\"].vmax = 0.8" + ] + }, + { + "cell_type": "markdown", + "id": "e3350b37", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "source": [ + "positional indexing also works event if subplots have string names" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3b1986f5", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "figure_grid[1, 0][\"rand-image\"].vim = 0.1\n", + "figure_grid[1, 0][\"rand-image\"].vmax = 0.3" + ] + }, { "cell_type": "code", "execution_count": null, diff --git a/fastplotlib/__init__.py b/fastplotlib/__init__.py index c1e4d2f2c..8b46dcc0b 100644 --- a/fastplotlib/__init__.py +++ b/fastplotlib/__init__.py @@ -1,6 +1,6 @@ from pathlib import Path -from .utils.gui import run +from .utils.gui import run # noqa from .graphics import * from .graphics.selectors import * from .legends import * @@ -14,4 +14,16 @@ __version__ = f.read().split("\n")[0] if len(enumerate_adapters()) < 1: - raise IndexError("No WGPU adapters found, fastplotlib will not work.") + raise IndexError( + f"WGPU could not enumerate any adapters, fastplotlib will not work.\n" + f"This is caused by one of the following:\n" + f"1. You do not have a hardware GPU installed and you do not have " + f"software rendering (ex. lavapipe) installed either\n" + f"2. Your GPU drivers are not installed or something is wrong with your GPU driver installation, " + f"re-installing the latest drivers from your hardware vendor (probably Nvidia or AMD) may help.\n" + f"3. You are missing system libraries that are required for WGPU to access GPU(s), this is " + f"common in cloud computing environments.\n" + f"These two links can help you troubleshoot:\n" + f"https://wgpu-py.readthedocs.io/en/stable/start.html#platform-requirements\n" + f"https://fastplotlib.readthedocs.io/en/latest/user_guide/gpu.html\n" + ) diff --git a/fastplotlib/graphics/line_collection.py b/fastplotlib/graphics/line_collection.py index 1c2e151e8..da74cc54e 100644 --- a/fastplotlib/graphics/line_collection.py +++ b/fastplotlib/graphics/line_collection.py @@ -19,7 +19,7 @@ class LineCollection(GraphicCollection, Interaction): def __init__( self, data: List[np.ndarray], - z_position: Iterable[float] | float = None, + z_offset: Iterable[float | int] | float | int = None, thickness: float | Iterable[float] = 2.0, colors: str | Iterable[str] | np.ndarray | Iterable[np.ndarray] = "w", alpha: float = 1.0, @@ -39,9 +39,9 @@ def __init__( List of line data to plot, each element must be a 1D, 2D, or 3D numpy array if elements are 2D, interpreted as [y_vals, n_lines] - z_position: Iterable of float or float, optional - | if ``float``, single position will be used for all lines - | if ``list`` of ``float``, each value will apply to the individual lines + z_offset: Iterable of float or float, optional + | if ``float`` | ``int``, single offset will be used for all lines + | if ``list`` of ``float`` | ``int``, each value will apply to the individual lines thickness: float or Iterable of float, default 2.0 | if ``float``, single thickness will be used for all lines @@ -90,8 +90,8 @@ def __init__( super().__init__(name) - if not isinstance(z_position, float) and z_position is not None: - if len(data) != len(z_position): + if not isinstance(z_offset, (float, int)) and z_offset is not None: + if len(data) != len(z_offset): raise ValueError( "z_position must be a single float or an iterable with same length as data" ) @@ -178,10 +178,10 @@ def __init__( self._set_world_object(pygfx.Group()) for i, d in enumerate(data): - if isinstance(z_position, list): - _z = z_position[i] + if isinstance(z_offset, list): + _z = z_offset[i] else: - _z = 1.0 + _z = z_offset if isinstance(thickness, list): _s = thickness[i] @@ -478,7 +478,7 @@ class LineStack(LineCollection): def __init__( self, data: List[np.ndarray], - z_position: Iterable[float] | float = None, + z_offset: Iterable[float] | float = None, thickness: float | Iterable[float] = 2.0, colors: str | Iterable[str] | np.ndarray | Iterable[np.ndarray] = "w", alpha: float = 1.0, @@ -500,8 +500,8 @@ def __init__( List of line data to plot, each element must be a 1D, 2D, or 3D numpy array if elements are 2D, interpreted as [y_vals, n_lines] - z_position: Iterable of float or float, optional - | if ``float``, single position will be used for all lines + z_offset: Iterable of float or float, optional + | if ``float``, single offset will be used for all lines | if ``list`` of ``float``, each value will apply to the individual lines thickness: float or Iterable of float, default 2.0 @@ -550,7 +550,7 @@ def __init__( """ super().__init__( data=data, - z_position=z_position, + z_offset=z_offset, thickness=thickness, colors=colors, alpha=alpha, diff --git a/fastplotlib/layouts/_figure.py b/fastplotlib/layouts/_figure.py index 7e83e103a..1c7439613 100644 --- a/fastplotlib/layouts/_figure.py +++ b/fastplotlib/layouts/_figure.py @@ -235,30 +235,23 @@ def __init__( # `create_controller()` will auto-determine controller for each subplot based on defaults controller_types = np.array(["default"] * len(self)).reshape(self.shape) - elif isinstance(controller_types, str): - if controller_types not in valid_controller_types.keys(): - raise ValueError( - f"invalid controller_types argument, you may pass either a single controller type as a str, or an" - f"iterable of controller types from the selection: {valid_controller_types.keys()}" - ) - # valid controller types + if isinstance(controller_types, str): + controller_types = [[controller_types]] + types_flat = list(chain(*controller_types)) # str controller_type or pygfx instances valid_str = list(valid_controller_types.keys()) + ["default"] - valid_instances = tuple(valid_controller_types.values()) # make sure each controller type is valid for controller_type in types_flat: if controller_type is None: continue - if (controller_type not in valid_str) and ( - not isinstance(controller_type, valid_instances) - ): + if controller_type not in valid_str: raise ValueError( - f"You have passed an invalid controller type, valid controller_types arguments are:\n" - f"{valid_str} or instances of {[c.__name__ for c in valid_instances]}" + f"You have passed the invalid `controller_type`: {controller_type}. " + f"Valid `controller_types` arguments are:\n {valid_str}" ) controller_types: np.ndarray[pygfx.Controller] = np.asarray( diff --git a/fastplotlib/layouts/_graphic_methods_mixin.py b/fastplotlib/layouts/_graphic_methods_mixin.py index a7acb5eec..9f82cfed5 100644 --- a/fastplotlib/layouts/_graphic_methods_mixin.py +++ b/fastplotlib/layouts/_graphic_methods_mixin.py @@ -178,7 +178,7 @@ def add_image( def add_line_collection( self, data: List[numpy.ndarray], - z_position: Union[Iterable[float], float] = None, + z_offset: Union[Iterable[float], float] = None, thickness: Union[float, Iterable[float]] = 2.0, colors: Union[str, Iterable[str], numpy.ndarray, Iterable[numpy.ndarray]] = "w", alpha: float = 1.0, @@ -199,8 +199,8 @@ def add_line_collection( List of line data to plot, each element must be a 1D, 2D, or 3D numpy array if elements are 2D, interpreted as [y_vals, n_lines] - z_position: Iterable of float or float, optional - | if ``float``, single position will be used for all lines + z_offset: Iterable of float or float, optional + | if ``float``, single offset will be used for all lines | if ``list`` of ``float``, each value will apply to the individual lines thickness: float or Iterable of float, default 2.0 @@ -251,7 +251,7 @@ def add_line_collection( return self._create_graphic( LineCollection, data, - z_position, + z_offset, thickness, colors, alpha, @@ -348,7 +348,7 @@ def add_line( def add_line_stack( self, data: List[numpy.ndarray], - z_position: Union[Iterable[float], float] = None, + z_offset: Union[Iterable[float], float] = None, thickness: Union[float, Iterable[float]] = 2.0, colors: Union[str, Iterable[str], numpy.ndarray, Iterable[numpy.ndarray]] = "w", alpha: float = 1.0, @@ -371,8 +371,8 @@ def add_line_stack( List of line data to plot, each element must be a 1D, 2D, or 3D numpy array if elements are 2D, interpreted as [y_vals, n_lines] - z_position: Iterable of float or float, optional - | if ``float``, single position will be used for all lines + z_offset: Iterable of float or float, optional + | if ``float``, single offset will be used for all lines | if ``list`` of ``float``, each value will apply to the individual lines thickness: float or Iterable of float, default 2.0 @@ -423,7 +423,7 @@ def add_line_stack( return self._create_graphic( LineStack, data, - z_position, + z_offset, thickness, colors, alpha, diff --git a/fastplotlib/layouts/_plot_area.py b/fastplotlib/layouts/_plot_area.py index bbc5b0e15..6ff07a748 100644 --- a/fastplotlib/layouts/_plot_area.py +++ b/fastplotlib/layouts/_plot_area.py @@ -466,7 +466,10 @@ def add_graphic(self, graphic: Graphic, center: bool = True): self._add_or_insert_graphic(graphic=graphic, center=center, action="add") - graphic.position_z = len(self) + if self.camera.fov == 0: + # for orthographic positions stack objects along the z-axis + # for perspective projections we assume the user wants full 3D control + graphic.position_z = len(self) def insert_graphic( self, @@ -505,10 +508,13 @@ def insert_graphic( graphic=graphic, center=center, action="insert", index=index ) - if z_position is None: - graphic.position_z = index - else: - graphic.position_z = z_position + if self.camera.fov == 0: + # for orthographic positions stack objects along the z-axis + # for perspective projections we assume the user wants full 3D control + if z_position is None: + graphic.position_z = index + else: + graphic.position_z = z_position def _add_or_insert_graphic( self, From b79610cd6dd8508f87e6ff0414236087fbd11c58 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Wed, 1 May 2024 16:12:34 -0400 Subject: [PATCH 072/185] simple scatter animation example in quickstart (#500) --- examples/notebooks/quickstart.ipynb | 47 ++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/examples/notebooks/quickstart.ipynb b/examples/notebooks/quickstart.ipynb index c40e5c9ff..84886ecae 100644 --- a/examples/notebooks/quickstart.ipynb +++ b/examples/notebooks/quickstart.ipynb @@ -1361,6 +1361,16 @@ "fig_em[0, 0].add_animations(tick)" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "774dba2e-f4c1-4c97-a427-c6f447139342", + "metadata": {}, + "outputs": [], + "source": [ + "fig_em.close()" + ] + }, { "cell_type": "markdown", "id": "a202b3d0-2a0b-450a-93d4-76d0a1129d1d", @@ -1410,7 +1420,7 @@ "fig_scatter = fpl.Figure()\n", "subplot_scatter = fig_scatter[0, 0]\n", "# use an alpha value since this will be a lot of points\n", - "scatter_graphic = subplot_scatter.add_scatter(data=cloud, sizes=3, colors=colors, alpha=0.7)\n", + "scatter_graphic = subplot_scatter.add_scatter(data=cloud, sizes=3, colors=colors, alpha=0.6)\n", "\n", "fig_scatter.show()" ] @@ -1434,6 +1444,17 @@ "scatter_graphic.colors[:n_points:2] = \"r\"" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "a5962263-8032-40ad-9981-fa0a649e2643", + "metadata": {}, + "outputs": [], + "source": [ + "# other half of the first cloud's points to purple\n", + "scatter_graphic.colors[1:n_points:2] = \"purple\"" + ] + }, { "cell_type": "code", "execution_count": null, @@ -1497,6 +1518,30 @@ "subplot_scatter.controller = \"fly\"" ] }, + { + "cell_type": "markdown", + "id": "43ae13f1-d59b-4673-b0b3-669542b4c127", + "metadata": {}, + "source": [ + "## Animation\n", + "\n", + "Move the cloud by a small delta on every render cycle" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "50d2e96f-718c-4925-9e81-a92e81134741", + "metadata": {}, + "outputs": [], + "source": [ + "def update_points(subplot):\n", + " deltas = np.random.normal(size=scatter_graphic.data().shape, loc=0, scale=0.15)\n", + " scatter_graphic.data = scatter_graphic.data() + deltas\n", + "\n", + "subplot_scatter.add_animations(update_points)" + ] + }, { "cell_type": "code", "execution_count": null, From 9a309ef1359244eb3791ddcfebc8b08b2697ae9f Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Wed, 1 May 2024 19:30:17 -0400 Subject: [PATCH 073/185] cycle colors in scatter example (#501) --- examples/notebooks/quickstart.ipynb | 34 ++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/examples/notebooks/quickstart.ipynb b/examples/notebooks/quickstart.ipynb index 84886ecae..9bfd822ab 100644 --- a/examples/notebooks/quickstart.ipynb +++ b/examples/notebooks/quickstart.ipynb @@ -1422,7 +1422,7 @@ "# use an alpha value since this will be a lot of points\n", "scatter_graphic = subplot_scatter.add_scatter(data=cloud, sizes=3, colors=colors, alpha=0.6)\n", "\n", - "fig_scatter.show()" + "fig_scatter.show(sidecar=True)" ] }, { @@ -1536,12 +1536,40 @@ "outputs": [], "source": [ "def update_points(subplot):\n", + " # move every point by a small amount\n", " deltas = np.random.normal(size=scatter_graphic.data().shape, loc=0, scale=0.15)\n", - " scatter_graphic.data = scatter_graphic.data() + deltas\n", + " scatter_graphic.data = scatter_graphic.data() + deltas \n", "\n", "subplot_scatter.add_animations(update_points)" ] }, + { + "cell_type": "markdown", + "id": "1592c6cd-d10a-4bda-ac4b-e06d428ffa1d", + "metadata": {}, + "source": [ + "Another animation function to cycle the colors of one of the clouds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb0394a3-47d9-4620-a754-d04d5f313cc7", + "metadata": {}, + "outputs": [], + "source": [ + "i = 0.05\n", + "def cycle_colors(subplot):\n", + " global i\n", + " # cycle the red values\n", + " scatter_graphic.colors[n_points * 2:, 0] = np.abs(np.sin(i))\n", + " scatter_graphic.colors[n_points * 2:, 1] = np.abs(np.sin(i + (np.pi / 4)))\n", + " scatter_graphic.colors[n_points * 2:, 2] = np.abs(np.cos(i))\n", + " i += 0.05\n", + "\n", + "subplot_scatter.add_animations(cycle_colors)" + ] + }, { "cell_type": "code", "execution_count": null, @@ -2020,7 +2048,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.3" + "version": "3.11.2" } }, "nbformat": 4, From 815e8b3ff6aed9fe17104a8c30677bd111958c70 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Sun, 16 Jun 2024 18:14:59 -0400 Subject: [PATCH 074/185] Graphic features refactor (#511) * just a start * pushing to continue on my desktop * progress on buffer manager cleanup_key * more progress, still lots to do * slicing working with PointsDataFeature, negative slices too, still major WIP * comitting stuff * _update_range() is pretty good now * comment * if statement * simply colors setting * type annotation * simpler slice parsing * remove cleanup_key :D :D :D git status! * all fancy and negative indexing working :D * start tests * exception message * more on tests * refactor sizes, not tested yet * start parameterizing buffer tests * better buffer tests * more variants * add array fancy indexing to same parameterization * parameterize tuple tests * remove repr * test offset and size * test offset and size * test create colors * also test with direct truth indices in colors * points tests, works * remove imports * sizes test working, other cleanup * export sizes feature again * ideas for sharing and unsharing buffers between graphics * ideas for sharing and unsharing buffers between graphics, nto tested * typing * attach and detach buffers to a graphic, not tested * import * more int point tests * Graphic.add_event_handler * adding and removing data feature event works and tested * common features, WIP * regular features and refactor line and scatter into positions graphic * uniform sizes * implement sizes and uniform size for scatter * VertexCmap feature, not yet tested * start image features, not tested, add thickness, not tested * better cmap parsing * cleanup * image features * cleanup * start selection feature refactor * more on selection features * offset and rotation for base graphic * feature event table * position feature event tables * rotation and offset features * work on selectors, WIP, linear region selector inits properly and moves for x-axis * proper centering * much simpliified and better linear region selector * linear region selector works well on x axis with events and data selection * vertex cmap fix, delete synchronizer * linear selector works * cleanup * update graphic methods mixin * update selector example nbs, still WIP * type annotation in setter * add notes to tests comments * refactor image stuff * image selector tool * return selectors as proxies * image stuff works * fix offsets adding graphics, fix positions_graphic cmap bug, quickstart runs :D * fix add_graphic args and mixin * simpler graphic collection stuff * more line collection * remove old events system * fix some examples * remove lingering older interaction stuff * cleanup * fill color arg * black * update examples * fix image tiling, better heatmap example * update hm examples * fix hlut * refactor graphics base * update line examples * bug fixes * update text to use new gfeatures * cleanup * implement iterator for TextureArray, much simpler now :D * basic texture tests * bugfix, cleanup * image graphic tests * type annot * test stuff * tests for common and visible kwarg for Graphic * bugfix * test remove event hanlders common fea * rename * start test positions graphics * test progress * black * add __len__ to buffer managers, add __array_interface__ raises error * TextureArray has len() * docstring * updates and tests * black * uniform colors tests and bug fix * black * bugfix uniform color and sizes * sizes and thickness tests * tests update * lotta team work * docstring, small things * rename UniformSizes -> UniformSize * update graphic methods mixin * update graphic methods mixin * bugfix * test data slice for positiosn graphics * test colors property within buffer tests * cleanup * emit user key not parsed key * rename cmap_values to transform * cmap_values -> cmap_transform in mixin * test graphics in vertex data buffer manager tests * cmap transform tests * make Graphic._features private, add Graphic.events property * color events tests * move data slice test to just buffer tests * data events tests * cleanup * cleanup, remove old histogram graphic * text changes and tests * texture array tests with graphic * image graphic tests * update image features * black * append data and world xy for graphic pointer events * black * move constructor to top * example tests for wide and square hm * update ci * fix message * update examples * bugfix, docstrings * docstrings, exception messages * update api docs * apparently we use __all__ in graphics to generate api docs * remove nbsphinx * bump version * docstring * finish up line collection * fix adding line selector to line collection * finish line collection * update line collection examples * graphic collections are now iterables, add examples for setting properties, add names and metadatas args, separate kwargs for collection and individual lines * 3d line stack example with animation * fix line collection cmap with additional args * black * update kwargs for line collection because of mixin * add numpy.integer check for buffermanager parse slice * rename * docstrings * line linear selector init logic * fix line collection init selectors * fix selector * add plot area hook for collections * docstring * update selector nbs * update example nb * update nb * cleanup * update nb * update nbs * feature is private class attr * fix * remove anim example from screenshot tests * update screenshots * update screenshot * update CONTRIBUTING * smaller hm data test for CI * exclude heatmap change data from tests, too large RAM usage probably * black * change dtype to save ram usage for CI * remove large square heatmap from screenshot tests * black * disable all but one hm test * fix gc * update screenshots * replace one more test image * fix docs * update image screenshots * fix docs * docstrings, Graphic.events -> Graphic.supported_events * update docs --------- Co-authored-by: Caitlin --- .github/workflows/black.yml | 16 +- .github/workflows/ci.yml | 1 + CONTRIBUTING.md | 43 +- docs/source/api/gpu.rst | 7 +- .../api/graphic_features/CmapFeature.rst | 36 - .../api/graphic_features/ColorFeature.rst | 34 - docs/source/api/graphic_features/Deleted.rst | 2 + .../api/graphic_features/FeatureEvent.rst | 29 - docs/source/api/graphic_features/FontSize.rst | 35 + .../api/graphic_features/GraphicFeature.rst | 33 - .../GraphicFeatureIndexable.rst | 34 - .../graphic_features/HeatmapCmapFeature.rst | 37 - .../graphic_features/HeatmapDataFeature.rst | 35 - .../source/api/graphic_features/ImageCmap.rst | 35 + .../api/graphic_features/ImageCmapFeature.rst | 37 - .../ImageCmapInterpolation.rst | 35 + .../api/graphic_features/ImageDataFeature.rst | 35 - .../graphic_features/ImageInterpolation.rst | 35 + .../source/api/graphic_features/ImageVmax.rst | 35 + .../source/api/graphic_features/ImageVmin.rst | 35 + .../LinearRegionSelectionFeature.rst | 2 + .../LinearSelectionFeature.rst | 2 + docs/source/api/graphic_features/Name.rst | 35 + docs/source/api/graphic_features/Offset.rst | 35 + .../graphic_features/PointsDataFeature.rst | 34 - .../graphic_features/PointsSizesFeature.rst | 3 + .../api/graphic_features/PresentFeature.rst | 33 - docs/source/api/graphic_features/Rotation.rst | 35 + docs/source/api/graphic_features/TextData.rst | 35 + .../api/graphic_features/TextFaceColor.rst | 35 + .../api/graphic_features/TextOutlineColor.rst | 35 + .../graphic_features/TextOutlineThickness.rst | 35 + .../api/graphic_features/TextureArray.rst | 39 + .../source/api/graphic_features/Thickness.rst | 35 + .../api/graphic_features/ThicknessFeature.rst | 33 - .../api/graphic_features/UniformColor.rst | 35 + .../api/graphic_features/UniformSize.rst | 35 + .../api/graphic_features/VertexCmap.rst | 40 + .../api/graphic_features/VertexColors.rst | 37 + .../api/graphic_features/VertexPositions.rst | 37 + docs/source/api/graphic_features/Visible.rst | 35 + docs/source/api/graphic_features/index.rst | 34 +- .../to_gpu_supported_dtype.rst | 29 - docs/source/api/graphics/HeatmapGraphic.rst | 44 - docs/source/api/graphics/ImageGraphic.rst | 25 +- docs/source/api/graphics/LineCollection.rst | 27 +- docs/source/api/graphics/LineGraphic.rst | 22 +- docs/source/api/graphics/LineStack.rst | 27 +- docs/source/api/graphics/ScatterGraphic.rst | 19 +- docs/source/api/graphics/TextGraphic.rst | 19 +- docs/source/api/graphics/index.rst | 5 +- docs/source/api/layouts/subplot.rst | 1 - .../api/selectors/LinearRegionSelector.rst | 18 +- docs/source/api/selectors/LinearSelector.rst | 18 +- docs/source/api/selectors/PolygonSelector.rst | 43 - docs/source/api/selectors/Synchronizer.rst | 33 - docs/source/api/selectors/index.rst | 2 - docs/source/conf.py | 3 +- docs/source/generate_api.py | 6 + docs/source/index.rst | 6 - docs/source/quickstart.ipynb | 1673 ----------------- examples/desktop/heatmap/heatmap.py | 12 +- examples/desktop/heatmap/heatmap_cmap.py | 16 +- examples/desktop/heatmap/heatmap_data.py | 20 +- examples/desktop/heatmap/heatmap_square.py | 33 + examples/desktop/heatmap/heatmap_vmin_vmax.py | 18 +- examples/desktop/heatmap/heatmap_wide.py | 32 + examples/desktop/image/image_rgbvminvmax.py | 4 +- examples/desktop/image/image_vminvmax.py | 4 +- examples/desktop/line/line_cmap.py | 8 +- examples/desktop/line/line_colorslice.py | 55 +- examples/desktop/line/line_dataslice.py | 6 +- examples/desktop/line/line_present_scaling.py | 49 - .../line_collection/line_collection.py | 4 +- .../line_collection_cmap_values.py | 11 +- ...line_collection_cmap_values_qualitative.py | 8 +- .../line_collection/line_collection_colors.py | 6 +- .../line_collection_slicing.py | 68 + .../desktop/line_collection/line_stack.py | 22 +- .../desktop/line_collection/line_stack_3d.py | 103 + examples/desktop/scatter/scatter_cmap.py | 6 +- .../desktop/scatter/scatter_colorslice.py | 7 +- examples/desktop/scatter/scatter_present.py | 38 - examples/desktop/screenshots/gridplot.png | 4 +- .../screenshots/gridplot_non_square.png | 4 +- examples/desktop/screenshots/heatmap.png | 4 +- examples/desktop/screenshots/heatmap_cmap.png | 4 +- examples/desktop/screenshots/heatmap_data.png | 4 +- .../desktop/screenshots/heatmap_square.png | 3 + .../desktop/screenshots/heatmap_vmin_vmax.png | 4 +- examples/desktop/screenshots/heatmap_wide.png | 3 + examples/desktop/screenshots/image_cmap.png | 4 +- examples/desktop/screenshots/image_rgb.png | 4 +- .../desktop/screenshots/image_rgbvminvmax.png | 4 +- examples/desktop/screenshots/image_simple.png | 4 +- .../desktop/screenshots/image_vminvmax.png | 4 +- .../screenshots/line_collection_slicing.png | 3 + .../desktop/screenshots/line_colorslice.png | 4 +- .../desktop/screenshots/line_dataslice.png | 4 +- .../screenshots/line_present_scaling.png | 3 - examples/desktop/screenshots/line_stack.png | 4 +- examples/desktop/screenshots/scatter_cmap.png | 4 +- .../desktop/screenshots/scatter_present.png | 3 - examples/notebooks/heatmap.ipynb | 14 +- .../notebooks/linear_region_selector.ipynb | 118 +- examples/notebooks/linear_selector.ipynb | 30 +- examples/notebooks/lines_cmap.ipynb | 30 +- examples/notebooks/quickstart.ipynb | 102 +- .../notebooks/scatter_sizes_animation.ipynb | 22 +- .../notebooks/screenshots/nb-astronaut.png | 4 +- .../screenshots/nb-astronaut_RGB.png | 4 +- examples/notebooks/screenshots/nb-camera.png | 4 +- .../nb-image-widget-movie-set_data.png | 4 +- .../nb-image-widget-single-gnuplot2.png | 4 +- .../screenshots/nb-image-widget-single.png | 4 +- ...et-zfish-frame-50-frame-apply-gaussian.png | 4 +- ...idget-zfish-frame-50-frame-apply-reset.png | 4 +- ...ge-widget-zfish-frame-50-max-window-13.png | 4 +- ...e-widget-zfish-frame-50-mean-window-13.png | 4 +- ...ge-widget-zfish-frame-50-mean-window-5.png | 4 +- .../nb-image-widget-zfish-frame-50.png | 4 +- .../nb-image-widget-zfish-frame-99.png | 4 +- ...ish-grid-frame-50-frame-apply-gaussian.png | 4 +- ...-zfish-grid-frame-50-frame-apply-reset.png | 4 +- ...dget-zfish-grid-frame-50-max-window-13.png | 4 +- ...get-zfish-grid-frame-50-mean-window-13.png | 4 +- ...dget-zfish-grid-frame-50-mean-window-5.png | 4 +- .../nb-image-widget-zfish-grid-frame-50.png | 4 +- .../nb-image-widget-zfish-grid-frame-99.png | 4 +- ...e-widget-zfish-grid-init-mean-window-5.png | 4 +- ...fish-grid-set_data-reset-indices-false.png | 4 +- ...zfish-grid-set_data-reset-indices-true.png | 4 +- ...-image-widget-zfish-init-mean-window-5.png | 4 +- ...dget-zfish-mixed-rgb-cockatoo-frame-50.png | 4 +- ...dget-zfish-mixed-rgb-cockatoo-set-data.png | 4 +- ...get-zfish-mixed-rgb-cockatoo-windowrgb.png | 4 +- .../screenshots/nb-lines-underlay.png | 4 +- examples/notebooks/subplots.ipynb | 54 +- examples/notebooks/subplots_simple.ipynb | 41 +- examples/notebooks/test_gc.ipynb | 24 +- fastplotlib/VERSION | 2 +- fastplotlib/graphics/__init__.py | 8 +- fastplotlib/graphics/_base.py | 782 +++----- fastplotlib/graphics/_collection_base.py | 388 ++++ fastplotlib/graphics/_features/__init__.py | 71 +- fastplotlib/graphics/_features/_base.py | 430 ++--- fastplotlib/graphics/_features/_colors.py | 434 ----- fastplotlib/graphics/_features/_common.py | 123 ++ fastplotlib/graphics/_features/_data.py | 219 --- fastplotlib/graphics/_features/_deleted.py | 41 - fastplotlib/graphics/_features/_image.py | 262 +++ .../graphics/_features/_positions_graphics.py | 458 +++++ fastplotlib/graphics/_features/_present.py | 72 - .../graphics/_features/_selection_features.py | 244 ++- fastplotlib/graphics/_features/_sizes.py | 120 -- fastplotlib/graphics/_features/_text.py | 92 + fastplotlib/graphics/_features/_thickness.py | 46 - fastplotlib/graphics/_features/utils.py | 87 + fastplotlib/graphics/_positions_base.py | 185 ++ fastplotlib/graphics/histogram.py | 114 -- fastplotlib/graphics/image.py | 628 +++---- fastplotlib/graphics/line.py | 308 ++- fastplotlib/graphics/line_collection.py | 561 +++--- fastplotlib/graphics/scatter.py | 127 +- fastplotlib/graphics/selectors/__init__.py | 8 +- .../graphics/selectors/_base_selector.py | 23 +- fastplotlib/graphics/selectors/_linear.py | 129 +- .../graphics/selectors/_linear_region.py | 394 ++-- fastplotlib/graphics/selectors/_sync.py | 90 - fastplotlib/graphics/text.py | 146 +- fastplotlib/layouts/_figure.py | 1 - fastplotlib/layouts/_graphic_methods_mixin.py | 386 ++-- fastplotlib/layouts/_plot_area.py | 15 +- fastplotlib/layouts/_subplot.py | 2 +- fastplotlib/utils/functions.py | 43 +- fastplotlib/utils/gui.py | 1 - fastplotlib/widgets/histogram_lut.py | 83 +- fastplotlib/widgets/image.py | 5 +- scripts/generate_add_graphic_methods.py | 2 +- setup.py | 1 - tests/__init__.py | 0 tests/events.py | 91 + tests/test_colors_buffer_manager.py | 250 +++ tests/test_common_features.py | 282 +++ tests/test_figure.py | 96 +- tests/test_image_graphic.py | 205 ++ tests/test_positions_data_buffer_manager.py | 208 ++ tests/test_positions_graphics.py | 446 +++++ tests/test_sizes_buffer_manager.py | 76 + tests/test_text_graphic.py | 101 + tests/test_texture_array.py | 230 +++ tests/utils.py | 185 ++ 192 files changed, 7204 insertions(+), 6537 deletions(-) delete mode 100644 docs/source/api/graphic_features/CmapFeature.rst delete mode 100644 docs/source/api/graphic_features/ColorFeature.rst delete mode 100644 docs/source/api/graphic_features/FeatureEvent.rst create mode 100644 docs/source/api/graphic_features/FontSize.rst delete mode 100644 docs/source/api/graphic_features/GraphicFeature.rst delete mode 100644 docs/source/api/graphic_features/GraphicFeatureIndexable.rst delete mode 100644 docs/source/api/graphic_features/HeatmapCmapFeature.rst delete mode 100644 docs/source/api/graphic_features/HeatmapDataFeature.rst create mode 100644 docs/source/api/graphic_features/ImageCmap.rst delete mode 100644 docs/source/api/graphic_features/ImageCmapFeature.rst create mode 100644 docs/source/api/graphic_features/ImageCmapInterpolation.rst delete mode 100644 docs/source/api/graphic_features/ImageDataFeature.rst create mode 100644 docs/source/api/graphic_features/ImageInterpolation.rst create mode 100644 docs/source/api/graphic_features/ImageVmax.rst create mode 100644 docs/source/api/graphic_features/ImageVmin.rst create mode 100644 docs/source/api/graphic_features/Name.rst create mode 100644 docs/source/api/graphic_features/Offset.rst delete mode 100644 docs/source/api/graphic_features/PointsDataFeature.rst delete mode 100644 docs/source/api/graphic_features/PresentFeature.rst create mode 100644 docs/source/api/graphic_features/Rotation.rst create mode 100644 docs/source/api/graphic_features/TextData.rst create mode 100644 docs/source/api/graphic_features/TextFaceColor.rst create mode 100644 docs/source/api/graphic_features/TextOutlineColor.rst create mode 100644 docs/source/api/graphic_features/TextOutlineThickness.rst create mode 100644 docs/source/api/graphic_features/TextureArray.rst create mode 100644 docs/source/api/graphic_features/Thickness.rst delete mode 100644 docs/source/api/graphic_features/ThicknessFeature.rst create mode 100644 docs/source/api/graphic_features/UniformColor.rst create mode 100644 docs/source/api/graphic_features/UniformSize.rst create mode 100644 docs/source/api/graphic_features/VertexCmap.rst create mode 100644 docs/source/api/graphic_features/VertexColors.rst create mode 100644 docs/source/api/graphic_features/VertexPositions.rst create mode 100644 docs/source/api/graphic_features/Visible.rst delete mode 100644 docs/source/api/graphic_features/to_gpu_supported_dtype.rst delete mode 100644 docs/source/api/graphics/HeatmapGraphic.rst delete mode 100644 docs/source/api/selectors/PolygonSelector.rst delete mode 100644 docs/source/api/selectors/Synchronizer.rst delete mode 100644 docs/source/quickstart.ipynb create mode 100644 examples/desktop/heatmap/heatmap_square.py create mode 100644 examples/desktop/heatmap/heatmap_wide.py delete mode 100644 examples/desktop/line/line_present_scaling.py create mode 100644 examples/desktop/line_collection/line_collection_slicing.py create mode 100644 examples/desktop/line_collection/line_stack_3d.py delete mode 100644 examples/desktop/scatter/scatter_present.py create mode 100644 examples/desktop/screenshots/heatmap_square.png create mode 100644 examples/desktop/screenshots/heatmap_wide.png create mode 100644 examples/desktop/screenshots/line_collection_slicing.png delete mode 100644 examples/desktop/screenshots/line_present_scaling.png delete mode 100644 examples/desktop/screenshots/scatter_present.png create mode 100644 fastplotlib/graphics/_collection_base.py delete mode 100644 fastplotlib/graphics/_features/_colors.py create mode 100644 fastplotlib/graphics/_features/_common.py delete mode 100644 fastplotlib/graphics/_features/_data.py delete mode 100644 fastplotlib/graphics/_features/_deleted.py create mode 100644 fastplotlib/graphics/_features/_image.py create mode 100644 fastplotlib/graphics/_features/_positions_graphics.py delete mode 100644 fastplotlib/graphics/_features/_present.py delete mode 100644 fastplotlib/graphics/_features/_sizes.py create mode 100644 fastplotlib/graphics/_features/_text.py delete mode 100644 fastplotlib/graphics/_features/_thickness.py create mode 100644 fastplotlib/graphics/_features/utils.py create mode 100644 fastplotlib/graphics/_positions_base.py delete mode 100644 fastplotlib/graphics/histogram.py delete mode 100644 fastplotlib/graphics/selectors/_sync.py create mode 100644 tests/__init__.py create mode 100644 tests/events.py create mode 100644 tests/test_colors_buffer_manager.py create mode 100644 tests/test_common_features.py create mode 100644 tests/test_image_graphic.py create mode 100644 tests/test_positions_data_buffer_manager.py create mode 100644 tests/test_positions_graphics.py create mode 100644 tests/test_sizes_buffer_manager.py create mode 100644 tests/test_text_graphic.py create mode 100644 tests/test_texture_array.py create mode 100644 tests/utils.py diff --git a/.github/workflows/black.yml b/.github/workflows/black.yml index bcb2d2b33..bec47fdc5 100644 --- a/.github/workflows/black.yml +++ b/.github/workflows/black.yml @@ -1,12 +1,24 @@ name: Lint -on: [push, pull_request] +on: + push: + branches: + - main + pull_request: + branches: + - main + types: + - opened + - reopened + - synchronize + - ready_for_review jobs: lint: runs-on: ubuntu-latest + if: ${{ !github.event.pull_request.draft }} steps: - uses: actions/checkout@v4 - uses: psf/black@stable with: - src: "./fastplotlib" \ No newline at end of file + src: "./fastplotlib" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7db694d01..9adb67f77 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,6 +17,7 @@ jobs: docs-build: name: Docs runs-on: bigmem + if: ${{ !github.event.pull_request.draft }} strategy: fail-fast: false steps: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fe9c90242..0786596b4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -77,35 +77,37 @@ keeps a *private* global dictionary of all `WorldObject` instances and users are This is due to garbage collection. This may be quite complicated for beginners, for more details see this PR: https://github.com/fastplotlib/fastplotlib/pull/160 . If you are curious or have more questions on garbage collection in fastplotlib you're welcome to post an issue :D. -#### Graphic Features +#### Graphic properties -There is one important thing that `fastplotlib` uses which we call "graphic features". +Graphic properties are all evented, and internally we called these "graphic features". They are the various +aspects of a graphic that the user can change. The "graphic features" subpackage can be found at `fastplotlib/graphics/_features`. As we can see this -is a private subpackage and never meant to be accessible to users. In `fastplotlib` "graphic features" are the various -aspects of a graphic that the user can change. Users can also run callbacks whenever a graphic feature changes. +is a private subpackage and never meant to be accessible to users.. ##### LineGraphic For example let's look at `LineGraphic` in `fastplotlib/graphics/line.py`. Every graphic has a class variable called -`feature_events` which is a set of all graphic features. It has the following graphic features: "data", "colors", "cmap", "thickness", "present". +`_features` which is a set of all graphic properties that are evented. It has the following evented properties: +`"data", "colors", "cmap", "thickness"` in addition to properties common to all graphics, such as `"name", "offset", "rotation", and "visible"` -Now look at the constructor for `LineGraphic`, it first creates an instance of `PointsDataFeature`. This is basically a -class that wraps the positions buffer, the vertex positions that define the line, and provides additional useful functionality. -For example, every time that the `data` is changed event handlers will be called (if any event handlers are registered). +Now look at the constructor for the `LineGraphic` base class `PositionsGraphic`, it first creates an instance of `VertexPositions`. +This is a class that manages vertex positions buffer. It defines the line, and provides additional useful functionality. +For example, every time that the `data` is changed, the new data will be marked for upload to the GPU before the next draw. +In addition, event handlers will be called if any event handlers are registered. -`ColorFeature`behaves similarly, but it can perform additional parsing that can create the colors buffer from different forms of user input. For example if a user runs: -`line_graphic.colors = "blue"`, then `ColorFeature.__setitem__()` will create a buffer that corresponds to what `pygfx.Color` thinks is "blue". -Users can also take advantage of fancy indexing, ex: `line_graphics.colors[bool_array] = "red"` :smile: +`VertexColors`behaves similarly, but it can perform additional parsing that can create the colors buffer from different +forms of user input. For example if a user runs: `line_graphic.colors = "blue"`, then `VertexColors.__setitem__()` will +create a buffer that corresponds to what `pygfx.Color` thinks is "blue". Users can also take advantage of fancy indexing, +ex: `line_graphics.colors[bool_array] = "red"` :smile: -`LineGraphic` also has a `CmapFeature`, this is a subclass of `ColorFeature` which can parse colormaps, for example: +`LineGraphic` also has a `VertexCmap`, this manages the line `VertexColors` instance to parse colormaps, for example: `line_graphic.cmap = "jet"` or even `line_graphic.cmap[50:] = "viridis"`. -`LineGraphic` also has `ThicknessFeature` which is pretty simple, `PresentFeature` which indicates if a graphic is -currently in the scene, and `DeletedFeature` which is useful if you need callbacks to indicate that the graphic has been -deleted (for example, removing references to a graphic from a legend). +`LineGraphic` also has a `thickness` property which is pretty simple, and `DeletedFeature` which is useful if you need +callbacks to indicate that the graphic has been deleted (for example, removing references to a graphic from a legend). -Other graphics have graphic features that are relevant to them, for example `ImageGraphic` has a `cmap` feature which is -unique to images or heatmaps. +Other graphics have properties that are relevant to them, for example `ImageGraphic` has `cmap`, `vmin`, `vmax`, +properties unique to images. #### Selectors @@ -192,9 +194,10 @@ the subplots. All subplots within a `Figure` share the same canvas and use diffe ## Tests in detail -The CI pipeline for a plotting library that is supposed to produce things that "look visually correct". Each example -within the `examples` dir is run and an image of the canvas is taken and compared with a ground-truth -screenshot that we have manually inspected. Ground-truth image are stored using `git-lfs`. +Backend tests are in `tests/`, in addition as a plotting library CI pipeline produces things that +"look visually correct". Each example within the `examples` dir is run and an image of the canvas +is taken and compared with a ground-truth screenshot that we have manually inspected. +Ground-truth image are stored using `git-lfs`. The ground-truth images are in: diff --git a/docs/source/api/gpu.rst b/docs/source/api/gpu.rst index 62ffd5797..6f94aff23 100644 --- a/docs/source/api/gpu.rst +++ b/docs/source/api/gpu.rst @@ -1,5 +1,6 @@ -fastplotlib.utils -***************** +fastplotlib.utils.gpu +********************* -.. automodule:: fastplotlib.utils.gpu +.. currentmodule:: fastplotlib.utils.gpu +.. automodule:: fastplotlib :members: diff --git a/docs/source/api/graphic_features/CmapFeature.rst b/docs/source/api/graphic_features/CmapFeature.rst deleted file mode 100644 index 7cc2f681f..000000000 --- a/docs/source/api/graphic_features/CmapFeature.rst +++ /dev/null @@ -1,36 +0,0 @@ -.. _api.CmapFeature: - -CmapFeature -*********** - -=========== -CmapFeature -=========== -.. currentmodule:: fastplotlib.graphics._features - -Constructor -~~~~~~~~~~~ -.. autosummary:: - :toctree: CmapFeature_api - - CmapFeature - -Properties -~~~~~~~~~~ -.. autosummary:: - :toctree: CmapFeature_api - - CmapFeature.buffer - CmapFeature.name - CmapFeature.values - -Methods -~~~~~~~ -.. autosummary:: - :toctree: CmapFeature_api - - CmapFeature.add_event_handler - CmapFeature.block_events - CmapFeature.clear_event_handlers - CmapFeature.remove_event_handler - diff --git a/docs/source/api/graphic_features/ColorFeature.rst b/docs/source/api/graphic_features/ColorFeature.rst deleted file mode 100644 index 3ed84cd70..000000000 --- a/docs/source/api/graphic_features/ColorFeature.rst +++ /dev/null @@ -1,34 +0,0 @@ -.. _api.ColorFeature: - -ColorFeature -************ - -============ -ColorFeature -============ -.. currentmodule:: fastplotlib.graphics._features - -Constructor -~~~~~~~~~~~ -.. autosummary:: - :toctree: ColorFeature_api - - ColorFeature - -Properties -~~~~~~~~~~ -.. autosummary:: - :toctree: ColorFeature_api - - ColorFeature.buffer - -Methods -~~~~~~~ -.. autosummary:: - :toctree: ColorFeature_api - - ColorFeature.add_event_handler - ColorFeature.block_events - ColorFeature.clear_event_handlers - ColorFeature.remove_event_handler - diff --git a/docs/source/api/graphic_features/Deleted.rst b/docs/source/api/graphic_features/Deleted.rst index 998e94588..09131c4a7 100644 --- a/docs/source/api/graphic_features/Deleted.rst +++ b/docs/source/api/graphic_features/Deleted.rst @@ -20,6 +20,7 @@ Properties .. autosummary:: :toctree: Deleted_api + Deleted.value Methods ~~~~~~~ @@ -30,4 +31,5 @@ Methods Deleted.block_events Deleted.clear_event_handlers Deleted.remove_event_handler + Deleted.set_value diff --git a/docs/source/api/graphic_features/FeatureEvent.rst b/docs/source/api/graphic_features/FeatureEvent.rst deleted file mode 100644 index f22ee3ef4..000000000 --- a/docs/source/api/graphic_features/FeatureEvent.rst +++ /dev/null @@ -1,29 +0,0 @@ -.. _api.FeatureEvent: - -FeatureEvent -************ - -============ -FeatureEvent -============ -.. currentmodule:: fastplotlib.graphics._features - -Constructor -~~~~~~~~~~~ -.. autosummary:: - :toctree: FeatureEvent_api - - FeatureEvent - -Properties -~~~~~~~~~~ -.. autosummary:: - :toctree: FeatureEvent_api - - -Methods -~~~~~~~ -.. autosummary:: - :toctree: FeatureEvent_api - - diff --git a/docs/source/api/graphic_features/FontSize.rst b/docs/source/api/graphic_features/FontSize.rst new file mode 100644 index 000000000..4b8df9826 --- /dev/null +++ b/docs/source/api/graphic_features/FontSize.rst @@ -0,0 +1,35 @@ +.. _api.FontSize: + +FontSize +******** + +======== +FontSize +======== +.. currentmodule:: fastplotlib.graphics._features + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: FontSize_api + + FontSize + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: FontSize_api + + FontSize.value + +Methods +~~~~~~~ +.. autosummary:: + :toctree: FontSize_api + + FontSize.add_event_handler + FontSize.block_events + FontSize.clear_event_handlers + FontSize.remove_event_handler + FontSize.set_value + diff --git a/docs/source/api/graphic_features/GraphicFeature.rst b/docs/source/api/graphic_features/GraphicFeature.rst deleted file mode 100644 index 7abc3e6b2..000000000 --- a/docs/source/api/graphic_features/GraphicFeature.rst +++ /dev/null @@ -1,33 +0,0 @@ -.. _api.GraphicFeature: - -GraphicFeature -************** - -============== -GraphicFeature -============== -.. currentmodule:: fastplotlib.graphics._features - -Constructor -~~~~~~~~~~~ -.. autosummary:: - :toctree: GraphicFeature_api - - GraphicFeature - -Properties -~~~~~~~~~~ -.. autosummary:: - :toctree: GraphicFeature_api - - -Methods -~~~~~~~ -.. autosummary:: - :toctree: GraphicFeature_api - - GraphicFeature.add_event_handler - GraphicFeature.block_events - GraphicFeature.clear_event_handlers - GraphicFeature.remove_event_handler - diff --git a/docs/source/api/graphic_features/GraphicFeatureIndexable.rst b/docs/source/api/graphic_features/GraphicFeatureIndexable.rst deleted file mode 100644 index 7bd1383bc..000000000 --- a/docs/source/api/graphic_features/GraphicFeatureIndexable.rst +++ /dev/null @@ -1,34 +0,0 @@ -.. _api.GraphicFeatureIndexable: - -GraphicFeatureIndexable -*********************** - -======================= -GraphicFeatureIndexable -======================= -.. currentmodule:: fastplotlib.graphics._features - -Constructor -~~~~~~~~~~~ -.. autosummary:: - :toctree: GraphicFeatureIndexable_api - - GraphicFeatureIndexable - -Properties -~~~~~~~~~~ -.. autosummary:: - :toctree: GraphicFeatureIndexable_api - - GraphicFeatureIndexable.buffer - -Methods -~~~~~~~ -.. autosummary:: - :toctree: GraphicFeatureIndexable_api - - GraphicFeatureIndexable.add_event_handler - GraphicFeatureIndexable.block_events - GraphicFeatureIndexable.clear_event_handlers - GraphicFeatureIndexable.remove_event_handler - diff --git a/docs/source/api/graphic_features/HeatmapCmapFeature.rst b/docs/source/api/graphic_features/HeatmapCmapFeature.rst deleted file mode 100644 index bac43c9b9..000000000 --- a/docs/source/api/graphic_features/HeatmapCmapFeature.rst +++ /dev/null @@ -1,37 +0,0 @@ -.. _api.HeatmapCmapFeature: - -HeatmapCmapFeature -****************** - -================== -HeatmapCmapFeature -================== -.. currentmodule:: fastplotlib.graphics._features - -Constructor -~~~~~~~~~~~ -.. autosummary:: - :toctree: HeatmapCmapFeature_api - - HeatmapCmapFeature - -Properties -~~~~~~~~~~ -.. autosummary:: - :toctree: HeatmapCmapFeature_api - - HeatmapCmapFeature.name - HeatmapCmapFeature.vmax - HeatmapCmapFeature.vmin - -Methods -~~~~~~~ -.. autosummary:: - :toctree: HeatmapCmapFeature_api - - HeatmapCmapFeature.add_event_handler - HeatmapCmapFeature.block_events - HeatmapCmapFeature.clear_event_handlers - HeatmapCmapFeature.remove_event_handler - HeatmapCmapFeature.reset_vmin_vmax - diff --git a/docs/source/api/graphic_features/HeatmapDataFeature.rst b/docs/source/api/graphic_features/HeatmapDataFeature.rst deleted file mode 100644 index 029f0e199..000000000 --- a/docs/source/api/graphic_features/HeatmapDataFeature.rst +++ /dev/null @@ -1,35 +0,0 @@ -.. _api.HeatmapDataFeature: - -HeatmapDataFeature -****************** - -================== -HeatmapDataFeature -================== -.. currentmodule:: fastplotlib.graphics._features - -Constructor -~~~~~~~~~~~ -.. autosummary:: - :toctree: HeatmapDataFeature_api - - HeatmapDataFeature - -Properties -~~~~~~~~~~ -.. autosummary:: - :toctree: HeatmapDataFeature_api - - HeatmapDataFeature.buffer - -Methods -~~~~~~~ -.. autosummary:: - :toctree: HeatmapDataFeature_api - - HeatmapDataFeature.add_event_handler - HeatmapDataFeature.block_events - HeatmapDataFeature.clear_event_handlers - HeatmapDataFeature.remove_event_handler - HeatmapDataFeature.update_gpu - diff --git a/docs/source/api/graphic_features/ImageCmap.rst b/docs/source/api/graphic_features/ImageCmap.rst new file mode 100644 index 000000000..23d16a4a2 --- /dev/null +++ b/docs/source/api/graphic_features/ImageCmap.rst @@ -0,0 +1,35 @@ +.. _api.ImageCmap: + +ImageCmap +********* + +========= +ImageCmap +========= +.. currentmodule:: fastplotlib.graphics._features + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: ImageCmap_api + + ImageCmap + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: ImageCmap_api + + ImageCmap.value + +Methods +~~~~~~~ +.. autosummary:: + :toctree: ImageCmap_api + + ImageCmap.add_event_handler + ImageCmap.block_events + ImageCmap.clear_event_handlers + ImageCmap.remove_event_handler + ImageCmap.set_value + diff --git a/docs/source/api/graphic_features/ImageCmapFeature.rst b/docs/source/api/graphic_features/ImageCmapFeature.rst deleted file mode 100644 index ae65744c7..000000000 --- a/docs/source/api/graphic_features/ImageCmapFeature.rst +++ /dev/null @@ -1,37 +0,0 @@ -.. _api.ImageCmapFeature: - -ImageCmapFeature -**************** - -================ -ImageCmapFeature -================ -.. currentmodule:: fastplotlib.graphics._features - -Constructor -~~~~~~~~~~~ -.. autosummary:: - :toctree: ImageCmapFeature_api - - ImageCmapFeature - -Properties -~~~~~~~~~~ -.. autosummary:: - :toctree: ImageCmapFeature_api - - ImageCmapFeature.name - ImageCmapFeature.vmax - ImageCmapFeature.vmin - -Methods -~~~~~~~ -.. autosummary:: - :toctree: ImageCmapFeature_api - - ImageCmapFeature.add_event_handler - ImageCmapFeature.block_events - ImageCmapFeature.clear_event_handlers - ImageCmapFeature.remove_event_handler - ImageCmapFeature.reset_vmin_vmax - diff --git a/docs/source/api/graphic_features/ImageCmapInterpolation.rst b/docs/source/api/graphic_features/ImageCmapInterpolation.rst new file mode 100644 index 000000000..7e04ec788 --- /dev/null +++ b/docs/source/api/graphic_features/ImageCmapInterpolation.rst @@ -0,0 +1,35 @@ +.. _api.ImageCmapInterpolation: + +ImageCmapInterpolation +********************** + +====================== +ImageCmapInterpolation +====================== +.. currentmodule:: fastplotlib.graphics._features + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: ImageCmapInterpolation_api + + ImageCmapInterpolation + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: ImageCmapInterpolation_api + + ImageCmapInterpolation.value + +Methods +~~~~~~~ +.. autosummary:: + :toctree: ImageCmapInterpolation_api + + ImageCmapInterpolation.add_event_handler + ImageCmapInterpolation.block_events + ImageCmapInterpolation.clear_event_handlers + ImageCmapInterpolation.remove_event_handler + ImageCmapInterpolation.set_value + diff --git a/docs/source/api/graphic_features/ImageDataFeature.rst b/docs/source/api/graphic_features/ImageDataFeature.rst deleted file mode 100644 index 35fe74cf7..000000000 --- a/docs/source/api/graphic_features/ImageDataFeature.rst +++ /dev/null @@ -1,35 +0,0 @@ -.. _api.ImageDataFeature: - -ImageDataFeature -**************** - -================ -ImageDataFeature -================ -.. currentmodule:: fastplotlib.graphics._features - -Constructor -~~~~~~~~~~~ -.. autosummary:: - :toctree: ImageDataFeature_api - - ImageDataFeature - -Properties -~~~~~~~~~~ -.. autosummary:: - :toctree: ImageDataFeature_api - - ImageDataFeature.buffer - -Methods -~~~~~~~ -.. autosummary:: - :toctree: ImageDataFeature_api - - ImageDataFeature.add_event_handler - ImageDataFeature.block_events - ImageDataFeature.clear_event_handlers - ImageDataFeature.remove_event_handler - ImageDataFeature.update_gpu - diff --git a/docs/source/api/graphic_features/ImageInterpolation.rst b/docs/source/api/graphic_features/ImageInterpolation.rst new file mode 100644 index 000000000..866e76333 --- /dev/null +++ b/docs/source/api/graphic_features/ImageInterpolation.rst @@ -0,0 +1,35 @@ +.. _api.ImageInterpolation: + +ImageInterpolation +****************** + +================== +ImageInterpolation +================== +.. currentmodule:: fastplotlib.graphics._features + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: ImageInterpolation_api + + ImageInterpolation + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: ImageInterpolation_api + + ImageInterpolation.value + +Methods +~~~~~~~ +.. autosummary:: + :toctree: ImageInterpolation_api + + ImageInterpolation.add_event_handler + ImageInterpolation.block_events + ImageInterpolation.clear_event_handlers + ImageInterpolation.remove_event_handler + ImageInterpolation.set_value + diff --git a/docs/source/api/graphic_features/ImageVmax.rst b/docs/source/api/graphic_features/ImageVmax.rst new file mode 100644 index 000000000..b7dfe7e2d --- /dev/null +++ b/docs/source/api/graphic_features/ImageVmax.rst @@ -0,0 +1,35 @@ +.. _api.ImageVmax: + +ImageVmax +********* + +========= +ImageVmax +========= +.. currentmodule:: fastplotlib.graphics._features + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: ImageVmax_api + + ImageVmax + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: ImageVmax_api + + ImageVmax.value + +Methods +~~~~~~~ +.. autosummary:: + :toctree: ImageVmax_api + + ImageVmax.add_event_handler + ImageVmax.block_events + ImageVmax.clear_event_handlers + ImageVmax.remove_event_handler + ImageVmax.set_value + diff --git a/docs/source/api/graphic_features/ImageVmin.rst b/docs/source/api/graphic_features/ImageVmin.rst new file mode 100644 index 000000000..0d4634894 --- /dev/null +++ b/docs/source/api/graphic_features/ImageVmin.rst @@ -0,0 +1,35 @@ +.. _api.ImageVmin: + +ImageVmin +********* + +========= +ImageVmin +========= +.. currentmodule:: fastplotlib.graphics._features + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: ImageVmin_api + + ImageVmin + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: ImageVmin_api + + ImageVmin.value + +Methods +~~~~~~~ +.. autosummary:: + :toctree: ImageVmin_api + + ImageVmin.add_event_handler + ImageVmin.block_events + ImageVmin.clear_event_handlers + ImageVmin.remove_event_handler + ImageVmin.set_value + diff --git a/docs/source/api/graphic_features/LinearRegionSelectionFeature.rst b/docs/source/api/graphic_features/LinearRegionSelectionFeature.rst index a15825530..b8958c86b 100644 --- a/docs/source/api/graphic_features/LinearRegionSelectionFeature.rst +++ b/docs/source/api/graphic_features/LinearRegionSelectionFeature.rst @@ -21,6 +21,7 @@ Properties :toctree: LinearRegionSelectionFeature_api LinearRegionSelectionFeature.axis + LinearRegionSelectionFeature.value Methods ~~~~~~~ @@ -31,4 +32,5 @@ Methods LinearRegionSelectionFeature.block_events LinearRegionSelectionFeature.clear_event_handlers LinearRegionSelectionFeature.remove_event_handler + LinearRegionSelectionFeature.set_value diff --git a/docs/source/api/graphic_features/LinearSelectionFeature.rst b/docs/source/api/graphic_features/LinearSelectionFeature.rst index aeb1ca66b..ad7b8645a 100644 --- a/docs/source/api/graphic_features/LinearSelectionFeature.rst +++ b/docs/source/api/graphic_features/LinearSelectionFeature.rst @@ -20,6 +20,7 @@ Properties .. autosummary:: :toctree: LinearSelectionFeature_api + LinearSelectionFeature.value Methods ~~~~~~~ @@ -30,4 +31,5 @@ Methods LinearSelectionFeature.block_events LinearSelectionFeature.clear_event_handlers LinearSelectionFeature.remove_event_handler + LinearSelectionFeature.set_value diff --git a/docs/source/api/graphic_features/Name.rst b/docs/source/api/graphic_features/Name.rst new file mode 100644 index 000000000..288fcfc22 --- /dev/null +++ b/docs/source/api/graphic_features/Name.rst @@ -0,0 +1,35 @@ +.. _api.Name: + +Name +**** + +==== +Name +==== +.. currentmodule:: fastplotlib.graphics._features + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: Name_api + + Name + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: Name_api + + Name.value + +Methods +~~~~~~~ +.. autosummary:: + :toctree: Name_api + + Name.add_event_handler + Name.block_events + Name.clear_event_handlers + Name.remove_event_handler + Name.set_value + diff --git a/docs/source/api/graphic_features/Offset.rst b/docs/source/api/graphic_features/Offset.rst new file mode 100644 index 000000000..683aaf763 --- /dev/null +++ b/docs/source/api/graphic_features/Offset.rst @@ -0,0 +1,35 @@ +.. _api.Offset: + +Offset +****** + +====== +Offset +====== +.. currentmodule:: fastplotlib.graphics._features + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: Offset_api + + Offset + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: Offset_api + + Offset.value + +Methods +~~~~~~~ +.. autosummary:: + :toctree: Offset_api + + Offset.add_event_handler + Offset.block_events + Offset.clear_event_handlers + Offset.remove_event_handler + Offset.set_value + diff --git a/docs/source/api/graphic_features/PointsDataFeature.rst b/docs/source/api/graphic_features/PointsDataFeature.rst deleted file mode 100644 index 078b1c535..000000000 --- a/docs/source/api/graphic_features/PointsDataFeature.rst +++ /dev/null @@ -1,34 +0,0 @@ -.. _api.PointsDataFeature: - -PointsDataFeature -***************** - -================= -PointsDataFeature -================= -.. currentmodule:: fastplotlib.graphics._features - -Constructor -~~~~~~~~~~~ -.. autosummary:: - :toctree: PointsDataFeature_api - - PointsDataFeature - -Properties -~~~~~~~~~~ -.. autosummary:: - :toctree: PointsDataFeature_api - - PointsDataFeature.buffer - -Methods -~~~~~~~ -.. autosummary:: - :toctree: PointsDataFeature_api - - PointsDataFeature.add_event_handler - PointsDataFeature.block_events - PointsDataFeature.clear_event_handlers - PointsDataFeature.remove_event_handler - diff --git a/docs/source/api/graphic_features/PointsSizesFeature.rst b/docs/source/api/graphic_features/PointsSizesFeature.rst index 7915cb09d..3dcc4eeb2 100644 --- a/docs/source/api/graphic_features/PointsSizesFeature.rst +++ b/docs/source/api/graphic_features/PointsSizesFeature.rst @@ -21,6 +21,8 @@ Properties :toctree: PointsSizesFeature_api PointsSizesFeature.buffer + PointsSizesFeature.shared + PointsSizesFeature.value Methods ~~~~~~~ @@ -31,4 +33,5 @@ Methods PointsSizesFeature.block_events PointsSizesFeature.clear_event_handlers PointsSizesFeature.remove_event_handler + PointsSizesFeature.set_value diff --git a/docs/source/api/graphic_features/PresentFeature.rst b/docs/source/api/graphic_features/PresentFeature.rst deleted file mode 100644 index 1ddbf1ec4..000000000 --- a/docs/source/api/graphic_features/PresentFeature.rst +++ /dev/null @@ -1,33 +0,0 @@ -.. _api.PresentFeature: - -PresentFeature -************** - -============== -PresentFeature -============== -.. currentmodule:: fastplotlib.graphics._features - -Constructor -~~~~~~~~~~~ -.. autosummary:: - :toctree: PresentFeature_api - - PresentFeature - -Properties -~~~~~~~~~~ -.. autosummary:: - :toctree: PresentFeature_api - - -Methods -~~~~~~~ -.. autosummary:: - :toctree: PresentFeature_api - - PresentFeature.add_event_handler - PresentFeature.block_events - PresentFeature.clear_event_handlers - PresentFeature.remove_event_handler - diff --git a/docs/source/api/graphic_features/Rotation.rst b/docs/source/api/graphic_features/Rotation.rst new file mode 100644 index 000000000..f8963b0fd --- /dev/null +++ b/docs/source/api/graphic_features/Rotation.rst @@ -0,0 +1,35 @@ +.. _api.Rotation: + +Rotation +******** + +======== +Rotation +======== +.. currentmodule:: fastplotlib.graphics._features + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: Rotation_api + + Rotation + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: Rotation_api + + Rotation.value + +Methods +~~~~~~~ +.. autosummary:: + :toctree: Rotation_api + + Rotation.add_event_handler + Rotation.block_events + Rotation.clear_event_handlers + Rotation.remove_event_handler + Rotation.set_value + diff --git a/docs/source/api/graphic_features/TextData.rst b/docs/source/api/graphic_features/TextData.rst new file mode 100644 index 000000000..1c27b6e48 --- /dev/null +++ b/docs/source/api/graphic_features/TextData.rst @@ -0,0 +1,35 @@ +.. _api.TextData: + +TextData +******** + +======== +TextData +======== +.. currentmodule:: fastplotlib.graphics._features + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: TextData_api + + TextData + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: TextData_api + + TextData.value + +Methods +~~~~~~~ +.. autosummary:: + :toctree: TextData_api + + TextData.add_event_handler + TextData.block_events + TextData.clear_event_handlers + TextData.remove_event_handler + TextData.set_value + diff --git a/docs/source/api/graphic_features/TextFaceColor.rst b/docs/source/api/graphic_features/TextFaceColor.rst new file mode 100644 index 000000000..5dae54192 --- /dev/null +++ b/docs/source/api/graphic_features/TextFaceColor.rst @@ -0,0 +1,35 @@ +.. _api.TextFaceColor: + +TextFaceColor +************* + +============= +TextFaceColor +============= +.. currentmodule:: fastplotlib.graphics._features + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: TextFaceColor_api + + TextFaceColor + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: TextFaceColor_api + + TextFaceColor.value + +Methods +~~~~~~~ +.. autosummary:: + :toctree: TextFaceColor_api + + TextFaceColor.add_event_handler + TextFaceColor.block_events + TextFaceColor.clear_event_handlers + TextFaceColor.remove_event_handler + TextFaceColor.set_value + diff --git a/docs/source/api/graphic_features/TextOutlineColor.rst b/docs/source/api/graphic_features/TextOutlineColor.rst new file mode 100644 index 000000000..f7831b0df --- /dev/null +++ b/docs/source/api/graphic_features/TextOutlineColor.rst @@ -0,0 +1,35 @@ +.. _api.TextOutlineColor: + +TextOutlineColor +**************** + +================ +TextOutlineColor +================ +.. currentmodule:: fastplotlib.graphics._features + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: TextOutlineColor_api + + TextOutlineColor + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: TextOutlineColor_api + + TextOutlineColor.value + +Methods +~~~~~~~ +.. autosummary:: + :toctree: TextOutlineColor_api + + TextOutlineColor.add_event_handler + TextOutlineColor.block_events + TextOutlineColor.clear_event_handlers + TextOutlineColor.remove_event_handler + TextOutlineColor.set_value + diff --git a/docs/source/api/graphic_features/TextOutlineThickness.rst b/docs/source/api/graphic_features/TextOutlineThickness.rst new file mode 100644 index 000000000..75d485781 --- /dev/null +++ b/docs/source/api/graphic_features/TextOutlineThickness.rst @@ -0,0 +1,35 @@ +.. _api.TextOutlineThickness: + +TextOutlineThickness +******************** + +==================== +TextOutlineThickness +==================== +.. currentmodule:: fastplotlib.graphics._features + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: TextOutlineThickness_api + + TextOutlineThickness + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: TextOutlineThickness_api + + TextOutlineThickness.value + +Methods +~~~~~~~ +.. autosummary:: + :toctree: TextOutlineThickness_api + + TextOutlineThickness.add_event_handler + TextOutlineThickness.block_events + TextOutlineThickness.clear_event_handlers + TextOutlineThickness.remove_event_handler + TextOutlineThickness.set_value + diff --git a/docs/source/api/graphic_features/TextureArray.rst b/docs/source/api/graphic_features/TextureArray.rst new file mode 100644 index 000000000..79707c453 --- /dev/null +++ b/docs/source/api/graphic_features/TextureArray.rst @@ -0,0 +1,39 @@ +.. _api.TextureArray: + +TextureArray +************ + +============ +TextureArray +============ +.. currentmodule:: fastplotlib.graphics._features + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: TextureArray_api + + TextureArray + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: TextureArray_api + + TextureArray.buffer + TextureArray.col_indices + TextureArray.row_indices + TextureArray.shared + TextureArray.value + +Methods +~~~~~~~ +.. autosummary:: + :toctree: TextureArray_api + + TextureArray.add_event_handler + TextureArray.block_events + TextureArray.clear_event_handlers + TextureArray.remove_event_handler + TextureArray.set_value + diff --git a/docs/source/api/graphic_features/Thickness.rst b/docs/source/api/graphic_features/Thickness.rst new file mode 100644 index 000000000..061f96fe8 --- /dev/null +++ b/docs/source/api/graphic_features/Thickness.rst @@ -0,0 +1,35 @@ +.. _api.Thickness: + +Thickness +********* + +========= +Thickness +========= +.. currentmodule:: fastplotlib.graphics._features + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: Thickness_api + + Thickness + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: Thickness_api + + Thickness.value + +Methods +~~~~~~~ +.. autosummary:: + :toctree: Thickness_api + + Thickness.add_event_handler + Thickness.block_events + Thickness.clear_event_handlers + Thickness.remove_event_handler + Thickness.set_value + diff --git a/docs/source/api/graphic_features/ThicknessFeature.rst b/docs/source/api/graphic_features/ThicknessFeature.rst deleted file mode 100644 index 80219a2cd..000000000 --- a/docs/source/api/graphic_features/ThicknessFeature.rst +++ /dev/null @@ -1,33 +0,0 @@ -.. _api.ThicknessFeature: - -ThicknessFeature -**************** - -================ -ThicknessFeature -================ -.. currentmodule:: fastplotlib.graphics._features - -Constructor -~~~~~~~~~~~ -.. autosummary:: - :toctree: ThicknessFeature_api - - ThicknessFeature - -Properties -~~~~~~~~~~ -.. autosummary:: - :toctree: ThicknessFeature_api - - -Methods -~~~~~~~ -.. autosummary:: - :toctree: ThicknessFeature_api - - ThicknessFeature.add_event_handler - ThicknessFeature.block_events - ThicknessFeature.clear_event_handlers - ThicknessFeature.remove_event_handler - diff --git a/docs/source/api/graphic_features/UniformColor.rst b/docs/source/api/graphic_features/UniformColor.rst new file mode 100644 index 000000000..7370589b7 --- /dev/null +++ b/docs/source/api/graphic_features/UniformColor.rst @@ -0,0 +1,35 @@ +.. _api.UniformColor: + +UniformColor +************ + +============ +UniformColor +============ +.. currentmodule:: fastplotlib.graphics._features + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: UniformColor_api + + UniformColor + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: UniformColor_api + + UniformColor.value + +Methods +~~~~~~~ +.. autosummary:: + :toctree: UniformColor_api + + UniformColor.add_event_handler + UniformColor.block_events + UniformColor.clear_event_handlers + UniformColor.remove_event_handler + UniformColor.set_value + diff --git a/docs/source/api/graphic_features/UniformSize.rst b/docs/source/api/graphic_features/UniformSize.rst new file mode 100644 index 000000000..e342d6a70 --- /dev/null +++ b/docs/source/api/graphic_features/UniformSize.rst @@ -0,0 +1,35 @@ +.. _api.UniformSize: + +UniformSize +*********** + +=========== +UniformSize +=========== +.. currentmodule:: fastplotlib.graphics._features + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: UniformSize_api + + UniformSize + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: UniformSize_api + + UniformSize.value + +Methods +~~~~~~~ +.. autosummary:: + :toctree: UniformSize_api + + UniformSize.add_event_handler + UniformSize.block_events + UniformSize.clear_event_handlers + UniformSize.remove_event_handler + UniformSize.set_value + diff --git a/docs/source/api/graphic_features/VertexCmap.rst b/docs/source/api/graphic_features/VertexCmap.rst new file mode 100644 index 000000000..a3311d6e6 --- /dev/null +++ b/docs/source/api/graphic_features/VertexCmap.rst @@ -0,0 +1,40 @@ +.. _api.VertexCmap: + +VertexCmap +********** + +========== +VertexCmap +========== +.. currentmodule:: fastplotlib.graphics._features + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: VertexCmap_api + + VertexCmap + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: VertexCmap_api + + VertexCmap.alpha + VertexCmap.buffer + VertexCmap.name + VertexCmap.shared + VertexCmap.transform + VertexCmap.value + +Methods +~~~~~~~ +.. autosummary:: + :toctree: VertexCmap_api + + VertexCmap.add_event_handler + VertexCmap.block_events + VertexCmap.clear_event_handlers + VertexCmap.remove_event_handler + VertexCmap.set_value + diff --git a/docs/source/api/graphic_features/VertexColors.rst b/docs/source/api/graphic_features/VertexColors.rst new file mode 100644 index 000000000..3c2089a78 --- /dev/null +++ b/docs/source/api/graphic_features/VertexColors.rst @@ -0,0 +1,37 @@ +.. _api.VertexColors: + +VertexColors +************ + +============ +VertexColors +============ +.. currentmodule:: fastplotlib.graphics._features + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: VertexColors_api + + VertexColors + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: VertexColors_api + + VertexColors.buffer + VertexColors.shared + VertexColors.value + +Methods +~~~~~~~ +.. autosummary:: + :toctree: VertexColors_api + + VertexColors.add_event_handler + VertexColors.block_events + VertexColors.clear_event_handlers + VertexColors.remove_event_handler + VertexColors.set_value + diff --git a/docs/source/api/graphic_features/VertexPositions.rst b/docs/source/api/graphic_features/VertexPositions.rst new file mode 100644 index 000000000..9669ab6d5 --- /dev/null +++ b/docs/source/api/graphic_features/VertexPositions.rst @@ -0,0 +1,37 @@ +.. _api.VertexPositions: + +VertexPositions +*************** + +=============== +VertexPositions +=============== +.. currentmodule:: fastplotlib.graphics._features + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: VertexPositions_api + + VertexPositions + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: VertexPositions_api + + VertexPositions.buffer + VertexPositions.shared + VertexPositions.value + +Methods +~~~~~~~ +.. autosummary:: + :toctree: VertexPositions_api + + VertexPositions.add_event_handler + VertexPositions.block_events + VertexPositions.clear_event_handlers + VertexPositions.remove_event_handler + VertexPositions.set_value + diff --git a/docs/source/api/graphic_features/Visible.rst b/docs/source/api/graphic_features/Visible.rst new file mode 100644 index 000000000..957b4433a --- /dev/null +++ b/docs/source/api/graphic_features/Visible.rst @@ -0,0 +1,35 @@ +.. _api.Visible: + +Visible +******* + +======= +Visible +======= +.. currentmodule:: fastplotlib.graphics._features + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: Visible_api + + Visible + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: Visible_api + + Visible.value + +Methods +~~~~~~~ +.. autosummary:: + :toctree: Visible_api + + Visible.add_event_handler + Visible.block_events + Visible.clear_event_handlers + Visible.remove_event_handler + Visible.set_value + diff --git a/docs/source/api/graphic_features/index.rst b/docs/source/api/graphic_features/index.rst index 06e3119e5..87504ea8a 100644 --- a/docs/source/api/graphic_features/index.rst +++ b/docs/source/api/graphic_features/index.rst @@ -4,20 +4,28 @@ Graphic Features .. toctree:: :maxdepth: 1 - ColorFeature - CmapFeature - ImageCmapFeature - HeatmapCmapFeature - PointsDataFeature + VertexColors + UniformColor + UniformSize + Thickness + VertexPositions PointsSizesFeature - ImageDataFeature - HeatmapDataFeature - PresentFeature - ThicknessFeature - GraphicFeature - GraphicFeatureIndexable - FeatureEvent - to_gpu_supported_dtype + VertexCmap + TextureArray + ImageCmap + ImageVmin + ImageVmax + ImageInterpolation + ImageCmapInterpolation + TextData + FontSize + TextFaceColor + TextOutlineColor + TextOutlineThickness LinearSelectionFeature LinearRegionSelectionFeature + Name + Offset + Rotation + Visible Deleted diff --git a/docs/source/api/graphic_features/to_gpu_supported_dtype.rst b/docs/source/api/graphic_features/to_gpu_supported_dtype.rst deleted file mode 100644 index 984a76157..000000000 --- a/docs/source/api/graphic_features/to_gpu_supported_dtype.rst +++ /dev/null @@ -1,29 +0,0 @@ -.. _api.to_gpu_supported_dtype: - -to_gpu_supported_dtype -********************** - -====================== -to_gpu_supported_dtype -====================== -.. currentmodule:: fastplotlib.graphics._features - -Constructor -~~~~~~~~~~~ -.. autosummary:: - :toctree: to_gpu_supported_dtype_api - - to_gpu_supported_dtype - -Properties -~~~~~~~~~~ -.. autosummary:: - :toctree: to_gpu_supported_dtype_api - - -Methods -~~~~~~~ -.. autosummary:: - :toctree: to_gpu_supported_dtype_api - - diff --git a/docs/source/api/graphics/HeatmapGraphic.rst b/docs/source/api/graphics/HeatmapGraphic.rst deleted file mode 100644 index ffa86eb16..000000000 --- a/docs/source/api/graphics/HeatmapGraphic.rst +++ /dev/null @@ -1,44 +0,0 @@ -.. _api.HeatmapGraphic: - -HeatmapGraphic -************** - -============== -HeatmapGraphic -============== -.. currentmodule:: fastplotlib - -Constructor -~~~~~~~~~~~ -.. autosummary:: - :toctree: HeatmapGraphic_api - - HeatmapGraphic - -Properties -~~~~~~~~~~ -.. autosummary:: - :toctree: HeatmapGraphic_api - - HeatmapGraphic.children - HeatmapGraphic.name - HeatmapGraphic.position - HeatmapGraphic.position_x - HeatmapGraphic.position_y - HeatmapGraphic.position_z - HeatmapGraphic.rotation - HeatmapGraphic.visible - HeatmapGraphic.world_object - -Methods -~~~~~~~ -.. autosummary:: - :toctree: HeatmapGraphic_api - - HeatmapGraphic.add_linear_region_selector - HeatmapGraphic.add_linear_selector - HeatmapGraphic.link - HeatmapGraphic.reset_feature - HeatmapGraphic.rotate - HeatmapGraphic.set_feature - diff --git a/docs/source/api/graphics/ImageGraphic.rst b/docs/source/api/graphics/ImageGraphic.rst index 00b27340d..a0ae8a5ed 100644 --- a/docs/source/api/graphics/ImageGraphic.rst +++ b/docs/source/api/graphics/ImageGraphic.rst @@ -20,14 +20,20 @@ Properties .. autosummary:: :toctree: ImageGraphic_api - ImageGraphic.children + ImageGraphic.block_events + ImageGraphic.cmap + ImageGraphic.cmap_interpolation + ImageGraphic.data + ImageGraphic.deleted + ImageGraphic.event_handlers + ImageGraphic.interpolation ImageGraphic.name - ImageGraphic.position - ImageGraphic.position_x - ImageGraphic.position_y - ImageGraphic.position_z + ImageGraphic.offset ImageGraphic.rotation + ImageGraphic.supported_events ImageGraphic.visible + ImageGraphic.vmax + ImageGraphic.vmin ImageGraphic.world_object Methods @@ -35,10 +41,13 @@ Methods .. autosummary:: :toctree: ImageGraphic_api + ImageGraphic.add_event_handler ImageGraphic.add_linear_region_selector ImageGraphic.add_linear_selector - ImageGraphic.link - ImageGraphic.reset_feature + ImageGraphic.clear_event_handlers + ImageGraphic.remove_event_handler + ImageGraphic.reset_vmin_vmax ImageGraphic.rotate - ImageGraphic.set_feature + ImageGraphic.share_property + ImageGraphic.unshare_property diff --git a/docs/source/api/graphics/LineCollection.rst b/docs/source/api/graphics/LineCollection.rst index 8d10d8376..c000b7334 100644 --- a/docs/source/api/graphics/LineCollection.rst +++ b/docs/source/api/graphics/LineCollection.rst @@ -20,17 +20,24 @@ Properties .. autosummary:: :toctree: LineCollection_api - LineCollection.children + LineCollection.block_events LineCollection.cmap - LineCollection.cmap_values + LineCollection.colors + LineCollection.data + LineCollection.deleted + LineCollection.event_handlers LineCollection.graphics + LineCollection.metadatas LineCollection.name - LineCollection.position - LineCollection.position_x - LineCollection.position_y - LineCollection.position_z + LineCollection.names + LineCollection.offset + LineCollection.offsets LineCollection.rotation + LineCollection.rotations + LineCollection.supported_events + LineCollection.thickness LineCollection.visible + LineCollection.visibles LineCollection.world_object Methods @@ -38,12 +45,14 @@ Methods .. autosummary:: :toctree: LineCollection_api + LineCollection.add_event_handler LineCollection.add_graphic LineCollection.add_linear_region_selector LineCollection.add_linear_selector - LineCollection.link + LineCollection.clear_event_handlers + LineCollection.remove_event_handler LineCollection.remove_graphic - LineCollection.reset_feature LineCollection.rotate - LineCollection.set_feature + LineCollection.share_property + LineCollection.unshare_property diff --git a/docs/source/api/graphics/LineGraphic.rst b/docs/source/api/graphics/LineGraphic.rst index 8b6fedf22..d260c3214 100644 --- a/docs/source/api/graphics/LineGraphic.rst +++ b/docs/source/api/graphics/LineGraphic.rst @@ -20,13 +20,17 @@ Properties .. autosummary:: :toctree: LineGraphic_api - LineGraphic.children + LineGraphic.block_events + LineGraphic.cmap + LineGraphic.colors + LineGraphic.data + LineGraphic.deleted + LineGraphic.event_handlers LineGraphic.name - LineGraphic.position - LineGraphic.position_x - LineGraphic.position_y - LineGraphic.position_z + LineGraphic.offset LineGraphic.rotation + LineGraphic.supported_events + LineGraphic.thickness LineGraphic.visible LineGraphic.world_object @@ -35,10 +39,12 @@ Methods .. autosummary:: :toctree: LineGraphic_api + LineGraphic.add_event_handler LineGraphic.add_linear_region_selector LineGraphic.add_linear_selector - LineGraphic.link - LineGraphic.reset_feature + LineGraphic.clear_event_handlers + LineGraphic.remove_event_handler LineGraphic.rotate - LineGraphic.set_feature + LineGraphic.share_property + LineGraphic.unshare_property diff --git a/docs/source/api/graphics/LineStack.rst b/docs/source/api/graphics/LineStack.rst index a39db46f8..18b35932d 100644 --- a/docs/source/api/graphics/LineStack.rst +++ b/docs/source/api/graphics/LineStack.rst @@ -20,17 +20,24 @@ Properties .. autosummary:: :toctree: LineStack_api - LineStack.children + LineStack.block_events LineStack.cmap - LineStack.cmap_values + LineStack.colors + LineStack.data + LineStack.deleted + LineStack.event_handlers LineStack.graphics + LineStack.metadatas LineStack.name - LineStack.position - LineStack.position_x - LineStack.position_y - LineStack.position_z + LineStack.names + LineStack.offset + LineStack.offsets LineStack.rotation + LineStack.rotations + LineStack.supported_events + LineStack.thickness LineStack.visible + LineStack.visibles LineStack.world_object Methods @@ -38,12 +45,14 @@ Methods .. autosummary:: :toctree: LineStack_api + LineStack.add_event_handler LineStack.add_graphic LineStack.add_linear_region_selector LineStack.add_linear_selector - LineStack.link + LineStack.clear_event_handlers + LineStack.remove_event_handler LineStack.remove_graphic - LineStack.reset_feature LineStack.rotate - LineStack.set_feature + LineStack.share_property + LineStack.unshare_property diff --git a/docs/source/api/graphics/ScatterGraphic.rst b/docs/source/api/graphics/ScatterGraphic.rst index 44d87d008..8f2b17fd6 100644 --- a/docs/source/api/graphics/ScatterGraphic.rst +++ b/docs/source/api/graphics/ScatterGraphic.rst @@ -20,13 +20,17 @@ Properties .. autosummary:: :toctree: ScatterGraphic_api - ScatterGraphic.children + ScatterGraphic.block_events + ScatterGraphic.cmap + ScatterGraphic.colors + ScatterGraphic.data + ScatterGraphic.deleted + ScatterGraphic.event_handlers ScatterGraphic.name - ScatterGraphic.position - ScatterGraphic.position_x - ScatterGraphic.position_y - ScatterGraphic.position_z + ScatterGraphic.offset ScatterGraphic.rotation + ScatterGraphic.sizes + ScatterGraphic.supported_events ScatterGraphic.visible ScatterGraphic.world_object @@ -35,5 +39,10 @@ Methods .. autosummary:: :toctree: ScatterGraphic_api + ScatterGraphic.add_event_handler + ScatterGraphic.clear_event_handlers + ScatterGraphic.remove_event_handler ScatterGraphic.rotate + ScatterGraphic.share_property + ScatterGraphic.unshare_property diff --git a/docs/source/api/graphics/TextGraphic.rst b/docs/source/api/graphics/TextGraphic.rst index 23425cf41..a3cd9bbb9 100644 --- a/docs/source/api/graphics/TextGraphic.rst +++ b/docs/source/api/graphics/TextGraphic.rst @@ -20,18 +20,18 @@ Properties .. autosummary:: :toctree: TextGraphic_api - TextGraphic.children + TextGraphic.block_events + TextGraphic.deleted + TextGraphic.event_handlers TextGraphic.face_color + TextGraphic.font_size TextGraphic.name + TextGraphic.offset TextGraphic.outline_color - TextGraphic.outline_size - TextGraphic.position - TextGraphic.position_x - TextGraphic.position_y - TextGraphic.position_z + TextGraphic.outline_thickness TextGraphic.rotation + TextGraphic.supported_events TextGraphic.text - TextGraphic.text_size TextGraphic.visible TextGraphic.world_object @@ -40,5 +40,10 @@ Methods .. autosummary:: :toctree: TextGraphic_api + TextGraphic.add_event_handler + TextGraphic.clear_event_handlers + TextGraphic.remove_event_handler TextGraphic.rotate + TextGraphic.share_property + TextGraphic.unshare_property diff --git a/docs/source/api/graphics/index.rst b/docs/source/api/graphics/index.rst index 611ee5833..b64ac53c0 100644 --- a/docs/source/api/graphics/index.rst +++ b/docs/source/api/graphics/index.rst @@ -4,10 +4,9 @@ Graphics .. toctree:: :maxdepth: 1 + LineGraphic ImageGraphic ScatterGraphic - LineGraphic - HeatmapGraphic + TextGraphic LineCollection LineStack - TextGraphic diff --git a/docs/source/api/layouts/subplot.rst b/docs/source/api/layouts/subplot.rst index 91884557a..61f5da307 100644 --- a/docs/source/api/layouts/subplot.rst +++ b/docs/source/api/layouts/subplot.rst @@ -42,7 +42,6 @@ Methods Subplot.add_animations Subplot.add_graphic - Subplot.add_heatmap Subplot.add_image Subplot.add_line Subplot.add_line_collection diff --git a/docs/source/api/selectors/LinearRegionSelector.rst b/docs/source/api/selectors/LinearRegionSelector.rst index 1b59e80c9..c9140bc7d 100644 --- a/docs/source/api/selectors/LinearRegionSelector.rst +++ b/docs/source/api/selectors/LinearRegionSelector.rst @@ -20,14 +20,17 @@ Properties .. autosummary:: :toctree: LinearRegionSelector_api - LinearRegionSelector.children + LinearRegionSelector.axis + LinearRegionSelector.block_events + LinearRegionSelector.deleted + LinearRegionSelector.event_handlers LinearRegionSelector.limits LinearRegionSelector.name - LinearRegionSelector.position - LinearRegionSelector.position_x - LinearRegionSelector.position_y - LinearRegionSelector.position_z + LinearRegionSelector.offset + LinearRegionSelector.parent LinearRegionSelector.rotation + LinearRegionSelector.selection + LinearRegionSelector.supported_events LinearRegionSelector.visible LinearRegionSelector.world_object @@ -36,10 +39,15 @@ Methods .. autosummary:: :toctree: LinearRegionSelector_api + LinearRegionSelector.add_event_handler LinearRegionSelector.add_ipywidget_handler + LinearRegionSelector.clear_event_handlers LinearRegionSelector.get_selected_data LinearRegionSelector.get_selected_index LinearRegionSelector.get_selected_indices LinearRegionSelector.make_ipywidget_slider + LinearRegionSelector.remove_event_handler LinearRegionSelector.rotate + LinearRegionSelector.share_property + LinearRegionSelector.unshare_property diff --git a/docs/source/api/selectors/LinearSelector.rst b/docs/source/api/selectors/LinearSelector.rst index 3278559d0..fa21f8f15 100644 --- a/docs/source/api/selectors/LinearSelector.rst +++ b/docs/source/api/selectors/LinearSelector.rst @@ -20,14 +20,17 @@ Properties .. autosummary:: :toctree: LinearSelector_api - LinearSelector.children + LinearSelector.axis + LinearSelector.block_events + LinearSelector.deleted + LinearSelector.event_handlers LinearSelector.limits LinearSelector.name - LinearSelector.position - LinearSelector.position_x - LinearSelector.position_y - LinearSelector.position_z + LinearSelector.offset + LinearSelector.parent LinearSelector.rotation + LinearSelector.selection + LinearSelector.supported_events LinearSelector.visible LinearSelector.world_object @@ -36,10 +39,15 @@ Methods .. autosummary:: :toctree: LinearSelector_api + LinearSelector.add_event_handler LinearSelector.add_ipywidget_handler + LinearSelector.clear_event_handlers LinearSelector.get_selected_data LinearSelector.get_selected_index LinearSelector.get_selected_indices LinearSelector.make_ipywidget_slider + LinearSelector.remove_event_handler LinearSelector.rotate + LinearSelector.share_property + LinearSelector.unshare_property diff --git a/docs/source/api/selectors/PolygonSelector.rst b/docs/source/api/selectors/PolygonSelector.rst deleted file mode 100644 index 8de87ec74..000000000 --- a/docs/source/api/selectors/PolygonSelector.rst +++ /dev/null @@ -1,43 +0,0 @@ -.. _api.PolygonSelector: - -PolygonSelector -*************** - -=============== -PolygonSelector -=============== -.. currentmodule:: fastplotlib - -Constructor -~~~~~~~~~~~ -.. autosummary:: - :toctree: PolygonSelector_api - - PolygonSelector - -Properties -~~~~~~~~~~ -.. autosummary:: - :toctree: PolygonSelector_api - - PolygonSelector.children - PolygonSelector.name - PolygonSelector.position - PolygonSelector.position_x - PolygonSelector.position_y - PolygonSelector.position_z - PolygonSelector.rotation - PolygonSelector.visible - PolygonSelector.world_object - -Methods -~~~~~~~ -.. autosummary:: - :toctree: PolygonSelector_api - - PolygonSelector.get_selected_data - PolygonSelector.get_selected_index - PolygonSelector.get_selected_indices - PolygonSelector.get_vertices - PolygonSelector.rotate - diff --git a/docs/source/api/selectors/Synchronizer.rst b/docs/source/api/selectors/Synchronizer.rst deleted file mode 100644 index 2b28fe351..000000000 --- a/docs/source/api/selectors/Synchronizer.rst +++ /dev/null @@ -1,33 +0,0 @@ -.. _api.Synchronizer: - -Synchronizer -************ - -============ -Synchronizer -============ -.. currentmodule:: fastplotlib - -Constructor -~~~~~~~~~~~ -.. autosummary:: - :toctree: Synchronizer_api - - Synchronizer - -Properties -~~~~~~~~~~ -.. autosummary:: - :toctree: Synchronizer_api - - Synchronizer.selectors - -Methods -~~~~~~~ -.. autosummary:: - :toctree: Synchronizer_api - - Synchronizer.add - Synchronizer.clear - Synchronizer.remove - diff --git a/docs/source/api/selectors/index.rst b/docs/source/api/selectors/index.rst index 01c040728..ffa4054db 100644 --- a/docs/source/api/selectors/index.rst +++ b/docs/source/api/selectors/index.rst @@ -6,5 +6,3 @@ Selectors LinearSelector LinearRegionSelector - PolygonSelector - Synchronizer diff --git a/docs/source/conf.py b/docs/source/conf.py index f681a8101..38133c901 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -23,7 +23,6 @@ "sphinx.ext.viewcode", "sphinx_copybutton", "sphinx_design", - "nbsphinx", ] autosummary_generate = True @@ -52,7 +51,7 @@ intersphinx_mapping = { "python": ("https://docs.python.org/3", None), "numpy": ("https://numpy.org/doc/stable/", None), - "pygfx": ("https://pygfx.readthedocs.io/en/latest", None), + "pygfx": ("https://pygfx.com/latest", None), "wgpu": ("https://wgpu-py.readthedocs.io/en/latest", None), } diff --git a/docs/source/generate_api.py b/docs/source/generate_api.py index a5f668130..0150836ec 100644 --- a/docs/source/generate_api.py +++ b/docs/source/generate_api.py @@ -263,6 +263,12 @@ def main(): with open(API_DIR.joinpath("utils.rst"), "w") as f: f.write(utils_str) + # gpu selection + fpl_functions = generate_functions_module(fastplotlib, "fastplotlib.utils.gpu") + + with open(API_DIR.joinpath("gpu.rst"), "w") as f: + f.write(fpl_functions) + if __name__ == "__main__": main() diff --git a/docs/source/index.rst b/docs/source/index.rst index 0bca839b9..e99e38c52 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -1,12 +1,6 @@ Welcome to fastplotlib's documentation! ======================================= -.. toctree:: - :caption: Quick Start - :maxdepth: 2 - - quickstart - .. toctree:: :caption: User Guide :maxdepth: 2 diff --git a/docs/source/quickstart.ipynb b/docs/source/quickstart.ipynb deleted file mode 100644 index 6a892399e..000000000 --- a/docs/source/quickstart.ipynb +++ /dev/null @@ -1,1673 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "93740a09-9111-4777-ad57-173e9b80a2f0", - "metadata": { - "tags": [] - }, - "source": [ - "# Quick Start Guide 🚀\n", - "\n", - "This notebook goes through the basic components of the `fastplotlib` API, images, image updates, line plots, scatter plots, and grid plots.\n", - "\n", - "**NOTE: This quick start guide in the docs is NOT interactive. Download the examples from the repo and try them on your own computer. You can run the desktop examples directly if you have `glfw` installed, or try the notebook demos:** https://github.com/kushalkolar/fastplotlib/tree/master/examples\n", - "\n", - "It will not be possible to have live demos on the docs until someone can figure out how to get [pygfx](https://github.com/pygfx/pygfx) to work with `wgpu` in the browser, perhaps through [pyodide](https://github.com/pyodide/pyodide) or something :D." - ] - }, - { - "cell_type": "markdown", - "id": "5d21c330-89cd-49ab-9069-4e3652d4286b", - "metadata": {}, - "source": [ - "**The example images are from `imageio` so you will need to install it for this example notebook. But `imageio` is not required to use `fasptlotlib`**" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "07f064bb-025a-4794-9b05-243810edaf60", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "!pip install imageio" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5f842366-bd39-47de-ad00-723b2be707e4", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "import imageio.v3 as iio" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fb57c3d3-f20d-4d88-9e7a-04b9309bc637", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "import fastplotlib as fpl\n", - "from ipywidgets import VBox, HBox, IntSlider\n", - "import numpy as np" - ] - }, - { - "cell_type": "markdown", - "id": "a9b386ac-9218-4f8f-97b3-f29b4201ef55", - "metadata": {}, - "source": [ - "## Images" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "237823b7-e2c0-4e2f-9ee8-e3fc2b4453c4", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# create a `Figure` instance\n", - "fig = fpl.Figure()\n", - "\n", - "# get a grayscale image\n", - "data = iio.imread(\"imageio:camera.png\")\n", - "\n", - "# plot the image data\n", - "image_graphic = fig[0, 0].add_image(data=data, name=\"sample-image\")\n", - "\n", - "# show the plot\n", - "fig.show()" - ] - }, - { - "cell_type": "markdown", - "id": "be5b408f-dd91-4e36-807a-8c22c8d7d216", - "metadata": {}, - "source": [ - "**In live notebooks or desktop applications, you can use the handle on the bottom right corner of the _canvas_ to resize it. You can also pan and zoom using your mouse!**" - ] - }, - { - "cell_type": "markdown", - "id": "9ba07ec1-a0cb-4461-87c6-c7b64d4a882b", - "metadata": {}, - "source": [ - "This is how you can take a snapshot of the canvas. Snapshots are shown throughout this doc page for the purposes of documentation, they are NOT necessary for real interactive usage. Download the notebooks to run live demos." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b64ba135-e753-43a9-ad1f-adcc7310792d", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig.canvas.snapshot()" - ] - }, - { - "cell_type": "markdown", - "id": "ac5f5e75-9aa4-441f-9a41-66c22cd53de8", - "metadata": {}, - "source": [ - "Changing graphic **\"features\"**" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d3541d1d-0819-450e-814c-588ffc8e7ed5", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "image_graphic.cmap = \"viridis\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ab544719-9187-45bd-8127-aac79eea30e4", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig.canvas.snapshot()" - ] - }, - { - "cell_type": "markdown", - "id": "9693cf94-11e9-46a6-a5b7-b0fbed42ad81", - "metadata": {}, - "source": [ - "### Slicing data\n", - "\n", - "**Most features, such as `data` support slicing!**\n", - "\n", - "Out image data is of shape [n_rows, n_cols]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "330a47b5-50b1-4e6a-b8ab-d55d92af2042", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "image_graphic.data().shape" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "601f46d9-7f32-4a43-9090-4674218800ea", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "image_graphic.data[::15, :] = 1\n", - "image_graphic.data[:, ::15] = 1" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3443948f-9ac9-484a-a4bf-3a06c1ce5658", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig.canvas.snapshot()" - ] - }, - { - "cell_type": "markdown", - "id": "53125b3b-3ce2-43c5-b2e3-7cd37cec7d7d", - "metadata": {}, - "source": [ - "**Fancy indexing**" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7344cbbe-40c3-4d9e-ae75-7abe3ddaeeeb", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "image_graphic.data[data > 175] = 255" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ef113d79-5d86-4be0-868e-30f82f8ab528", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig.canvas.snapshot()" - ] - }, - { - "cell_type": "markdown", - "id": "4df5296e-2a18-403f-82f1-acb8eaf280e3", - "metadata": {}, - "source": [ - "Adjust vmin vmax" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "28af88d1-0518-47a4-ab73-431d6aaf9cb8", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "image_graphic.cmap.vmin = 50\n", - "image_graphic.cmap.vmax = 150" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e3dfb827-c812-447d-b413-dc15653160b1", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig.canvas.snapshot()" - ] - }, - { - "cell_type": "markdown", - "id": "19a1b56b-fdca-40c5-91c9-3c9486fd8a21", - "metadata": {}, - "source": [ - "**Set the entire data array again**\n", - "\n", - "Note: The shape of the new data array must match the current data shown in the Graphic." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4dc3d0e4-b128-42cd-a53e-76846fc9b8a8", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "new_data = iio.imread(\"imageio:astronaut.png\")\n", - "new_data.shape" - ] - }, - { - "cell_type": "markdown", - "id": "3bd06068-fe3f-404d-ba4a-a72a2105904f", - "metadata": {}, - "source": [ - "This is an RGB image, convert to grayscale to maintain the shape of (512, 512)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "150047a6-a6ac-442d-a468-3e0c224a2b7e", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "gray = new_data.dot([0.3, 0.6, 0.1])\n", - "gray.shape" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bf24576b-d336-4754-9992-9649ccaa4d1e", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "image_graphic.data = gray" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "67d810c2-4020-4769-a5ba-0d4a972ee243", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig.canvas.snapshot()" - ] - }, - { - "cell_type": "markdown", - "id": "2fe82654-e554-4be6-92a0-ecdee0ef8519", - "metadata": {}, - "source": [ - "reset vmin vmax" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0be6e4bb-cf9a-4155-9f6a-8106e66e6132", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "image_graphic.cmap.reset_vmin_vmax()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bd51936c-ad80-4b33-b855-23565265a430", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig.canvas.snapshot()" - ] - }, - { - "cell_type": "markdown", - "id": "a6c1f3fb-a3a7-4175-bd8d-bb3203740771", - "metadata": {}, - "source": [ - "### Indexing plots" - ] - }, - { - "cell_type": "markdown", - "id": "3fc38694-aca6-4f56-97ac-3435059a6be7", - "metadata": {}, - "source": [ - "**Plots are indexable and give you their graphics by name**" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8a547138-0f7d-470b-9925-8df479c3979e", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5551861f-9860-4515-8222-2f1c6d6a3220", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig[0, 0][\"sample-image\"]" - ] - }, - { - "cell_type": "markdown", - "id": "0c29b36e-0eb4-4bb3-a8db-add58c303ee8", - "metadata": {}, - "source": [ - "**You can also use numerical indexing on `plot.graphics`**" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ce6adbb0-078a-4e74-b189-58f860ee5df5", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig[0, 0].graphics" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "119bd6af-c486-4378-bc23-79b1759aa3a4", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig[0, 0].graphics[0]" - ] - }, - { - "cell_type": "markdown", - "id": "6b8e3f0d-56f8-447f-bf26-b52629d06e95", - "metadata": {}, - "source": [ - "The `Graphic` instance is also returned when you call `plot.add_`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "967c0cbd-287c-4d99-9891-9baf18f7b56a", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "image_graphic" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5da72e26-3536-47b8-839c-53452dd94f7a", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "image_graphic is fig[0, 0][\"sample-image\"]" - ] - }, - { - "cell_type": "markdown", - "id": "2b5ee18b-e61b-415d-902a-688b1c9c03b8", - "metadata": {}, - "source": [ - "### RGB images\n", - "\n", - "`cmap` arguments are ignored for rgb images, but vmin vmax still works" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1f7143ec-8ee1-47d2-b017-d0a8efc69fc6", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig_rgb = fpl.Figure()\n", - "\n", - "fig_rgb[0, 0].add_image(new_data, name=\"rgb-image\")\n", - "\n", - "fig_rgb.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a47b1eaf-3638-470a-88a5-0026c81d7e2b", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig_rgb.canvas.snapshot()" - ] - }, - { - "cell_type": "markdown", - "id": "4848a929-4f3b-46d7-921b-ebfe8de0ebb5", - "metadata": {}, - "source": [ - "vmin and vmax are still applicable to rgb images" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ffe50132-8dd0-433c-b9c6-9ead8c3d48de", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig_rgb[0, 0][\"rgb-image\"].cmap.vmin = 100" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "161468ba-b836-4021-8d11-8dfc140b94eb", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig_rgb.canvas.snapshot()" - ] - }, - { - "cell_type": "markdown", - "id": "1cb03f42-1029-4b16-a16b-35447d9e2955", - "metadata": { - "tags": [] - }, - "source": [ - "## Image updates\n", - "\n", - "This examples show how you can define animation functions that run on every render cycle." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "aadd757f-6379-4f52-a709-46aa57c56216", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# create another `Figure` instance\n", - "fig_vid = fpl.Figure()\n", - "\n", - "# make some random data again\n", - "data = np.random.rand(512, 512)\n", - "\n", - "# plot the data\n", - "fig_vid[0, 0].add_image(data=data, name=\"random-image\")\n", - "\n", - "# a function to update the image_graphic\n", - "# a subplot will pass its instance to the animation function as an argument\n", - "def update_data(subplot):\n", - " new_data = np.random.rand(512, 512)\n", - " subplot[\"random-image\"].data = new_data\n", - "\n", - "#add this as an animation function to the subplot\n", - "fig_vid[0, 0].add_animations(update_data)\n", - "\n", - "# show the plot\n", - "fig_vid.show()" - ] - }, - { - "cell_type": "markdown", - "id": "b313eda1-6e6c-466f-9fd5-8b70c1d3c110", - "metadata": {}, - "source": [ - "**Share controllers across plots**\n", - "\n", - "This example creates a new plot, but it synchronizes the pan-zoom controller" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "86e70b1e-4328-4035-b992-70dff16d2a69", - "metadata": {}, - "outputs": [], - "source": [ - "fig_sync = fpl.Figure(controllers=fig_vid.controllers)\n", - "\n", - "data = np.random.rand(512, 512)\n", - "\n", - "image_graphic_instance = fig_sync[0, 0].add_image(data=data, cmap=\"viridis\")\n", - "\n", - "# you will need to define a new animation function for this graphic\n", - "def update_data_2():\n", - " new_data = np.random.rand(512, 512)\n", - " # alternatively, you can use the stored reference to the graphic as well instead of indexing the Plot\n", - " image_graphic_instance.data = new_data\n", - "\n", - "# add the animation function to the figure instead of the subplot\n", - "fig_sync.add_animations(update_data_2)\n", - "\n", - "fig_sync.show()" - ] - }, - { - "cell_type": "markdown", - "id": "f226c9c2-8d0e-41ab-9ab9-1ae31fd91de5", - "metadata": {}, - "source": [ - "Keeping a reference to the Graphic instance, as shown above `image_graphic_instance`, is useful if you're creating something where you need flexibility in the naming of the graphics" - ] - }, - { - "cell_type": "markdown", - "id": "d11fabb7-7c76-4e94-893d-80ed9ee3be3d", - "metadata": {}, - "source": [ - "You can also use `ipywidgets.VBox` and `HBox` to stack plots. See the `gridplot` notebooks for a proper gridplot interface for more automated subplotting\n", - "\n", - "Not shown in the docs, try the live demo for this feature" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ef9743b3-5f81-4b79-9502-fa5fca08e56d", - "metadata": {}, - "outputs": [], - "source": [ - "#VBox([plot_v.canvas, plot_sync.show()])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "11839d95-8ff7-444c-ae13-6b072c3112c5", - "metadata": {}, - "outputs": [], - "source": [ - "#HBox([plot_v.show(), plot_sync.show()])" - ] - }, - { - "cell_type": "markdown", - "id": "e7859338-8162-408b-ac72-37e606057045", - "metadata": { - "tags": [] - }, - "source": [ - "## Line plots\n", - "\n", - "2D line plots\n", - "\n", - "This example plots a sine wave, cosine wave, and ricker wavelet and demonstrates how **Graphic Features** can be modified by slicing!" - ] - }, - { - "cell_type": "markdown", - "id": "a6fee1c2-4a24-4325-bca2-26e5a4bf6338", - "metadata": {}, - "source": [ - "Generate some data." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8e8280da-b421-43a5-a1a6-2a196a408e9a", - "metadata": {}, - "outputs": [], - "source": [ - "# linspace, create 100 evenly spaced x values from -10 to 10\n", - "xs = np.linspace(-10, 10, 100)\n", - "# sine wave\n", - "ys = np.sin(xs)\n", - "sine = np.dstack([xs, ys])[0]\n", - "\n", - "# cosine wave\n", - "ys = np.cos(xs) + 5\n", - "cosine = np.dstack([xs, ys])[0]\n", - "\n", - "# sinc function\n", - "a = 0.5\n", - "ys = np.sinc(xs) * 3 + 8\n", - "sinc = np.dstack([xs, ys])[0]" - ] - }, - { - "cell_type": "markdown", - "id": "fbb806e5-1565-4189-936c-b7cf147a59ee", - "metadata": {}, - "source": [ - "Plot all of it on the same plot. Each line plot will be an individual Graphic, you can have any combination of graphics on a plot." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "93a5d1e6-d019-4dd0-a0d1-25d1704ab7a7", - "metadata": {}, - "outputs": [], - "source": [ - "# Create a plot instance\n", - "fig_line = fpl.Figure()\n", - "\n", - "# plot sine wave, use a single color\n", - "sine_graphic = fig_line[0, 0].add_line(data=sine, thickness=5, colors=\"magenta\")\n", - "\n", - "# you can also use colormaps for lines!\n", - "cosine_graphic = fig_line[0, 0].add_line(data=cosine, thickness=12, cmap=\"autumn\")\n", - "\n", - "# or a list of colors for each datapoint\n", - "colors = [\"r\"] * 25 + [\"purple\"] * 25 + [\"y\"] * 25 + [\"b\"] * 25\n", - "sinc_graphic = fig_line[0, 0].add_line(data=sinc, thickness=5, colors = colors)\n", - "\n", - "fig_line.show()" - ] - }, - { - "cell_type": "markdown", - "id": "22dde600-0f56-4370-b017-c8f23a6c01aa", - "metadata": {}, - "source": [ - "\"stretching\" the camera, useful for large timeseries data\n", - "\n", - "Set `maintain_aspect = False` on a camera, and then use the right mouse button and move the mouse to stretch and squeeze the view!\n", - "\n", - "You can also click the **`1:1`** button to toggle this." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2695f023-f6ce-4e26-8f96-4fbed5510d1d", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig_line[0, 0].camera.maintain_aspect = False" - ] - }, - { - "cell_type": "markdown", - "id": "1651e965-f750-47ac-bf53-c23dae84cc98", - "metadata": {}, - "source": [ - "reset the plot area" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ba50a6ed-0f1b-4795-91dd-a7c3e40b8e3c", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig_line[0, 0].auto_scale(maintain_aspect=True)" - ] - }, - { - "cell_type": "markdown", - "id": "dcd68796-c190-4c3f-8519-d73b98ff6367", - "metadata": {}, - "source": [ - "Graphic features support slicing! :D " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "cb0d13ed-ef07-46ff-b19e-eeca4c831037", - "metadata": {}, - "outputs": [], - "source": [ - "# indexing of colors\n", - "cosine_graphic.colors[:15] = \"magenta\"\n", - "cosine_graphic.colors[90:] = \"red\"\n", - "cosine_graphic.colors[60] = \"w\"\n", - "\n", - "# indexing to assign colormaps to entire lines or segments\n", - "sinc_graphic.cmap[10:50] = \"gray\"\n", - "sine_graphic.cmap = \"seismic\"\n", - "\n", - "# more complex indexing, set the blue value directly from an array\n", - "cosine_graphic.colors[65:90, 0] = np.linspace(0, 1, 90-65)" - ] - }, - { - "cell_type": "markdown", - "id": "bfe14ed3-e81f-4058-96a7-e2720b6d2f45", - "metadata": {}, - "source": [ - "Make a snapshot of the canvas after slicing" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a061a888-d732-406e-a9c2-8cc632fbc368", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig_line.canvas.snapshot()" - ] - }, - { - "cell_type": "markdown", - "id": "c9689887-cdf3-4a4d-948f-7efdb09bde4e", - "metadata": {}, - "source": [ - "**You can capture changes to a graphic feature as events**" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "cfa001f6-c640-4f91-beb0-c19b030e503f", - "metadata": {}, - "outputs": [], - "source": [ - "def callback_func(event_data):\n", - " print(event_data)\n", - "\n", - "# Will print event data when the color changes\n", - "cosine_graphic.colors.add_event_handler(callback_func)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bb8a0f95-0063-4cd4-a117-e3d62c6e120d", - "metadata": {}, - "outputs": [], - "source": [ - "# more complex indexing of colors\n", - "# from point 15 - 30, set every 3rd point as \"cyan\"\n", - "cosine_graphic.colors[15:50:3] = \"cyan\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3da9a43b-35bd-4b56-9cc7-967536aac967", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig_line.canvas.snapshot()" - ] - }, - { - "cell_type": "markdown", - "id": "c29f81f9-601b-49f4-b20c-575c56e58026", - "metadata": {}, - "source": [ - "Graphic `data` is also indexable" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d1a4314b-5723-43c7-94a0-b4cbb0e44d60", - "metadata": {}, - "outputs": [], - "source": [ - "cosine_graphic.data[10:50:5, :2] = sine[10:50:5]\n", - "cosine_graphic.data[90:, 1] = 7" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "682db47b-8c7a-4934-9be4-2067e9fb12d5", - "metadata": {}, - "outputs": [], - "source": [ - "cosine_graphic.data[0] = np.array([[-10, 0, 0]])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f779cba0-7ee2-4795-8da8-9a9593d3893e", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig_line.canvas.snapshot()" - ] - }, - { - "cell_type": "markdown", - "id": "3f6d264b-1b03-407e-9d83-cd6cfb02e706", - "metadata": {}, - "source": [ - "Toggle the presence of a graphic within the scene" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fcba75b7-9a1e-4aae-9dec-715f7f7456c3", - "metadata": {}, - "outputs": [], - "source": [ - "sinc_graphic.present = False" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a5e22d0f-a244-47e2-9a2d-1eaf79eda1d9", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig_line.canvas.snapshot()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "763b9943-53a4-4e2a-b47a-4e9e5c9d7be3", - "metadata": {}, - "outputs": [], - "source": [ - "sinc_graphic.present = True" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b22a8660-26b3-4c73-b87a-df9d7cb4353a", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig_line.canvas.snapshot()" - ] - }, - { - "cell_type": "markdown", - "id": "86f4e535-ce88-415a-b8d2-53612a2de7b9", - "metadata": {}, - "source": [ - "You can create callbacks to `present` too, for example to re-scale the plot w.r.t. graphics that are present in the scene" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "64a20a16-75a5-4772-a849-630ade9be4ff", - "metadata": {}, - "outputs": [], - "source": [ - "sinc_graphic.present.add_event_handler(fig_line[0, 0].auto_scale)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fb093046-c94c-4085-86b4-8cd85cb638ff", - "metadata": {}, - "outputs": [], - "source": [ - "sinc_graphic.present = False" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f9dd6a54-3460-4fb7-bffb-82fd9288902f", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig_line.canvas.snapshot()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f05981c3-c768-4631-ae62-6a8407b20c4c", - "metadata": {}, - "outputs": [], - "source": [ - "sinc_graphic.present = True" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "cb5bf73e-b015-4b4f-82a0-c3ae8cc39ef7", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig_line.canvas.snapshot()" - ] - }, - { - "cell_type": "markdown", - "id": "05f93e93-283b-45d8-ab31-8d15a7671dd2", - "metadata": {}, - "source": [ - "You can set the z-positions of graphics to have them appear under or over other graphics" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6bb33406-5bef-455b-86ea-358a7d3ffa94", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "img = np.random.rand(20, 100)\n", - "\n", - "fig_line[0, 0].add_image(img, name=\"image\", cmap=\"gray\")\n", - "\n", - "# z axis position -1 so it is below all the lines\n", - "fig_line[0, 0][\"image\"].position_z = -1\n", - "fig_line[0, 0][\"image\"].position_x = -50" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5b586a89-ca3e-4e88-a801-bdd665384f59", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig_line.canvas.snapshot()" - ] - }, - { - "cell_type": "markdown", - "id": "2c90862e-2f2a-451f-a468-0cf6b857e87a", - "metadata": {}, - "source": [ - "### 3D line plot" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9c51229f-13a2-4653-bff3-15d43ddbca7b", - "metadata": {}, - "outputs": [], - "source": [ - "# just set the camera as \"3d\", the rest is basically the same :D \n", - "fig_line_3d = fpl.Figure(cameras='3d')\n", - "\n", - "# create a spiral\n", - "phi = np.linspace(0, 30, 200)\n", - "\n", - "xs = phi * np.cos(phi)\n", - "ys = phi * np.sin(phi)\n", - "zs = phi\n", - "\n", - "# use 3D data\n", - "# note: you usually mix 3D and 2D graphics on the same plot\n", - "spiral = np.dstack([xs, ys, zs])[0]\n", - "\n", - "fig_line_3d[0, 0].add_line(data=spiral, thickness=2, cmap='winter')\n", - "\n", - "fig_line_3d.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "28eb7014-4773-4a34-8bfc-bd3a46429012", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig_line_3d[0, 0].auto_scale(maintain_aspect=True)" - ] - }, - { - "cell_type": "markdown", - "id": "a202b3d0-2a0b-450a-93d4-76d0a1129d1d", - "metadata": {}, - "source": [ - "## Scatter plots\n", - "\n", - "Plot tens of thousands or millions of points\n", - "\n", - "There might be a small delay for a few seconds before the plot shows, this is due to shaders being compiled and a few other things. The plot should be very fast and responsive once it is displayed and future modifications should also be fast!" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "39252df5-9ae5-4132-b97b-2785c5fa92ea", - "metadata": {}, - "outputs": [], - "source": [ - "# create a random distribution\n", - "# only 1,000 points shown here in the docs, but it can be millions\n", - "n_points = 1_000\n", - "\n", - "# if you have a good GPU go for 1.5 million points :D \n", - "# this is multiplied by 3\n", - "#n_points = 500_000\n", - "\n", - "# dimensions always have to be [n_points, xyz]\n", - "dims = (n_points, 3)\n", - "\n", - "clouds_offset = 15\n", - "\n", - "# create some random clouds\n", - "normal = np.random.normal(size=dims, scale=5)\n", - "# stack the data into a single array\n", - "cloud = np.vstack(\n", - " [\n", - " normal - clouds_offset,\n", - " normal,\n", - " normal + clouds_offset,\n", - " ]\n", - ")\n", - "\n", - "# color each of them separately\n", - "colors = [\"yellow\"] * n_points + [\"cyan\"] * n_points + [\"magenta\"] * n_points\n", - "\n", - "# create plot\n", - "fig_scatter = fpl.Figure()\n", - "\n", - "# use an alpha value since this will be a lot of points\n", - "scatter_graphic = fig_scatter[0, 0].add_scatter(data=cloud, sizes=3, colors=colors, alpha=0.7)\n", - "\n", - "fig_scatter.show()" - ] - }, - { - "cell_type": "markdown", - "id": "b6e4a704-ee6b-4316-956e-acb4dcc1c6f2", - "metadata": {}, - "source": [ - "**Scatter graphic features work similarly to line graphic**" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8fa46ec0-8680-44f5-894c-559de3145932", - "metadata": {}, - "outputs": [], - "source": [ - "# half of the first cloud's points to red\n", - "scatter_graphic.colors[:n_points:2] = \"r\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "293a4793-44b9-4d18-ae6a-68e7c6f91acc", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig_scatter.canvas.snapshot()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e4dc71e4-5144-436f-a464-f2a29eee8f0b", - "metadata": {}, - "outputs": [], - "source": [ - "# set the green value directly\n", - "scatter_graphic.colors[n_points:n_points * 2, 1] = 0.3" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5ea7852d-fdae-401b-83b6-b6cfd975f64f", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig_scatter.canvas.snapshot()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5b637a29-cd5e-4011-ab81-3f91490d9ecd", - "metadata": {}, - "outputs": [], - "source": [ - "# set color values directly using an array\n", - "scatter_graphic.colors[n_points * 2:] = np.repeat([[1, 1, 0, 0.5]], n_points, axis=0)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "02c19f51-6436-4601-976e-04326df0de81", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig_scatter.canvas.snapshot()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a4084fce-78a2-48b3-9a0d-7b57c165c3c1", - "metadata": {}, - "outputs": [], - "source": [ - "# change the data, change y-values\n", - "scatter_graphic.data[n_points:n_points * 2, 1] += 15" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2ec43f58-4710-4603-9358-682c4af3f701", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig_scatter.canvas.snapshot()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f486083e-7c58-4255-ae1a-3fe5d9bfaeed", - "metadata": {}, - "outputs": [], - "source": [ - "# set x values directly but using an array\n", - "scatter_graphic.data[n_points:n_points * 2, 0] = np.linspace(-40, 0, n_points)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6bcb3bc3-4b75-4bbc-b8ca-f8a3219ec3d7", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig_scatter.canvas.snapshot()" - ] - }, - { - "cell_type": "markdown", - "id": "d9e554de-c436-4684-a46a-ce8a33d409ac", - "metadata": {}, - "source": [ - "## ipywidget layouts\n", - "\n", - "This just plots everything from these examples in a single output cell" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "01a6f70b-c81b-4ee5-8a6b-d979b87227eb", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# row1 = HBox([plot.show(), plot_v.show(), plot_sync.show()])\n", - "# row2 = HBox([plot_l.show(), plot_l3d.show(), plot_s.show()])\n", - "\n", - "# VBox([row1, row2])" - ] - }, - { - "cell_type": "markdown", - "id": "a26c0063-b7e0-4f36-bb14-db06bafa31aa", - "metadata": {}, - "source": [ - "## More subplots" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6b7e1129-ae8e-4a0f-82dc-bd8fb65871fc", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# Figure of shape 2 x 3 with all controllers synced\n", - "figure_grid = fpl.Figure(shape=(2, 3), controller_ids=\"sync\")\n", - "\n", - "# Make a random image graphic for each subplot\n", - "for subplot in figure_grid:\n", - " # create image data\n", - " data = np.random.rand(512, 512)\n", - " # add an image to the subplot\n", - " subplot.add_image(data, name=\"rand-img\")\n", - "\n", - "# Define a function to update the image graphics with new data\n", - "# add_animations will pass the gridplot to the animation function\n", - "def update_data(f):\n", - " for subplot in f:\n", - " new_data = np.random.rand(512, 512)\n", - " # index the image graphic by name and set the data\n", - " subplot[\"rand-img\"].data = new_data\n", - " \n", - "# add the animation function\n", - "figure_grid.add_animations(update_data)\n", - "\n", - "# show the gridplot \n", - "figure_grid.show()" - ] - }, - { - "cell_type": "markdown", - "id": "f4f71c34-3925-442f-bd76-60dd57d09f48", - "metadata": {}, - "source": [ - "### Slicing GridPlot" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d8194c9e-9a99-4d4a-8984-a4cfcab0c42c", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# positional indexing\n", - "# row 0 and col 0\n", - "figure_grid[0, 0]" - ] - }, - { - "cell_type": "markdown", - "id": "d626640f-bc93-4883-9bf4-47b825bbc663", - "metadata": {}, - "source": [ - "You can get the graphics within a subplot, just like with simple `Plot`" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bffec80c-e81b-4945-85a2-c2c5e8395677", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "figure_grid[0, 1].graphics" - ] - }, - { - "cell_type": "markdown", - "id": "a4e3184f-c86a-4a7e-b803-31632cc163b0", - "metadata": {}, - "source": [ - "and change their properties" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "04b616fb-6644-42ba-8683-0589ce7d165e", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "figure_grid[0, 1].graphics[0].vmax = 0.5" - ] - }, - { - "cell_type": "markdown", - "id": "28f7362c-d1b9-43ef-85c5-4d68f70f459c", - "metadata": {}, - "source": [ - "more slicing with `GridPlot`" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "920e6365-bb50-4882-9b0d-8367dc485360", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# you can give subplots human-readable string names\n", - "figure_grid[0, 2].name = \"top-right-plot\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "73300d2c-3e70-43ad-b5a2-40341b701ac8", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "figure_grid[\"top-right-plot\"]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "834d9905-35e9-4711-9375-5b1828c80ee2", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# view its position\n", - "figure_grid[\"top-right-plot\"].position" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9aa61efa-c6a5-4611-a03b-1b8da66b19f0", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# these are really the same\n", - "figure_grid[\"top-right-plot\"] is figure_grid[0, 2]" - ] - }, - { - "cell_type": "markdown", - "id": "28c8b145-86cb-4445-92be-b7537a87f7ca", - "metadata": {}, - "source": [ - "Indexing with subplot name and graphic name" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2b7b73a3-5335-4bd5-bbef-c7d3cfbb3ca7", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "figure_grid[\"top-right-plot\"][\"rand-img\"].vmin = 0.5" - ] - }, - { - "cell_type": "markdown", - "id": "6a5b4368-ae4d-442c-a11f-45c70267339b", - "metadata": {}, - "source": [ - "## Figure subplot customization" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "175d45a6-3351-4b75-8ff3-08797fe0a389", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# 2 rows and 3 columns\n", - "shape = (2, 3)\n", - "\n", - "# pan-zoom controllers for each view\n", - "# views are synced if they have the \n", - "# same controller ID\n", - "controller_ids = [\n", - " [0, 3, 1], # id each controller with an integer\n", - " [2, 2, 3]\n", - "]\n", - "\n", - "\n", - "# you can give string names for each subplot within the gridplot\n", - "names = [\n", - " [\"subplot0\", \"subplot1\", \"subplot2\"],\n", - " [\"subplot3\", \"subplot4\", \"subplot5\"]\n", - "]\n", - "\n", - "# Create the grid plot\n", - "figure_grid = fpl.Figure(\n", - " shape=shape,\n", - " controller_ids=controller_ids,\n", - " names=names,\n", - ")\n", - "\n", - "\n", - "# Make a random image graphic for each subplot\n", - "for subplot in figure_grid:\n", - " data = np.random.rand(512, 512)\n", - " # create and add an ImageGraphic\n", - " subplot.add_image(data=data, name=\"rand-image\")\n", - " \n", - "\n", - "# Define a function to update the image graphics \n", - "# with new randomly generated data\n", - "def set_random_frame(gp):\n", - " for subplot in gp:\n", - " new_data = np.random.rand(512, 512)\n", - " subplot[\"rand-image\"].data = new_data\n", - "\n", - "# add the animation\n", - "figure_grid.add_animations(set_random_frame)\n", - "figure_grid.show()" - ] - }, - { - "cell_type": "markdown", - "id": "4224f1c2-5e61-4894-8d72-0519598a3cef", - "metadata": {}, - "source": [ - "Indexing the gridplot to access subplots" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d88dd9b2-9359-42e8-9dfb-96dcbbb34b95", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# can access subplot by name\n", - "figure_grid[\"subplot0\"]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a14df7ea-14c3-4a8a-84f2-2e2194236d9e", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# can access subplot by index\n", - "figure_grid[0, 0]" - ] - }, - { - "cell_type": "markdown", - "id": "5f8a3427-7949-40a4-aec2-38d5d95ef156", - "metadata": {}, - "source": [ - "**subplots also support indexing!**\n", - "\n", - "this can be used to get graphics if they are named" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8c99fee0-ce46-4f18-8300-af025c9a967c", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# can access graphic directly via name\n", - "figure_grid[\"subplot0\"][\"rand-image\"]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ed4eebb7-826d-4856-bbb8-db2de966a0c3", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "figure_grid[\"subplot0\"][\"rand-image\"].vmin = 0.6\n", - "figure_grid[\"subplot0\"][\"rand-image\"].vmax = 0.8" - ] - }, - { - "cell_type": "markdown", - "id": "ad322f6f-e7de-4eb3-a1d9-cf28701a2eae", - "metadata": {}, - "source": [ - "positional indexing also works event if subplots have string names" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "759d3966-d92b-460f-ba48-e57adabbf163", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "figure_grid[1, 0][\"rand-image\"].vim = 0.1\n", - "figure_grid[1, 0][\"rand-image\"].vmax = 0.3" - ] - } - ], - "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.11.3" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/examples/desktop/heatmap/heatmap.py b/examples/desktop/heatmap/heatmap.py index fa5ec6715..f3a1bf460 100644 --- a/examples/desktop/heatmap/heatmap.py +++ b/examples/desktop/heatmap/heatmap.py @@ -12,18 +12,14 @@ fig = fpl.Figure() -xs = np.linspace(0, 1_000, 10_000) +xs = np.linspace(0, 1_000, 10_000, dtype=np.float32) -sine = np.sin(xs) -cosine = np.cos(xs) +sine = np.sin(np.sqrt(xs)) -# alternating sines and cosines -data = np.zeros((10_000, 10_000), dtype=np.float32) -data[::2] = sine -data[1::2] = cosine +data = np.vstack([sine * i for i in range(20_000)]) # plot the image data -heatmap_graphic = fig[0, 0].add_heatmap(data=data, name="heatmap") +img = fig[0, 0].add_image(data=data, name="heatmap") fig.show() diff --git a/examples/desktop/heatmap/heatmap_cmap.py b/examples/desktop/heatmap/heatmap_cmap.py index a1434bb0e..39e697c93 100644 --- a/examples/desktop/heatmap/heatmap_cmap.py +++ b/examples/desktop/heatmap/heatmap_cmap.py @@ -4,7 +4,7 @@ Change the cmap of a heatmap """ -# test_example = true +# test_example = false import fastplotlib as fpl import numpy as np @@ -12,18 +12,14 @@ fig = fpl.Figure() -xs = np.linspace(0, 1_000, 10_000) +xs = np.linspace(0, 1_000, 10_000, dtype=np.float32) -sine = np.sin(xs) -cosine = np.cos(xs) +sine = np.sin(np.sqrt(xs)) -# alternating sines and cosines -data = np.zeros((10_000, 10_000), dtype=np.float32) -data[::2] = sine -data[1::2] = cosine +data = np.vstack([sine * i for i in range(20_000)]) # plot the image data -heatmap_graphic = fig[0, 0].add_heatmap(data=data, name="heatmap") +img = fig[0, 0].add_image(data=data, name="heatmap") fig.show() @@ -31,7 +27,7 @@ fig[0, 0].auto_scale() -heatmap_graphic.cmap = "viridis" +img.cmap = "viridis" if __name__ == "__main__": print(__doc__) diff --git a/examples/desktop/heatmap/heatmap_data.py b/examples/desktop/heatmap/heatmap_data.py index 67aee1668..75ef3ce41 100644 --- a/examples/desktop/heatmap/heatmap_data.py +++ b/examples/desktop/heatmap/heatmap_data.py @@ -4,7 +4,7 @@ Change the data of a heatmap """ -# test_example = true +# test_example = false import fastplotlib as fpl import numpy as np @@ -12,28 +12,24 @@ fig = fpl.Figure() -xs = np.linspace(0, 1_000, 10_000) +xs = np.linspace(0, 1_000, 9_000, dtype=np.float32) -sine = np.sin(xs) -cosine = np.cos(xs) +sine = np.sin(np.sqrt(xs)) -# alternating sines and cosines -data = np.zeros((10_000, 10_000), dtype=np.float32) -data[::2] = sine -data[1::2] = cosine +data = np.vstack([sine * i for i in range(9_000)]) # plot the image data -heatmap_graphic = fig[0, 0].add_heatmap(data=data, name="heatmap") +img = fig[0, 0].add_image(data=data, name="heatmap") fig.show() fig.canvas.set_logical_size(1500, 1500) fig[0, 0].auto_scale() +cosine = np.cos(np.sqrt(xs)[:3000]) -heatmap_graphic.data[:5_000] = sine -heatmap_graphic.data[5_000:] = cosine - +# change first 2,000 rows and 3,000 columns +img.data[:2_000, :3_000] = np.vstack([cosine * i * 4 for i in range(2_000)]) if __name__ == "__main__": print(__doc__) diff --git a/examples/desktop/heatmap/heatmap_square.py b/examples/desktop/heatmap/heatmap_square.py new file mode 100644 index 000000000..f776b74e1 --- /dev/null +++ b/examples/desktop/heatmap/heatmap_square.py @@ -0,0 +1,33 @@ +""" +Square Heatmap +============== +square heatmap test +""" + +# test_example = false + +import fastplotlib as fpl +import numpy as np + + +fig = fpl.Figure() + +xs = np.linspace(0, 1_000, 20_000, dtype=np.float32) + +sine = np.sin(np.sqrt(xs)) + +data = np.vstack([sine * i for i in range(20_000)]) + +# plot the image data +img = fig[0, 0].add_image(data=data, name="heatmap") + +del data # data no longer needed after given to graphic +fig.show() + +fig.canvas.set_logical_size(1500, 1500) + +fig[0, 0].auto_scale() + +if __name__ == "__main__": + print(__doc__) + fpl.run() diff --git a/examples/desktop/heatmap/heatmap_vmin_vmax.py b/examples/desktop/heatmap/heatmap_vmin_vmax.py index 6fe8a08b8..75b6b7b68 100644 --- a/examples/desktop/heatmap/heatmap_vmin_vmax.py +++ b/examples/desktop/heatmap/heatmap_vmin_vmax.py @@ -4,7 +4,7 @@ Change the vmin vmax of a heatmap """ -# test_example = true +# test_example = false import fastplotlib as fpl import numpy as np @@ -12,18 +12,14 @@ fig = fpl.Figure() -xs = np.linspace(0, 1_000, 10_000) +xs = np.linspace(0, 1_000, 10_000, dtype=np.float32) -sine = np.sin(xs) -cosine = np.cos(xs) +sine = np.sin(np.sqrt(xs)) -# alternating sines and cosines -data = np.zeros((10_000, 10_000), dtype=np.float32) -data[::2] = sine -data[1::2] = cosine +data = np.vstack([sine * i for i in range(20_000)]) # plot the image data -heatmap_graphic = fig[0, 0].add_heatmap(data=data, name="heatmap") +img = fig[0, 0].add_image(data=data, name="heatmap") fig.show() @@ -31,8 +27,8 @@ fig[0, 0].auto_scale() -heatmap_graphic.cmap.vmin = -0.5 -heatmap_graphic.cmap.vmax = 0.5 +img.vmin = -5_000 +img.vmax = 10_000 if __name__ == "__main__": print(__doc__) diff --git a/examples/desktop/heatmap/heatmap_wide.py b/examples/desktop/heatmap/heatmap_wide.py new file mode 100644 index 000000000..251c25fa4 --- /dev/null +++ b/examples/desktop/heatmap/heatmap_wide.py @@ -0,0 +1,32 @@ +""" +Wide Heatmap +============ +Wide example +""" + +# test_example = false + +import fastplotlib as fpl +import numpy as np + + +fig = fpl.Figure() + +xs = np.linspace(0, 1_000, 20_000, dtype=np.float32) + +sine = np.sin(np.sqrt(xs)) + +data = np.vstack([sine * i for i in range(10_000)]) + +# plot the image data +img = fig[0, 0].add_image(data=data, name="heatmap") + +fig.show() + +fig.canvas.set_logical_size(1500, 1500) + +fig[0, 0].auto_scale() + +if __name__ == "__main__": + print(__doc__) + fpl.run() diff --git a/examples/desktop/image/image_rgbvminvmax.py b/examples/desktop/image/image_rgbvminvmax.py index 9725c038a..56114e1e3 100644 --- a/examples/desktop/image/image_rgbvminvmax.py +++ b/examples/desktop/image/image_rgbvminvmax.py @@ -23,8 +23,8 @@ fig[0, 0].auto_scale() -image_graphic.cmap.vmin = 0.5 -image_graphic.cmap.vmax = 0.75 +image_graphic.vmin = 0.5 +image_graphic.vmax = 0.75 if __name__ == "__main__": diff --git a/examples/desktop/image/image_vminvmax.py b/examples/desktop/image/image_vminvmax.py index 3c8607aef..d24d1f18c 100644 --- a/examples/desktop/image/image_vminvmax.py +++ b/examples/desktop/image/image_vminvmax.py @@ -23,8 +23,8 @@ fig[0, 0].auto_scale() -image_graphic.cmap.vmin = 0.5 -image_graphic.cmap.vmax = 0.75 +image_graphic.vmin = 0.5 +image_graphic.vmax = 0.75 if __name__ == "__main__": diff --git a/examples/desktop/line/line_cmap.py b/examples/desktop/line/line_cmap.py index 7d8e1e7d6..f18ceb201 100644 --- a/examples/desktop/line/line_cmap.py +++ b/examples/desktop/line/line_cmap.py @@ -21,21 +21,21 @@ ys = np.cos(xs) - 5 cosine = np.dstack([xs, ys])[0] -# cmap_values from an array, so the colors on the sine line will be based on the sine y-values +# cmap_transform from an array, so the colors on the sine line will be based on the sine y-values sine_graphic = fig[0, 0].add_line( data=sine, thickness=10, cmap="plasma", - cmap_values=sine[:, 1] + cmap_transform=sine[:, 1] ) # qualitative colormaps, useful for cluster labels or other types of categorical labels -cmap_values = [0] * 25 + [5] * 10 + [1] * 35 + [2] * 30 +labels = [0] * 25 + [5] * 10 + [1] * 35 + [2] * 30 cosine_graphic = fig[0, 0].add_line( data=cosine, thickness=10, cmap="tab10", - cmap_values=cmap_values + cmap_transform=labels ) fig.show() diff --git a/examples/desktop/line/line_colorslice.py b/examples/desktop/line/line_colorslice.py index 4df666531..28b877793 100644 --- a/examples/desktop/line/line_colorslice.py +++ b/examples/desktop/line/line_colorslice.py @@ -1,6 +1,6 @@ """ Line Plot -============ +========= Example showing color slicing with cosine, sine, sinc lines. """ @@ -15,25 +15,48 @@ xs = np.linspace(-10, 10, 100) # sine wave ys = np.sin(xs) -sine = np.dstack([xs, ys])[0] +sine = np.column_stack([xs, ys]) # cosine wave -ys = np.cos(xs) + 5 -cosine = np.dstack([xs, ys])[0] +ys = np.cos(xs) +cosine = np.column_stack([xs, ys]) # sinc function a = 0.5 -ys = np.sinc(xs) * 3 + 8 -sinc = np.dstack([xs, ys])[0] +ys = np.sinc(xs) * 3 +sinc = np.column_stack([xs, ys]) -sine_graphic = fig[0, 0].add_line(data=sine, thickness=5, colors="magenta") +sine_graphic = fig[0, 0].add_line( + data=sine, + thickness=5, + colors="magenta" +) # you can also use colormaps for lines! -cosine_graphic = fig[0, 0].add_line(data=cosine, thickness=12, cmap="autumn") +cosine_graphic = fig[0, 0].add_line( + data=cosine, + thickness=12, + cmap="autumn", + offset=(0, 3, 0) # places the graphic at a y-axis offset of 3, offsets don't affect data +) # or a list of colors for each datapoint colors = ["r"] * 25 + ["purple"] * 25 + ["y"] * 25 + ["b"] * 25 -sinc_graphic = fig[0, 0].add_line(data=sinc, thickness=5, colors=colors) +sinc_graphic = fig[0, 0].add_line( + data=sinc, + thickness=5, + colors=colors, + offset=(0, 6, 0) +) + +zeros = np.zeros(xs.size) +zeros_data = np.column_stack([xs, zeros]) +zeros_graphic = fig[0, 0].add_line( + data=zeros_data, + thickness=8, + colors="w", + offset=(0, 10, 0) +) fig.show() @@ -42,10 +65,6 @@ cosine_graphic.colors[90:] = "red" cosine_graphic.colors[60] = "w" -# indexing to assign colormaps to entire lines or segments -sinc_graphic.cmap[10:50] = "gray" -sine_graphic.cmap = "seismic" - # more complex indexing, set the blue value directly from an array cosine_graphic.colors[65:90, 0] = np.linspace(0, 1, 90-65) @@ -53,8 +72,14 @@ key = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 67, 19]) sinc_graphic.colors[key] = "Red" -key2 = np.array([True, False, True, False, True, True, True, True]) -cosine_graphic.colors[key2] = "Green" +# boolean fancy indexing +zeros_graphic.colors[xs < -5] = "green" + +# assign colormap to an entire line +sine_graphic.cmap = "seismic" +# or to segments of a line +zeros_graphic.cmap[50:75] = "jet" +zeros_graphic.cmap[75:] = "viridis" fig.canvas.set_logical_size(800, 800) diff --git a/examples/desktop/line/line_dataslice.py b/examples/desktop/line/line_dataslice.py index 12a5f0f04..c2c6b9d36 100644 --- a/examples/desktop/line/line_dataslice.py +++ b/examples/desktop/line/line_dataslice.py @@ -41,9 +41,9 @@ cosine_graphic.data[90:, 1] = 7 cosine_graphic.data[0] = np.array([[-10, 0, 0]]) -# additional fancy indexing using numpy -key2 = np.array([True, False, True, False, True, True, True, True]) -sinc_graphic.data[key2] = np.array([[5, 1, 2]]) +# additional fancy indexing with boolean array +bool_key = [True, True, True, False, False] * 20 +sinc_graphic.data[bool_key, 1] = 7 # y vals to 1 fig.canvas.set_logical_size(800, 800) diff --git a/examples/desktop/line/line_present_scaling.py b/examples/desktop/line/line_present_scaling.py deleted file mode 100644 index d334e6fbd..000000000 --- a/examples/desktop/line/line_present_scaling.py +++ /dev/null @@ -1,49 +0,0 @@ -""" -Line Plot -============ -Example showing present and scaling feature for lines. -""" - -# test_example = true - -import fastplotlib as fpl -import numpy as np - - -fig = fpl.Figure() - -xs = np.linspace(-10, 10, 100) -# sine wave -ys = np.sin(xs) -sine = np.dstack([xs, ys])[0] - -# cosine wave -ys = np.cos(xs) + 5 -cosine = np.dstack([xs, ys])[0] - -# sinc function -a = 0.5 -ys = np.sinc(xs) * 3 + 8 -sinc = np.dstack([xs, ys])[0] - -sine_graphic = fig[0, 0].add_line(data=sine, thickness=5, colors="magenta") - -# you can also use colormaps for lines! -cosine_graphic = fig[0, 0].add_line(data=cosine, thickness=12, cmap="autumn") - -# or a list of colors for each datapoint -colors = ["r"] * 25 + ["purple"] * 25 + ["y"] * 25 + ["b"] * 25 -sinc_graphic = fig[0, 0].add_line(data=sinc, thickness=5, colors=colors) - -fig.show() - -sinc_graphic.present = False - -fig.canvas.set_logical_size(800, 800) - -fig[0, 0].auto_scale() - - -if __name__ == "__main__": - print(__doc__) - fpl.run() diff --git a/examples/desktop/line_collection/line_collection.py b/examples/desktop/line_collection/line_collection.py index dd6f3ca33..db99e32ed 100644 --- a/examples/desktop/line_collection/line_collection.py +++ b/examples/desktop/line_collection/line_collection.py @@ -1,6 +1,6 @@ """ -Line Plot -============ +Line collection +=============== Example showing how to plot line collections """ diff --git a/examples/desktop/line_collection/line_collection_cmap_values.py b/examples/desktop/line_collection/line_collection_cmap_values.py index 9eeef40f8..5ffc032e9 100644 --- a/examples/desktop/line_collection/line_collection_cmap_values.py +++ b/examples/desktop/line_collection/line_collection_cmap_values.py @@ -1,7 +1,7 @@ """ -Line Plot -============ -Example showing how to plot line collections +Line collections quantitative cmap +================================== +Example showing a line collection with a quantitative cmap """ # test_example = true @@ -36,10 +36,7 @@ def make_circle(center, radius: float, n_points: int = 75) -> np.ndarray: fig = fpl.Figure() fig[0, 0].add_line_collection( - circles, - cmap="bwr", - cmap_values=cmap_values, - thickness=10 + circles, cmap="bwr", cmap_transform=cmap_values, thickness=10 ) fig.show() diff --git a/examples/desktop/line_collection/line_collection_cmap_values_qualitative.py b/examples/desktop/line_collection/line_collection_cmap_values_qualitative.py index 85f0724d8..f96fd3aac 100644 --- a/examples/desktop/line_collection/line_collection_cmap_values_qualitative.py +++ b/examples/desktop/line_collection/line_collection_cmap_values_qualitative.py @@ -1,7 +1,7 @@ """ -Line Plot -============ -Example showing how to plot line collections +Line collections qualitative cmaps +================================== +Example showing a line collection with a qualitative cmap """ # test_example = true @@ -44,7 +44,7 @@ def make_circle(center, radius: float, n_points: int = 75) -> np.ndarray: fig[0, 0].add_line_collection( circles, cmap="tab10", - cmap_values=cmap_values, + cmap_transform=cmap_values, thickness=10 ) diff --git a/examples/desktop/line_collection/line_collection_colors.py b/examples/desktop/line_collection/line_collection_colors.py index d53afcd5b..3ee561d8f 100644 --- a/examples/desktop/line_collection/line_collection_colors.py +++ b/examples/desktop/line_collection/line_collection_colors.py @@ -1,7 +1,7 @@ """ -Line Plot -============ -Example showing how to plot line collections +Line collection colors +====================== +Example showing one way ot setting colors for individual lines in a collection """ # test_example = true diff --git a/examples/desktop/line_collection/line_collection_slicing.py b/examples/desktop/line_collection/line_collection_slicing.py new file mode 100644 index 000000000..9eaebdd7e --- /dev/null +++ b/examples/desktop/line_collection/line_collection_slicing.py @@ -0,0 +1,68 @@ +""" +Line collection slicing +======================= +Example showing how to slice a line collection +""" + +# test_example = true + +import numpy as np +import fastplotlib as fpl + + +xs = np.linspace(0, np.pi * 10, 100) +# sine wave +ys = np.sin(xs) + +data = np.column_stack([xs, ys]) +multi_data = np.stack([data] * 15) + + +fig = fpl.Figure() + +lines = fig[0, 0].add_line_stack( + multi_data, + thickness=[2, 10, 2, 5, 5, 5, 8, 8, 8, 9, 3, 3, 3, 4, 4], + separation=1, + metadatas=list(range(15)), # some metadata + names=list("abcdefghijklmno"), # unique name for each line +) + +print("slice a collection to return a collection indexer") +print(lines[1:5]) # lines 1, 2, 3, 4 + +print("collections supports fancy indexing!") +print(lines[::3]) + +print("fancy index using properties of individual lines!") +print(lines[lines.thickness < 3]) +print(lines[lines.metadatas > 10]) + +# set line properties, such as data +# set y-values of lines 3, 4, 5 +lines[3:6].data[:, 1] = np.cos(xs) +# set these same lines to a different color +lines[3:6].colors = "cyan" + +# setting properties using fancy indexing +# set cmap along the line collection +lines[-3:].cmap = "plasma" + +# set cmap of along a single line +lines[7].cmap = "jet" + +# fancy indexing using line properties! +lines[lines.thickness > 8].colors = "r" +lines[lines.names == "a"].colors = "b" + +# fancy index at the level of lines and individual line properties! +lines[::2].colors[::5] = "magenta" # set every 5th point of every other line to magenta +lines[3:6].colors[50:, -1] = 0.6 # set half the points alpha to 0.6 + +fig.show(maintain_aspect=False) + +fig.canvas.set_logical_size(900, 600) + +if __name__ == "__main__": + print(__doc__) + fpl.run() diff --git a/examples/desktop/line_collection/line_stack.py b/examples/desktop/line_collection/line_stack.py index cf5d933e3..676e6e5c2 100644 --- a/examples/desktop/line_collection/line_stack.py +++ b/examples/desktop/line_collection/line_stack.py @@ -1,7 +1,7 @@ """ -Line Plot -============ -Example showing how to plot line collections +Line stack +========== +Example showing how to plot a stack of lines """ # test_example = true @@ -10,17 +10,21 @@ import fastplotlib as fpl -xs = np.linspace(0, 100, 1000) +xs = np.linspace(0, np.pi * 10, 100) # sine wave -ys = np.sin(xs) * 20 +ys = np.sin(xs) -# make 25 lines -data = np.vstack([ys] * 25) +data = np.column_stack([xs, ys]) +multi_data = np.stack([data] * 10) fig = fpl.Figure() -# line stack takes all the same arguments as line collection and behaves similarly -fig[0, 0].add_line_stack(data, cmap="jet") +line_stack = fig[0, 0].add_line_stack( + multi_data, # shape: (10, 100, 2), i.e. [n_lines, n_points, xy] + cmap="jet", # applied along n_lines + thickness=5, + separation=1, # spacing between lines along the separation axis, default separation along "y" axis +) fig.show(maintain_aspect=False) diff --git a/examples/desktop/line_collection/line_stack_3d.py b/examples/desktop/line_collection/line_stack_3d.py new file mode 100644 index 000000000..41914e2d2 --- /dev/null +++ b/examples/desktop/line_collection/line_stack_3d.py @@ -0,0 +1,103 @@ +""" +Line stack 3D +============= +Example showing a 3D stack of lines with animations +""" + +# test_example = false + +import numpy as np +import fastplotlib as fpl + + +xs = np.linspace(0, np.pi * 10, 100) +# spiral +ys = np.sin(xs) +zs = np.cos(xs) + +data = np.column_stack([xs, ys, zs]) +multi_data = np.stack([data] * 10) + +# create figure to plot lines and use an orbit controller in 3D +fig = fpl.Figure(cameras="3d", controller_types="orbit") + +line_stack = fig[0, 0].add_line_stack( + multi_data, # shape: (10, 100, 2), i.e. [n_lines, n_points, xy] + cmap="jet", # applied along n_lines + thickness=3, + separation=1, # spacing between lines along the separation axis, default separation along "y" axis + name="lines", +) + + +x_increment = 0.1 + + +def animate_data(subplot): + """animate with different rates of spinning the spirals""" + global xs # x vals + global x_increment # increment + + # calculate the new data + # new a different spinning rate for each spiral + # top ones will spin faster than the bottom ones + new_xs = [xs + (factor * x_increment) for factor in np.linspace(0.5, 1.5, 10)] + y = [np.sin(x) for x in new_xs] + z = [np.cos(x) for x in new_xs] + + # iterate through collection and set data of each line + for i, line in enumerate(subplot["lines"]): + # set y and z values + line.data[:, 1:] = np.column_stack([y[i], z[i]]) + + x_increment += 0.1 + + +colors_iteration = 0 + + +def animate_colors(subplot): + """animate the colors""" + global colors_iteration + + # change the colors only on every 50th render cycle + # otherwise it just looks like flickering because it's too fast :) + if colors_iteration % 50 != 0: + colors_iteration += 1 + return + + # use cmap_transform to shift the cmap + cmap_transform = np.roll(np.arange(10), shift=int(colors_iteration / 50)) + + # set cmap with the transform + subplot["lines"].cmap = "jet", cmap_transform + + colors_iteration += 1 + + +fig[0, 0].add_animations(animate_data, animate_colors) + +# just a pre-saved camera state +camera_state = { + "position": np.array([-18.0, 9.0, 8.0]), + "rotation": np.array([0.00401791, -0.5951809, 0.00297593, 0.80357619]), + "scale": np.array([1.0, 1.0, 1.0]), + "reference_up": np.array([0.0, 1.0, 0.0]), + "fov": 50.0, + "width": 32, + "height": 20, + "zoom": 1, + "maintain_aspect": True, + "depth_range": None, +} + +fig.show(maintain_aspect=False) + +fig[0, 0].camera.set_state(camera_state) + +fig.canvas.set_logical_size(500, 500) + + +if __name__ == "__main__": + print(__doc__) + fpl.run() diff --git a/examples/desktop/scatter/scatter_cmap.py b/examples/desktop/scatter/scatter_cmap.py index f1bba98c3..58c43c0ea 100644 --- a/examples/desktop/scatter/scatter_cmap.py +++ b/examples/desktop/scatter/scatter_cmap.py @@ -21,7 +21,11 @@ agg.fit_predict(data) scatter_graphic = fig[0, 0].add_scatter( - data=data[:, :-1], sizes=15, alpha=0.7, cmap="Set1", cmap_values=agg.labels_ + data=data[:, :-1], # use only xy data + sizes=15, + alpha=0.7, + cmap="Set1", + cmap_transform=agg.labels_ # use the labels as a transform to map colors from the colormap ) fig.show() diff --git a/examples/desktop/scatter/scatter_colorslice.py b/examples/desktop/scatter/scatter_colorslice.py index 43f405b06..60433b5f5 100644 --- a/examples/desktop/scatter/scatter_colorslice.py +++ b/examples/desktop/scatter/scatter_colorslice.py @@ -19,7 +19,12 @@ n_points = 50 colors = ["yellow"] * n_points + ["cyan"] * n_points + ["magenta"] * n_points -scatter_graphic = fig[0, 0].add_scatter(data=data[:, :-1], sizes=6, alpha=0.7, colors=colors) +scatter_graphic = fig[0, 0].add_scatter( + data=data[:, :-1], + sizes=6, + alpha=0.7, + colors=colors # use colors from the list of strings +) fig.show() diff --git a/examples/desktop/scatter/scatter_present.py b/examples/desktop/scatter/scatter_present.py deleted file mode 100644 index 5da4610bd..000000000 --- a/examples/desktop/scatter/scatter_present.py +++ /dev/null @@ -1,38 +0,0 @@ -""" -Scatter Plot -============ -Example showing present feature for scatter plot. -""" - -# test_example = true - -import fastplotlib as fpl -import numpy as np -from pathlib import Path - - -fig = fpl.Figure() - -data_path = Path(__file__).parent.parent.joinpath("data", "iris.npy") -data = np.load(data_path) - -n_points = 50 -colors = ["yellow"] * n_points + ["cyan"] * n_points + ["magenta"] * n_points - -scatter_graphic = fig[0, 0].add_scatter(data=data[:, :-1], sizes=6, alpha=0.7, colors=colors) - -colors = ["red"] * n_points + ["white"] * n_points + ["blue"] * n_points -scatter_graphic2 = fig[0, 0].add_scatter(data=data[:, 1:], sizes=6, alpha=0.7, colors=colors) - -fig.show() - -fig.canvas.set_logical_size(800, 800) - -fig[0, 0].auto_scale() - -scatter_graphic.present = False - - -if __name__ == "__main__": - print(__doc__) - fpl.run() diff --git a/examples/desktop/screenshots/gridplot.png b/examples/desktop/screenshots/gridplot.png index ebf2d3a97..9e81fe8c6 100644 --- a/examples/desktop/screenshots/gridplot.png +++ b/examples/desktop/screenshots/gridplot.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f972f67b8830657ab14899f749fb385a080280304377d8868e6cd39c766a0afd -size 267084 +oid sha256:462a06e9c74dc9f0958aa265349dfac9c31d77a3ab3915f85596c85f2e7a6f3a +size 266056 diff --git a/examples/desktop/screenshots/gridplot_non_square.png b/examples/desktop/screenshots/gridplot_non_square.png index bc642b729..b74be7065 100644 --- a/examples/desktop/screenshots/gridplot_non_square.png +++ b/examples/desktop/screenshots/gridplot_non_square.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:352bf94c68444a330b000d7b6b3ec51b5b694ff3a0ce810299b325315923d9af -size 175938 +oid sha256:9ab4b1f8188824b81fe29b5c6ac7177734fb2b9958133e19f02919d1da98b96c +size 174978 diff --git a/examples/desktop/screenshots/heatmap.png b/examples/desktop/screenshots/heatmap.png index a8c8b73fe..ec6cf9955 100644 --- a/examples/desktop/screenshots/heatmap.png +++ b/examples/desktop/screenshots/heatmap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5620e4dcb964dbf3318ac77e566af395a35b9762e0687dec2e1a2864eb291fd3 -size 102994 +oid sha256:754bd8713617bf61d1adf57b3e84c1257b038bf15412aa3c8bd466d1405086e7 +size 48524 diff --git a/examples/desktop/screenshots/heatmap_cmap.png b/examples/desktop/screenshots/heatmap_cmap.png index cee81dd30..c495cf72c 100644 --- a/examples/desktop/screenshots/heatmap_cmap.png +++ b/examples/desktop/screenshots/heatmap_cmap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8863461569f5b89d1443e3051a5512f3987487fcb9e057215d2f030a180fa09f -size 97996 +oid sha256:d2ba0b76e982ceb1439c5ebaabfaf089ea9b09e50934718eaaa29d7492272196 +size 42746 diff --git a/examples/desktop/screenshots/heatmap_data.png b/examples/desktop/screenshots/heatmap_data.png index 316a73753..229d6c2cc 100644 --- a/examples/desktop/screenshots/heatmap_data.png +++ b/examples/desktop/screenshots/heatmap_data.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a975179e82893dbb04e4674310761e7b02bb62ae6abb1b89397720bddf96ae5f -size 19084 +oid sha256:a7160c4f034214f8052a6d88001dac706b0a85a5a4df076958ba1a176344b85a +size 53854 diff --git a/examples/desktop/screenshots/heatmap_square.png b/examples/desktop/screenshots/heatmap_square.png new file mode 100644 index 000000000..00a01133e --- /dev/null +++ b/examples/desktop/screenshots/heatmap_square.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8d01171b2bd05b5c88df4312c303094fdede36b1cf930455ace6d1fb12d8eb36 +size 81274 diff --git a/examples/desktop/screenshots/heatmap_vmin_vmax.png b/examples/desktop/screenshots/heatmap_vmin_vmax.png index 357683d82..b028291f7 100644 --- a/examples/desktop/screenshots/heatmap_vmin_vmax.png +++ b/examples/desktop/screenshots/heatmap_vmin_vmax.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9592f3724016db1b7431bc100b16bec175e197c111e7b442dc2255d51da3f5e8 -size 114957 +oid sha256:61c3754de3a7e6622ce1a77dbbf9bbd6ccfd3ccad3b1463b009bf93511258034 +size 44426 diff --git a/examples/desktop/screenshots/heatmap_wide.png b/examples/desktop/screenshots/heatmap_wide.png new file mode 100644 index 000000000..927b933d6 --- /dev/null +++ b/examples/desktop/screenshots/heatmap_wide.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:271e0d769b608d0f34a153ab8b8353f1e5d127f239951fc407ccedd3eee5e2e5 +size 82687 diff --git a/examples/desktop/screenshots/image_cmap.png b/examples/desktop/screenshots/image_cmap.png index bbf51ab18..be16ba213 100644 --- a/examples/desktop/screenshots/image_cmap.png +++ b/examples/desktop/screenshots/image_cmap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:555fd969606d0cb231ac152724f7c9717a2220ce22db663c5e7d5793f828ed34 -size 189654 +oid sha256:552a4d5141a5a87baaedd8a9d7d911dfdddee5792c024c77012665268af865e9 +size 189479 diff --git a/examples/desktop/screenshots/image_rgb.png b/examples/desktop/screenshots/image_rgb.png index 9a5082b12..8d391f07c 100644 --- a/examples/desktop/screenshots/image_rgb.png +++ b/examples/desktop/screenshots/image_rgb.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:95f3cae6caf8d64d1a6b4799df52dc61cc05bd6b6ea465edbec06a9678f32435 -size 218089 +oid sha256:55e76cea92eb34e1e25d730d2533a9a0d845921e78bc980708d320bb353a2d73 +size 218413 diff --git a/examples/desktop/screenshots/image_rgbvminvmax.png b/examples/desktop/screenshots/image_rgbvminvmax.png index 00bbdc0c5..eabe85d28 100644 --- a/examples/desktop/screenshots/image_rgbvminvmax.png +++ b/examples/desktop/screenshots/image_rgbvminvmax.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4fc06b8cdd72040cf2ffc44cde80d5ae21ca392daac25d79fe175b5865b13552 -size 34894 +oid sha256:7ee27d89170b30a3da7fe6d752961b30e17712d7905d8fa0686f9597debe68f9 +size 34620 diff --git a/examples/desktop/screenshots/image_simple.png b/examples/desktop/screenshots/image_simple.png index 94fcd3061..853eb2f01 100644 --- a/examples/desktop/screenshots/image_simple.png +++ b/examples/desktop/screenshots/image_simple.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3dcfb5d48d0e4db920c33ee725e2c66f3c8e04a66e03d283a6481f42a4121a16 -size 190178 +oid sha256:e943bd3b1e00acaed274dd185f5362210e39330e0f541db9ceee489fa0a9a344 +size 189822 diff --git a/examples/desktop/screenshots/image_vminvmax.png b/examples/desktop/screenshots/image_vminvmax.png index 00bbdc0c5..eabe85d28 100644 --- a/examples/desktop/screenshots/image_vminvmax.png +++ b/examples/desktop/screenshots/image_vminvmax.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4fc06b8cdd72040cf2ffc44cde80d5ae21ca392daac25d79fe175b5865b13552 -size 34894 +oid sha256:7ee27d89170b30a3da7fe6d752961b30e17712d7905d8fa0686f9597debe68f9 +size 34620 diff --git a/examples/desktop/screenshots/line_collection_slicing.png b/examples/desktop/screenshots/line_collection_slicing.png new file mode 100644 index 000000000..ba4170874 --- /dev/null +++ b/examples/desktop/screenshots/line_collection_slicing.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:01090d611117fd0d2b3f9971e359871c9598a634a1829e74848b1c78a770d437 +size 131764 diff --git a/examples/desktop/screenshots/line_colorslice.png b/examples/desktop/screenshots/line_colorslice.png index 3d04c473f..789265530 100644 --- a/examples/desktop/screenshots/line_colorslice.png +++ b/examples/desktop/screenshots/line_colorslice.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aa941eaf5b940b4eebab89ed836cbd092e16b4758abafa3722c296db65c0c4b5 -size 33233 +oid sha256:25e87f566a667c98b54d4acdf115d16b486e047242b9ce8b141e5724b9d0a46a +size 33191 diff --git a/examples/desktop/screenshots/line_dataslice.png b/examples/desktop/screenshots/line_dataslice.png index 0863751bf..e55a6111e 100644 --- a/examples/desktop/screenshots/line_dataslice.png +++ b/examples/desktop/screenshots/line_dataslice.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:78ccd51d1891fb6a345cb2885a341f276d8ad7a6fa506deda6cae6ef14c64094 -size 45843 +oid sha256:e76275ea6f5719e16ff0ef3401dc33fe4b70c4c9010b3b673fca26812f33b9e8 +size 46400 diff --git a/examples/desktop/screenshots/line_present_scaling.png b/examples/desktop/screenshots/line_present_scaling.png deleted file mode 100644 index ba7142106..000000000 --- a/examples/desktop/screenshots/line_present_scaling.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:06f7dd45eb495fecfcf46478c6430a658640ceb2855c4797bc184cf4134571e3 -size 20180 diff --git a/examples/desktop/screenshots/line_stack.png b/examples/desktop/screenshots/line_stack.png index c13f05f04..29d941fd4 100644 --- a/examples/desktop/screenshots/line_stack.png +++ b/examples/desktop/screenshots/line_stack.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5480aefe6e723863b919a4eeb4755310fe7036b27beb8e2e2402e04943ee8c1e -size 201102 +oid sha256:73226917c233f3fd3d7ec0b40d5a7ded904d275c871242dc0578bddf4c19d0bd +size 93687 diff --git a/examples/desktop/screenshots/scatter_cmap.png b/examples/desktop/screenshots/scatter_cmap.png index 87a6e0ded..560f1942d 100644 --- a/examples/desktop/screenshots/scatter_cmap.png +++ b/examples/desktop/screenshots/scatter_cmap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a02d2b5d4735d656d1b754ac3681a7700d961d7e4a43dfaf3a7dd0d4f6516ba6 -size 37808 +oid sha256:9479bb3995bd145a163a2f25592a4c85c52c663d33381efee7743ffc1f16aef1 +size 32894 diff --git a/examples/desktop/screenshots/scatter_present.png b/examples/desktop/screenshots/scatter_present.png deleted file mode 100644 index 08bc610b3..000000000 --- a/examples/desktop/screenshots/scatter_present.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:bd072918f21ed0ce4ea4e1f4499ec1ff66d867cfdc0ecd6b3ed8092141cd348e -size 14195 diff --git a/examples/notebooks/heatmap.ipynb b/examples/notebooks/heatmap.ipynb index 90c07a3cb..7de3af2a0 100644 --- a/examples/notebooks/heatmap.ipynb +++ b/examples/notebooks/heatmap.ipynb @@ -5,9 +5,7 @@ "id": "d8c90f4b-b635-4027-b7d5-080d77bd40a3", "metadata": {}, "source": [ - "# The `HeatmapGraphic` is useful for looking at very large arrays\n", - "\n", - "`ImageGraphic` is limited to a max size of `8192 x 8192`" + "# Looking at very large arrays" ] }, { @@ -40,13 +38,11 @@ }, "outputs": [], "source": [ - "xs = np.linspace(0, 50, 10_000)\n", - "\n", - "sine_data = np.sin(xs)\n", + "xs = np.linspace(0, 1_000, 20_000)\n", "\n", - "cosine_data = np.cos(xs)\n", + "sine = np.sin(np.sqrt(xs))\n", "\n", - "data = np.vstack([(sine_data, cosine_data) for i in range(5)])" + "data = np.vstack([sine * i for i in range(10_000)])" ] }, { @@ -72,7 +68,7 @@ "source": [ "fig = fpl.Figure()\n", "\n", - "fig[0, 0].add_heatmap(data, cmap=\"viridis\")\n", + "fig[0, 0].add_image(data, cmap=\"viridis\")\n", "\n", "fig.show(maintain_aspect=False)" ] diff --git a/examples/notebooks/linear_region_selector.ipynb b/examples/notebooks/linear_region_selector.ipynb index 2ba40ed54..57a72bdec 100644 --- a/examples/notebooks/linear_region_selector.ipynb +++ b/examples/notebooks/linear_region_selector.ipynb @@ -17,7 +17,7 @@ "source": [ "import fastplotlib as fpl\n", "import numpy as np\n", - "from ipywidgets import IntRangeSlider, FloatRangeSlider, VBox\n", + "# from ipywidgets import IntRangeSlider, FloatRangeSlider, VBox\n", "\n", "fig = fpl.Figure((2, 2))\n", "\n", @@ -25,11 +25,12 @@ "zoomed_prealloc = 1_000\n", "\n", "# data to plot\n", - "xs = np.linspace(0, 100, 1_000)\n", - "sine = np.sin(xs) * 20\n", + "xs = np.linspace(0, 10* np.pi, 1_000)\n", + "sine = np.sin(xs)\n", + "sine += 100\n", "\n", "# make sine along x axis\n", - "sine_graphic_x = fig[0, 0].add_line(sine)\n", + "sine_graphic_x = fig[0, 0].add_line(np.column_stack([xs, sine]), offset=(10, 0, 0))\n", "\n", "# just something that looks different for line along y-axis\n", "sine_y = sine\n", @@ -47,7 +48,7 @@ "ls_y = sine_graphic_y.add_linear_region_selector(axis=\"y\")\n", "\n", "# preallocate array for storing zoomed in data\n", - "zoomed_init = np.column_stack([np.arange(zoomed_prealloc), np.random.rand(zoomed_prealloc)])\n", + "zoomed_init = np.column_stack([np.arange(zoomed_prealloc), np.zeros(zoomed_prealloc)])\n", "\n", "# make line graphics for displaying zoomed data\n", "zoomed_x = fig[1, 0].add_line(zoomed_init)\n", @@ -62,62 +63,54 @@ " # interpolate to preallocated size\n", " return np.interp(x, xp, fp=subdata[:, axis]) # use the y-values\n", "\n", - "\n", + "@ls_x.add_event_handler(\"selection\")\n", "def set_zoom_x(ev):\n", " \"\"\"sets zoomed x selector data\"\"\"\n", - " selected_data = ev.pick_info[\"selected_data\"]\n", - " zoomed_x.data = interpolate(selected_data, axis=1) # use the y-values\n", + " # get the selected data\n", + " selected_data = ev.get_selected_data()\n", + " if selected_data.size == 0:\n", + " # no data selected\n", + " zoomed_x.data[:, 1] = 0\n", + "\n", + " # set the y-values\n", + " zoomed_x.data[:, 1] = interpolate(selected_data, axis=1)\n", " fig[1, 0].auto_scale()\n", "\n", "\n", "def set_zoom_y(ev):\n", - " \"\"\"sets zoomed y selector data\"\"\"\n", - " selected_data = ev.pick_info[\"selected_data\"]\n", - " zoomed_y.data = -interpolate(selected_data, axis=0) # use the x-values\n", + " \"\"\"sets zoomed x selector data\"\"\"\n", + " # get the selected data\n", + " selected_data = ev.get_selected_data()\n", + " if selected_data.size == 0:\n", + " # no data selected\n", + " zoomed_y.data[:, 0] = 0\n", + "\n", + " # set the x-values\n", + " zoomed_y.data[:, 0] = -interpolate(selected_data, axis=1)\n", " fig[1, 1].auto_scale()\n", "\n", "\n", - "# update zoomed plots when bounds change\n", - "ls_x.selection.add_event_handler(set_zoom_x)\n", - "ls_y.selection.add_event_handler(set_zoom_y)\n", - "\n", - "fig.show()" - ] - }, - { - "cell_type": "markdown", - "id": "0bad4a35-f860-4f85-9061-920154ab682b", - "metadata": {}, - "source": [ - "### On the x-axis we have a 1-1 mapping from the data that we have passed and the line geometry positions. So the `bounds` min max corresponds directly to the data indices." + "fig.show(maintain_aspect=False)" ] }, { "cell_type": "code", "execution_count": null, - "id": "2c96a3ff-c2e7-4683-8097-8491e97dd6d3", + "id": "2f29e913-c4f8-44a6-8692-eb14436849a5", "metadata": {}, "outputs": [], "source": [ - "ls_x.selection()" + "sine_graphic_x.data[:, 1].ptp()" ] }, { "cell_type": "code", "execution_count": null, - "id": "3ec71e3f-291c-43c6-a954-0a082ba5981c", + "id": "1947a477-5dd2-4df9-aecd-6967c6ab45fe", "metadata": {}, "outputs": [], "source": [ - "ls_x.get_selected_indices()" - ] - }, - { - "cell_type": "markdown", - "id": "1588a89e-1da4-4ada-92e2-7437ba942065", - "metadata": {}, - "source": [ - "### However, for the y-axis line we have passed a 2D array where we've used a linspace, so there is not a 1-1 mapping from the data to the line geometry positions. Use `get_selected_indices()` to get the indices of the data bounded by the current selection. In addition the position of the Graphic is not `(0, 0)`. You must use `get_selected_indices()` whenever you want the indices of the selected data." + "np.clip(-0.1, 0, 10)" ] }, { @@ -127,7 +120,7 @@ "metadata": {}, "outputs": [], "source": [ - "ls_y.selection()" + "ls_y.selection" ] }, { @@ -173,17 +166,18 @@ " subplot.add_line(zoomed_init, name=\"zoomed\")\n", "\n", "\n", + "@selector.add_event_handler(\"selection\")\n", "def update_zoomed_subplots(ev):\n", " \"\"\"update the zoomed subplots\"\"\"\n", - " zoomed_data = selector.get_selected_data()\n", + " zoomed_data = ev.get_selected_data()\n", " \n", " for i in range(len(zoomed_data)):\n", + " # interpolate y-vals\n", " data = interpolate(zoomed_data[i], axis=1)\n", - " fig_stack[i + 1, 0][\"zoomed\"].data = data\n", + " fig_stack[i + 1, 0][\"zoomed\"].data[:, 1] = data\n", " fig_stack[i + 1, 0].auto_scale()\n", "\n", "\n", - "selector.selection.add_event_handler(update_zoomed_subplots)\n", "fig_stack.show()" ] }, @@ -195,50 +189,6 @@ "# Large line stack with selector" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "d5ffb678-c989-49ee-85a9-4fd7822f033c", - "metadata": {}, - "outputs": [], - "source": [ - "import fastplotlib as fpl\n", - "import numpy as np\n", - "\n", - "# data to plot\n", - "xs = np.linspace(0, 250, 10_000)\n", - "sine = np.sin(xs) * 20\n", - "cosine = np.cos(xs) * 20\n", - "\n", - "fig_stack_large = fpl.Figure((1, 2))\n", - "\n", - "# sines and cosines\n", - "sines = [sine] * 1_00\n", - "cosines = [cosine] * 1_00\n", - "\n", - "# make line stack\n", - "line_stack = fig_stack_large[0, 0].add_line_stack(sines + cosines, separation=50)\n", - "\n", - "# make selector\n", - "stack_selector = line_stack.add_linear_region_selector(padding=200)\n", - "\n", - "zoomed_line_stack = fig_stack_large[0, 1].add_line_stack([zoomed_init] * 2_000, separation=50, name=\"zoomed\")\n", - " \n", - "def update_zoomed_stack(ev):\n", - " \"\"\"update the zoomed subplots\"\"\"\n", - " zoomed_data = stack_selector.get_selected_data()\n", - " \n", - " for i in range(len(zoomed_data)):\n", - " data = interpolate(zoomed_data[i], axis=1)\n", - " zoomed_line_stack.graphics[i].data = data\n", - " \n", - " fig_stack_large[0, 1].auto_scale()\n", - "\n", - "\n", - "stack_selector.selection.add_event_handler(update_zoomed_stack)\n", - "fig_stack_large.show()" - ] - }, { "cell_type": "code", "execution_count": null, diff --git a/examples/notebooks/linear_selector.ipynb b/examples/notebooks/linear_selector.ipynb index e9c8e664a..bac8df182 100644 --- a/examples/notebooks/linear_selector.ipynb +++ b/examples/notebooks/linear_selector.ipynb @@ -5,7 +5,7 @@ "id": "a06e1fd9-47df-42a3-a76c-19e23d7b89fd", "metadata": {}, "source": [ - "## `LinearSelector`, draggable selector that can optionally associated with an ipywidget." + "## `LinearSelector`, draggable selector that can also be linked to an ipywidget slider" ] }, { @@ -16,7 +16,6 @@ "outputs": [], "source": [ "import fastplotlib as fpl\n", - "from fastplotlib.graphics.selectors import Synchronizer\n", "\n", "import numpy as np\n", "from ipywidgets import VBox, IntSlider, FloatSlider\n", @@ -35,16 +34,14 @@ "selector2 = sine_graphic.add_linear_selector(20)\n", "selector3 = sine_graphic.add_linear_selector(40)\n", "\n", - "ss = Synchronizer(selector, selector2, selector3)\n", - "\n", + "# one of the selectors will change the line colors when it moves\n", + "@selector.add_event_handler(\"selection\")\n", "def set_color_at_index(ev):\n", " # changes the color at the index where the slider is\n", - " ix = ev.pick_info[\"selected_index\"]\n", - " g = ev.pick_info[\"graphic\"].parent\n", + " ix = ev.get_selected_index()\n", + " g = ev.graphic.parent\n", " g.colors[ix] = \"green\"\n", "\n", - "selector.selection.add_event_handler(set_color_at_index)\n", - "\n", "# fastplotlib LineSelector can make an ipywidget slider and return it :D \n", "ipywidget_slider = selector.make_ipywidget_slider()\n", "ipywidget_slider.description = \"slider1\"\n", @@ -57,7 +54,15 @@ "selector3.add_ipywidget_handler(ipywidget_slider3, step=0.1)\n", "\n", "fig[0, 0].auto_scale()\n", - "fig.show(add_widgets=[ipywidget_slider])" + "VBox([fig.show(), ipywidget_slider, ipywidget_slider2, ipywidget_slider3])" + ] + }, + { + "cell_type": "markdown", + "id": "d83caca6-e9b6-45df-b93c-0dfe0498d20e", + "metadata": {}, + "source": [ + "Double click the first selctor, and then use `Shift` + Right/Left Arrow Key to move it!" ] }, { @@ -67,13 +72,16 @@ "metadata": {}, "outputs": [], "source": [ + "# this controls the step-size of arrow key movements\n", "selector.step = 0.1" ] }, { "cell_type": "markdown", "id": "3b0f448f-bbe4-4b87-98e3-093f561c216c", - "metadata": {}, + "metadata": { + "jp-MarkdownHeadingCollapsed": true + }, "source": [ "### Drag linear selectors with the mouse, hold \"Shift\" to synchronize movement of all the selectors" ] @@ -105,8 +113,6 @@ "for i, c in enumerate(colors):\n", " sel = sine_stack.add_linear_selector(i * 100, color=c, name=str(i))\n", " selectors.append(sel)\n", - " \n", - "ss = Synchronizer(*selectors)\n", "\n", "fig.show()" ] diff --git a/examples/notebooks/lines_cmap.ipynb b/examples/notebooks/lines_cmap.ipynb index dbcbb3e16..3ceb25326 100644 --- a/examples/notebooks/lines_cmap.ipynb +++ b/examples/notebooks/lines_cmap.ipynb @@ -39,11 +39,11 @@ "xs = np.linspace(-10, 10, 100)\n", "# sine wave\n", "ys = np.sin(xs)\n", - "sine = np.dstack([xs, ys])[0]\n", + "sine = np.column_stack([xs, ys])\n", "\n", "# cosine wave\n", "ys = np.cos(xs)\n", - "cosine = np.dstack([xs, ys])[0]" + "cosine = np.column_stack([xs, ys])" ] }, { @@ -107,6 +107,14 @@ "plot_test(\"lines-cmap-jet\", fig)" ] }, + { + "cell_type": "markdown", + "id": "13c1c034-2b3b-4568-b979-7c0bbea698ae", + "metadata": {}, + "source": [ + "map colors from sine data values by setting the cmap transform" + ] + }, { "cell_type": "code", "execution_count": null, @@ -116,7 +124,7 @@ }, "outputs": [], "source": [ - "fig[0, 0].graphics[0].cmap.values = sine[:, 1]" + "fig[0, 0].graphics[0].cmap.transform = sine[:, 1]" ] }, { @@ -141,7 +149,8 @@ }, "outputs": [], "source": [ - "fig[0, 0].graphics[0].cmap.values = cosine[:, 1]" + "# set transform from cosine\n", + "fig[0, 0].graphics[0].cmap.transform = cosine[:, 1]" ] }, { @@ -166,6 +175,7 @@ }, "outputs": [], "source": [ + "# change cmap\n", "fig[0, 0].graphics[0].cmap = \"viridis\"" ] }, @@ -182,6 +192,14 @@ "plot_test(\"lines-cmap-viridis\", fig)" ] }, + { + "cell_type": "markdown", + "id": "1f52bfdc-8151-4bab-973c-1bac36011802", + "metadata": {}, + "source": [ + "use cmap transform to map for a qualitative transform" + ] + }, { "cell_type": "code", "execution_count": null, @@ -191,7 +209,7 @@ }, "outputs": [], "source": [ - "cmap_values = [0] * 25 + [1] * 5 + [2] * 50 + [3] * 20" + "cmap_transform = [0] * 25 + [1] * 5 + [2] * 50 + [3] * 20" ] }, { @@ -203,7 +221,7 @@ }, "outputs": [], "source": [ - "fig[0, 0].graphics[0].cmap.values = cmap_values" + "fig[0, 0].graphics[0].cmap.transform = cmap_transform" ] }, { diff --git a/examples/notebooks/quickstart.ipynb b/examples/notebooks/quickstart.ipynb index 9bfd822ab..5c5040418 100644 --- a/examples/notebooks/quickstart.ipynb +++ b/examples/notebooks/quickstart.ipynb @@ -156,7 +156,9 @@ }, "outputs": [], "source": [ - "image_graphic.data().shape" + "# some graphic properties behave like arrays\n", + "# access the underlying array using .values\n", + "image_graphic.data.value.shape" ] }, { @@ -209,8 +211,8 @@ }, "outputs": [], "source": [ - "image_graphic.cmap.vmin = 50\n", - "image_graphic.cmap.vmax = 150" + "image_graphic.vmin = 50\n", + "image_graphic.vmax = 150" ] }, { @@ -301,7 +303,7 @@ }, "outputs": [], "source": [ - "image_graphic.cmap.reset_vmin_vmax()" + "image_graphic.reset_vmin_vmax()" ] }, { @@ -500,7 +502,7 @@ }, "outputs": [], "source": [ - "fig_rgb[0, 0][\"rgb-image\"].cmap.vmin = 100" + "fig_rgb[0, 0][\"rgb-image\"].vmin = 100" ] }, { @@ -893,72 +895,6 @@ "plot_test(\"lines-data\", fig_lines)" ] }, - { - "cell_type": "markdown", - "id": "3f6d264b-1b03-407e-9d83-cd6cfb02e706", - "metadata": {}, - "source": [ - "### Toggle the presence of a graphic within the scene" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fcba75b7-9a1e-4aae-9dec-715f7f7456c3", - "metadata": {}, - "outputs": [], - "source": [ - "sinc_graphic.present = False" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "763b9943-53a4-4e2a-b47a-4e9e5c9d7be3", - "metadata": {}, - "outputs": [], - "source": [ - "sinc_graphic.present = True" - ] - }, - { - "cell_type": "markdown", - "id": "86f4e535-ce88-415a-b8d2-53612a2de7b9", - "metadata": {}, - "source": [ - "### You can create callbacks to this too, for example to re-scale the plot w.r.t. graphics that are present in the scene" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "64a20a16-75a5-4772-a849-630ade9be4ff", - "metadata": {}, - "outputs": [], - "source": [ - "sinc_graphic.present.add_event_handler(subplot.auto_scale)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fb093046-c94c-4085-86b4-8cd85cb638ff", - "metadata": {}, - "outputs": [], - "source": [ - "sinc_graphic.present = False" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f05981c3-c768-4631-ae62-6a8407b20c4c", - "metadata": {}, - "outputs": [], - "source": [ - "sinc_graphic.present = True" - ] - }, { "cell_type": "markdown", "id": "05f93e93-283b-45d8-ab31-8d15a7671dd2", @@ -978,12 +914,13 @@ "source": [ "img = iio.imread(\"imageio:camera.png\")\n", "\n", - "subplot.add_image(img[::20, ::20], name=\"image\", cmap=\"gray\")\n", + "subplot.add_image(\n", + " img[::20, ::20],\n", + " name=\"image\",\n", + " cmap=\"gray\",\n", + ")\n", "\n", - "# z axis position -1 so it is below all the lines\n", - "subplot[\"image\"].position_z = -1\n", - "subplot[\"image\"].position_x = -8\n", - "subplot[\"image\"].position_y = -8" + "subplot[\"image\"].offset = (-12, -10, -1)" ] }, { @@ -1282,11 +1219,8 @@ "magnetic_vectors = [np.array([[0, 0, z], [0, y, z]]) for (y, z) in zip(m_ys[::10], zs[::10])]\n", "\n", "# add as a line collection\n", - "fig_em[0, 0].add_line_collection(electric_vectors, colors=\"blue\", thickness=1.5, name=\"e-vec\", z_offset=0)\n", - "fig_em[0, 0].add_line_collection(magnetic_vectors, colors=\"red\", thickness=1.5, name=\"m-vec\", z_offset=0)\n", - "# note that the z_offset in `add_line_collection` is not data-related\n", - "# it is the z-offset for where to place the *graphic*, by default with Orthographic cameras (i.e. 2D views)\n", - "# it will increment by 1 for each line in the collection, we want to disable this so set z_position=0\n", + "fig_em[0, 0].add_line_collection(electric_vectors, colors=\"blue\", thickness=1.5, name=\"e-vec\")\n", + "fig_em[0, 0].add_line_collection(magnetic_vectors, colors=\"red\", thickness=1.5, name=\"m-vec\")\n", "\n", "# axes are a WIP, just draw a white line along z for now\n", "z_axis = np.array([[0, 0, 0], [0, 0, stop]])\n", @@ -1537,8 +1471,8 @@ "source": [ "def update_points(subplot):\n", " # move every point by a small amount\n", - " deltas = np.random.normal(size=scatter_graphic.data().shape, loc=0, scale=0.15)\n", - " scatter_graphic.data = scatter_graphic.data() + deltas \n", + " deltas = np.random.normal(size=scatter_graphic.data.value.shape, loc=0, scale=0.15)\n", + " scatter_graphic.data = scatter_graphic.data[:] + deltas\n", "\n", "subplot_scatter.add_animations(update_points)" ] @@ -2048,7 +1982,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.2" + "version": "3.11.3" } }, "nbformat": 4, diff --git a/examples/notebooks/scatter_sizes_animation.ipynb b/examples/notebooks/scatter_sizes_animation.ipynb index 9ca067bee..0cd301fb1 100644 --- a/examples/notebooks/scatter_sizes_animation.ipynb +++ b/examples/notebooks/scatter_sizes_animation.ipynb @@ -17,16 +17,17 @@ "size_delta_scales = np.array([10, 40, 100], dtype=np.float32)\n", "min_sizes = 6\n", "\n", + "\n", "def update_positions(subplot):\n", - " current_time = time()\n", - " newPositions = points + np.sin(((current_time / 4) % 1)*np.pi)\n", - " subplot.graphics[0].data = newPositions\n", + " g = subplot.graphics[0]\n", + " g.data[:, :-1] += np.sin(((time() / 4))*np.pi)\n", + "\n", "\n", "def update_sizes(subplot):\n", - " current_time = time()\n", - " sin_sample = np.sin(((current_time / 4) % 1)*np.pi)\n", - " size_delta = sin_sample*size_delta_scales\n", - " subplot.graphics[0].sizes = min_sizes + size_delta\n", + " sin_sample = np.abs(np.sin((time() / 1)*np.pi))\n", + " size_delta = sin_sample * size_delta_scales\n", + " subplot.graphics[0].sizes = size_delta\n", + "\n", "\n", "scatter = fig[0, 0].add_scatter(points, colors=[\"red\", \"green\", \"blue\"], sizes=12)\n", "fig[0, 0].add_animations(update_positions, update_sizes)\n", @@ -34,13 +35,6 @@ "fig[0, 0].camera.width = 12\n", "fig.show(autoscale=False)" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { diff --git a/examples/notebooks/screenshots/nb-astronaut.png b/examples/notebooks/screenshots/nb-astronaut.png index 378260288..3e55979ee 100644 --- a/examples/notebooks/screenshots/nb-astronaut.png +++ b/examples/notebooks/screenshots/nb-astronaut.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e584533ea04b9758634ba62dceeb72991861c509d01dc082436c54c272686409 -size 112104 +oid sha256:a0fdb5b319347b4db4611dcf92cf08359c938f42a64b05d0dd163e0ca289e3c3 +size 112299 diff --git a/examples/notebooks/screenshots/nb-astronaut_RGB.png b/examples/notebooks/screenshots/nb-astronaut_RGB.png index bf11bf667..fbb514e3e 100644 --- a/examples/notebooks/screenshots/nb-astronaut_RGB.png +++ b/examples/notebooks/screenshots/nb-astronaut_RGB.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:db9602a610f258803d74ac03cd46447dd5a7ad62241ec26a4c3df30c1d6de299 -size 110408 +oid sha256:2d312ce9097114bc32886c0370861bcf7deebfb4fda99e03817ebec2226eabdc +size 110338 diff --git a/examples/notebooks/screenshots/nb-camera.png b/examples/notebooks/screenshots/nb-camera.png index 9db4005bc..5629bd211 100644 --- a/examples/notebooks/screenshots/nb-camera.png +++ b/examples/notebooks/screenshots/nb-camera.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4bb9080b99c2717e093bf6ae4986bf0689a8d377e137a7022c9c6929b9a335d3 -size 77965 +oid sha256:0a415917cc16f09ab7b78eea5e5579d7dd45b6d92e80d87ba0970e9dd0568eb2 +size 77419 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-set_data.png b/examples/notebooks/screenshots/nb-image-widget-movie-set_data.png index 5be8f55a3..486c89963 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-set_data.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-set_data.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:66a310e312add59a310ff0a50335db97ac557d7f2967d8251a7d811c25a4de28 -size 40517 +oid sha256:3dbb4d04175c5603ff7e56a04438c8f0cfff7deff61889a06c342cedc04ac323 +size 43172 diff --git a/examples/notebooks/screenshots/nb-image-widget-single-gnuplot2.png b/examples/notebooks/screenshots/nb-image-widget-single-gnuplot2.png index b8bf7adeb..02423a02a 100644 --- a/examples/notebooks/screenshots/nb-image-widget-single-gnuplot2.png +++ b/examples/notebooks/screenshots/nb-image-widget-single-gnuplot2.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dbfa1e7aeb7f0a068a33f2f11023a06f834332f7b3d8e4cf97b51222536fd6cb -size 434782 +oid sha256:d7384d1a69629cfcdbebdc9e9e6a152383446f3cb696e69a11543253cdde2e64 +size 434060 diff --git a/examples/notebooks/screenshots/nb-image-widget-single.png b/examples/notebooks/screenshots/nb-image-widget-single.png index 86119e247..408739d6e 100644 --- a/examples/notebooks/screenshots/nb-image-widget-single.png +++ b/examples/notebooks/screenshots/nb-image-widget-single.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7ceee2cdd73092cb84b4b0f2876fc08d838b8a47bb94d431a6c19c8a4793a153 -size 403521 +oid sha256:b57dffe179b6f52d204968085c885d70183d1f6a9a3f5a1dc2d005229b7acd01 +size 404179 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-gaussian.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-gaussian.png index 82cee281f..596548486 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-gaussian.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-gaussian.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:47e3e6cea0e738b2731060488886606f05595cfdfb0d81e6db1aa099dc8e3a84 -size 148181 +oid sha256:da1c660e4fb779ac6a4baed3d329cf80274981288ea076f243bb43aee7fb8eff +size 157731 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-reset.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-reset.png index 0b7832eee..1318be413 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-reset.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-reset.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0af4ceb50ed269aa80667c3012a871c87f73777cd8cb497ebb243b53932b9bad -size 72377 +oid sha256:0d4f6407c3b029b01088fab9522a21f7d90d7a87def1ecbbb45f0fb4f8508f87 +size 69106 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-max-window-13.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-max-window-13.png index 2bc2db3a5..e5fdbdd28 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-max-window-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-max-window-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9053c70da35fd42fe44a76e0ace8788ba79667b33c596409ca1e1f2f6d6ba3ad -size 195906 +oid sha256:e4176109805f4f521a1b630b68df1dce80a63b82a5ed01a6ba4c2cae0dfeb6bd +size 184423 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-13.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-13.png index d5999dd0f..bf9548962 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:77d4a8542a5507e3eda1203a6da29a2f533bbbe2988ad297948c74e44a4337ec -size 177152 +oid sha256:12df56b1045cdaddb94355b7e960aa58137a44eff4ff22aab3596c53ea7944c8 +size 179403 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-5.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-5.png index 29af0398d..7b3e6bfba 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-5.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3422923039d45b20ea6150f0ad545bdf876596ba60b156df5ec4004590a29a3e -size 139029 +oid sha256:d5fd2f0918f4a29769ebb572f5163abb52667cf24e89efdd1d99bc57a0f5f607 +size 140124 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50.png index bb07b8fbb..a72245f3b 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ba9e055298372238ce0cd0c5ac4d75db8cd53f3f4acffbcc22bf7d503b40ec57 -size 79174 +oid sha256:981c925f52ae8789f7f0d24ef3fe34efb7a08f221a7bc6079dd12f01099c3d25 +size 75054 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-99.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-99.png index 6e8274659..19c19dc1f 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-99.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-99.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c6aed15f9f1b6bae442687613c5b04621f42e78f1dbda1e3560b000d652ba0b3 -size 61523 +oid sha256:6a5ecd1f966250ead16a96df996ff39877b4ee28534b7724a4a8e1db9c8984d2 +size 58334 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-gaussian.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-gaussian.png index 28704bd2d..bcf663279 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-gaussian.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-gaussian.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:499fab9183f2528297fdfa3c96a25eb58b2376a44556d790ef06928e0379af3a -size 174612 +oid sha256:dbe1375ae8d2f348ad5d7e030fa67d45c250c6ed263c67179d542d0bd903e0d3 +size 177334 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-reset.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-reset.png index d163fd22a..963290515 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-reset.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-reset.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:72832b86f802ee90b4eb54cb64d64aff59527fe0c7dcb87a4d8ab281ad15726b -size 142136 +oid sha256:0ff341df374d816411e58c82d432e98a9f4cec1551725dafcd65cdb0c43edb12 +size 138235 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-max-window-13.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-max-window-13.png index 52ebd8591..a049a484c 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-max-window-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-max-window-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b414fbb8f6935901b65851de9c7cb37df628d7b24759dc7f407ee130389216a3 -size 371687 +oid sha256:bd4c51f7e07e46d7c660d28563aff1b7d3759387fc10db10addca29dfc0919b0 +size 365838 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-13.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-13.png index 6c406a621..ada15017c 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e0ef52156509f308d972533fb45509ba7451b4d6149400d519aae28274609e41 -size 212053 +oid sha256:1d13cc8a32b40f5c721ab30020632d7dc1c679c8c8e5857476176e986de18ad3 +size 211240 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-5.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-5.png index aaed804b8..2e71fd30d 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-5.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8fd94e597074094dc3718153c8bb94fb0b1bf58e58e34601e5c7281f938f52bd -size 200278 +oid sha256:39044f4bb54038eee17f0d75940fd848b304629858d9e747ac0c81ce076d3c25 +size 199075 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50.png index 3110fa7cf..690b1c578 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:76ab21314fbd7846c1b94aeeed9ef7b97be99d2f2f7f09c13c0474a889c21179 -size 159319 +oid sha256:109a4c8d708114e1b36d1a9fa129dbd9a768617baa953f37500310c0968b688a +size 154169 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-99.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-99.png index 0cfad54e7..3e577698c 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-99.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-99.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:77f247de5374a8cb7ce27a338ab8be880d53d9586b52f646b400901ba70be3aa -size 146217 +oid sha256:568ae05e8889cec56530db0149613826f2697f55d8252cffbd32ff692b565fcf +size 141338 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-init-mean-window-5.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-init-mean-window-5.png index c74807939..1ab48d117 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-init-mean-window-5.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-init-mean-window-5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:33c3dfa77bbc558493634ab83fd1539ef76022a2e20d10e1353d2bd0a0e94a2c -size 183739 +oid sha256:e7847dc083d6df2b20788b42153674c557b627b75db74d9446b06e165aa5a50a +size 182713 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-false.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-false.png index a2841b1d5..0b0f05fc3 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-false.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-false.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:715f7909db0d374c2e618bb41f7a6341a8cc8891b1e3e8678a6f934fd71159a4 -size 127129 +oid sha256:839dd3fdc9db98d7044d88e816c179bff34f30584ba26ce7a96ea3b35fc3374e +size 122463 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-true.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-true.png index 9064f2323..534403b1e 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-true.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-true.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:76708e8e6e6865d700aa5286fca4d58ba4eb91f21ab3b0243bb128e9a84f063c -size 131192 +oid sha256:f7088e11517a4a78492d16746ac8101b2e5e9142ebd61966030c555ab173443e +size 126267 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-init-mean-window-5.png b/examples/notebooks/screenshots/nb-image-widget-zfish-init-mean-window-5.png index 1fbaec974..94993c688 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-init-mean-window-5.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-init-mean-window-5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:51c62474b9ebee76242ef82a710a83b90e0183792790f6a2cd00213642b76755 -size 99519 +oid sha256:709c7ec07e3e37e0415fad3fa542729d2896e8e546b6ea8d1373e7b46005bc26 +size 97278 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-frame-50.png b/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-frame-50.png index 5e0750ac8..27c693c1a 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-frame-50.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-frame-50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7f8f74a0a5fa24e10a88d3723836306913243fa5fc23f46f44bbdae4c0209075 -size 58878 +oid sha256:c66db583e0d455319b665d35a8c5c8a5f717653c11311cdaba90e2c85e64235f +size 58941 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-set-data.png b/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-set-data.png index 8df83fe33..7444d7dbf 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-set-data.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-set-data.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0809b2dda0e773b7f100386f97144c40d36d51cd935c86ef1dcd4a938fce3981 -size 56319 +oid sha256:36867cd793634d00c46c782911abae6e7c579067aeeed891e40ddedbb0c228d9 +size 56505 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-windowrgb.png b/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-windowrgb.png index 5bbefc7ae..3941f3120 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-windowrgb.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-windowrgb.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a2e2e2cf7ac6be1a4fccec54494c3fd48af673765653675438fa2469c549e90c -size 55055 +oid sha256:8b23f3655fcfcd85f6138f4de5cedf228a6dbad0c8cff0c87900042f83b8f409 +size 55269 diff --git a/examples/notebooks/screenshots/nb-lines-underlay.png b/examples/notebooks/screenshots/nb-lines-underlay.png index d4b3d9f6d..e7f6aeb0c 100644 --- a/examples/notebooks/screenshots/nb-lines-underlay.png +++ b/examples/notebooks/screenshots/nb-lines-underlay.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9b12c8f29436be8d17c38f420120ab3d54b0eee9bef751eea2f99d01b1a8fa43 -size 50761 +oid sha256:8ed174c362c7e7c491eba02b32102f59423af41537577c694fdcd54d69c065b3 +size 50422 diff --git a/examples/notebooks/subplots.ipynb b/examples/notebooks/subplots.ipynb index 72b4b3007..c9774029f 100644 --- a/examples/notebooks/subplots.ipynb +++ b/examples/notebooks/subplots.ipynb @@ -136,39 +136,51 @@ "metadata": {}, "outputs": [], "source": [ - "fig[\"subplot0\"][\"rand-image\"].cmap.vmin = 0.6\n", - "fig[\"subplot0\"][\"rand-image\"].cmap.vmax = 0.8" + "fig[\"subplot0\"][\"rand-image\"].vmin = 0.6\n", + "fig[\"subplot0\"][\"rand-image\"].vmax = 0.8" ] }, { "cell_type": "markdown", + "id": "39c8a5acbad7980b", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, "source": [ "If they are not named use .graphics" - ], - "metadata": { - "collapsed": false - } + ] }, { "cell_type": "code", "execution_count": null, + "id": "d27af25002237db5", + "metadata": { + "collapsed": false, + "is_executing": true, + "jupyter": { + "outputs_hidden": false + } + }, "outputs": [], "source": [ "fig[\"subplot0\"].graphics" - ], - "metadata": { - "collapsed": false, - "is_executing": true - } + ] }, { "cell_type": "markdown", + "id": "2299a8ae23e39c37", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, "source": [ "### positional indexing also works" - ], - "metadata": { - "collapsed": false - } + ] }, { "cell_type": "code", @@ -177,17 +189,9 @@ "metadata": {}, "outputs": [], "source": [ - "fig[1, 0][\"rand-image\"].cmap.vim = 0.1\n", - "fig[1, 0][\"rand-image\"].cmap.vmax = 0.3" + "fig[1, 0][\"rand-image\"].vim = 0.1\n", + "fig[1, 0][\"rand-image\"].vmax = 0.3" ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a61e34a5-ee1b-4abb-8718-ec4715ffaa52", - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { diff --git a/examples/notebooks/subplots_simple.ipynb b/examples/notebooks/subplots_simple.ipynb index e519584d3..9ff4e4284 100644 --- a/examples/notebooks/subplots_simple.ipynb +++ b/examples/notebooks/subplots_simple.ipynb @@ -10,7 +10,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "id": "5171a06e-1bdc-4908-9726-3c1fd45dbb9d", "metadata": { "ExecuteTime": { @@ -19,40 +19,7 @@ }, "tags": [] }, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "301d76bd4c5c42c7912cdd28651e2899", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Image(value=b'\\x89PNG\\r\\n\\x1a\\n\\x00\\x00\\x00\\rIHDR\\x00\\x00\\x01,\\x00\\x00\\x007\\x08\\x06\\x00\\x00\\x00\\xb6\\x1bw\\x99\\x…" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Unable to find extension: VK_EXT_swapchain_colorspace\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Available devices:\n", - "✅ (default) | AMD RADV POLARIS10 (ACO) | DiscreteGPU | Vulkan | Mesa 20.3.5 (ACO)\n", - "❗ | llvmpipe (LLVM 11.0.1, 256 bits) | CPU | Vulkan | Mesa 20.3.5 (LLVM 11.0.1)\n", - "✅ | NVIDIA GeForce RTX 3080 | DiscreteGPU | Vulkan | 530.30.02\n", - "❗ | Radeon RX 570 Series (POLARIS10, DRM 3.40.0, 5.10.0-21-amd64, LLVM 11.0.1) | Unknown | OpenGL | \n" - ] - } - ], + "outputs": [], "source": [ "import numpy as np\n", "import fastplotlib as fpl" @@ -165,7 +132,7 @@ }, "outputs": [], "source": [ - "fig[0, 1].graphics[0].cmap.vmax = 0.5" + "fig[0, 1].graphics[0].vmax = 0.5" ] }, { @@ -244,7 +211,7 @@ }, "outputs": [], "source": [ - "fig[\"top-right-plot\"][\"rand-img\"].cmap.vmin = 0.5" + "fig[\"top-right-plot\"][\"rand-img\"].vmin = 0.5" ] }, { diff --git a/examples/notebooks/test_gc.ipynb b/examples/notebooks/test_gc.ipynb index 39f964cf7..57d7bb576 100644 --- a/examples/notebooks/test_gc.ipynb +++ b/examples/notebooks/test_gc.ipynb @@ -93,12 +93,11 @@ "\n", "\n", "for g in objects:\n", - " for feature in g.feature_events:\n", - " if isinstance(g, fpl.LineCollection):\n", - " continue # skip collections for now\n", + " for feature in g._features:\n", + " # if isinstance(g, fpl.LineCollection):?\n", + " # continue # skip collections for now\n", " \n", - " f = getattr(g, feature)\n", - " f.add_event_handler(feature_changed_handler)\n", + " g.add_event_handler(feature_changed_handler, feature)\n", "\n", "fig.show()" ] @@ -136,9 +135,8 @@ "\n", "# add some events onto all the image graphics\n", "for g in iw.managed_graphics:\n", - " for f in g.feature_events:\n", - " fea = getattr(g, f)\n", - " fea.add_event_handler(feature_changed_handler)\n", + " for f in g._features:\n", + " g.add_event_handler(feature_changed_handler, f)\n", "\n", "iw.show()" ] @@ -174,6 +172,14 @@ "source": [ "test_references(old_graphics)" ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "712bb6ea-7244-4e03-8dfa-9419daa34915", + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { @@ -192,7 +198,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.3" + "version": "3.11.2" } }, "nbformat": 4, diff --git a/fastplotlib/VERSION b/fastplotlib/VERSION index d9958b371..0ea3a944b 100644 --- a/fastplotlib/VERSION +++ b/fastplotlib/VERSION @@ -1 +1 @@ -0.1.0.a16 +0.2.0 diff --git a/fastplotlib/graphics/__init__.py b/fastplotlib/graphics/__init__.py index 2a008015e..ff96baa4c 100644 --- a/fastplotlib/graphics/__init__.py +++ b/fastplotlib/graphics/__init__.py @@ -1,15 +1,15 @@ from .line import LineGraphic from .scatter import ScatterGraphic -from .image import ImageGraphic, HeatmapGraphic +from .image import ImageGraphic from .text import TextGraphic from .line_collection import LineCollection, LineStack + __all__ = [ + "LineGraphic", "ImageGraphic", "ScatterGraphic", - "LineGraphic", - "HeatmapGraphic", + "TextGraphic", "LineCollection", "LineStack", - "TextGraphic", ] diff --git a/fastplotlib/graphics/_base.py b/fastplotlib/graphics/_base.py index 3a5b043f5..cab941894 100644 --- a/fastplotlib/graphics/_base.py +++ b/fastplotlib/graphics/_base.py @@ -1,22 +1,28 @@ +from collections import defaultdict +from functools import partial from typing import Any, Literal, TypeAlias import weakref -from warnings import warn -from abc import ABC, abstractmethod -from dataclasses import dataclass import numpy as np import pylinalg as la +from wgpu.gui.base import log_exception -from pygfx import WorldObject - -from ._features import GraphicFeature, PresentFeature, GraphicFeatureIndexable, Deleted +import pygfx +from ._features import ( + BufferManager, + Deleted, + Name, + Offset, + Rotation, + Visible, +) HexStr: TypeAlias = str # dict that holds all world objects for a given python kernel/session # Graphic objects only use proxies to WorldObjects -WORLD_OBJECTS: dict[HexStr, WorldObject] = dict() #: {hex id str: WorldObject} +WORLD_OBJECTS: dict[HexStr, pygfx.WorldObject] = dict() #: {hex id str: WorldObject} PYGFX_EVENTS = [ @@ -35,9 +41,11 @@ ] -class BaseGraphic: +class Graphic: + _features = {} + def __init_subclass__(cls, **kwargs): - """set the type of the graphic in lower case like "image", "line_collection", etc.""" + # set the type of the graphic in lower case like "image", "line_collection", etc. cls.type = ( cls.__name__.lower() .replace("graphic", "") @@ -45,22 +53,24 @@ def __init_subclass__(cls, **kwargs): .replace("stack", "_stack") ) + # set of all features + cls._features = { + *cls._features, + "name", + "offset", + "rotation", + "visible", + "deleted", + } super().__init_subclass__(**kwargs) - -class Graphic(BaseGraphic): - feature_events = {} - - def __init_subclass__(cls, **kwargs): - super().__init_subclass__(**kwargs) - # all graphics give off a feature event when deleted - cls.feature_events = {*cls.feature_events, "deleted"} - def __init__( self, name: str = None, + offset: np.ndarray | list | tuple = (0.0, 0.0, 0.0), + rotation: np.ndarray | list | tuple = (0.0, 0.0, 0.0, 1.0), + visible: bool = True, metadata: Any = None, - collection_index: int = None, ): """ @@ -69,6 +79,12 @@ def __init__( name: str, optional name this graphic to use it as a key to access from the plot + offset: (float, float, float), default (0., 0., 0.) + (x, y, z) vector to offset this graphic from the origin + + rotation: (float, float, float, float), default (0, 0, 0, 1) + rotation quaternion + metadata: Any, optional metadata attached to this Graphic, this is for the user to manage @@ -76,116 +92,254 @@ def __init__( if (name is not None) and (not isinstance(name, str)): raise TypeError("Graphic `name` must be of type ") - self._name = name self.metadata = metadata - self.collection_index = collection_index self.registered_callbacks = dict() - self.present = PresentFeature(parent=self) # store hex id str of Graphic instance mem location self._fpl_address: HexStr = hex(id(self)) - self.deleted = Deleted(self, False) - self._plot_area = None + # event handlers + self._event_handlers = defaultdict(set) + + # maps callbacks to their partials + self._event_handler_wrappers = defaultdict(set) + + # all the common features + self._name = Name(name) + self._deleted = Deleted(False) + self._rotation = Rotation(rotation) + self._offset = Offset(offset) + self._visible = Visible(visible) + self._block_events = False + + @property + def supported_events(self) -> tuple[str]: + """events supported by this graphic""" + return (*tuple(self._features), *PYGFX_EVENTS) + @property def name(self) -> str | None: - """str name reference for this item""" - return self._name + """Graphic name""" + return self._name.value @name.setter - def name(self, name: str): - if self.name == name: - return + def name(self, value: str): + self._name.set_value(self, value) + + @property + def offset(self) -> np.ndarray: + """Offset position of the graphic, array: [x, y, z]""" + return self._offset.value + + @offset.setter + def offset(self, value: np.ndarray | list | tuple): + self._offset.set_value(self, value) + + @property + def rotation(self) -> np.ndarray: + """Orientation of the graphic as a quaternion""" + return self._rotation.value + + @rotation.setter + def rotation(self, value: np.ndarray | list | tuple): + self._rotation.set_value(self, value) + + @property + def visible(self) -> bool: + """Whether the graphic is visible""" + return self._visible.value + + @visible.setter + def visible(self, value: bool): + self._visible.set_value(self, value) + + @property + def deleted(self) -> bool: + """used to emit an event after the graphic is deleted""" + return self._deleted.value - if not isinstance(name, str): - raise TypeError("`Graphic` name must be of type ") + @deleted.setter + def deleted(self, value: bool): + self._deleted.set_value(self, value) - if self._plot_area is not None: - self._plot_area._check_graphic_name_exists(name) + @property + def block_events(self) -> bool: + """Used to block events for a graphic and prevent recursion.""" + return self._block_events - self._name = name + @block_events.setter + def block_events(self, value: bool): + self._block_events = value @property - def world_object(self) -> WorldObject: + def world_object(self) -> pygfx.WorldObject: """Associated pygfx WorldObject. Always returns a proxy, real object cannot be accessed directly.""" # We use weakref to simplify garbage collection return weakref.proxy(WORLD_OBJECTS[self._fpl_address]) - def _set_world_object(self, wo: WorldObject): + def _set_world_object(self, wo: pygfx.WorldObject): WORLD_OBJECTS[self._fpl_address] = wo - @property - def position(self) -> np.ndarray: - """position of the graphic, [x, y, z]""" - return self.world_object.world.position + self.world_object.visible = self.visible - @property - def position_x(self) -> float: - """x-axis position of the graphic""" - return self.world_object.world.x + # set offset if it's not (0., 0., 0.) + if not all(self.world_object.world.position == self.offset): + self.offset = self.offset - @property - def position_y(self) -> float: - """y-axis position of the graphic""" - return self.world_object.world.y + # set rotation if it's not (0., 0., 0., 1.) + if not all(self.world_object.world.rotation == self.rotation): + self.rotation = self.rotation + + def unshare_property(self, feature: str): + raise NotImplementedError + + def share_property(self, feature: BufferManager): + raise NotImplementedError @property - def position_z(self) -> float: - """z-axis position of the graphic""" - return self.world_object.world.z + def event_handlers(self) -> list[tuple[str, callable, ...]]: + """ + Registered event handlers. Read-only use ``add_event_handler()`` + and ``remove_event_handler()`` to manage callbacks + """ + return list(self._event_handlers.items()) - @position.setter - def position(self, val): - self.world_object.world.position = val + def add_event_handler(self, *args): + """ + Register an event handler. - @position_x.setter - def position_x(self, val): - self.world_object.world.x = val + Parameters + ---------- + callback: callable, the first argument + Event handler, must accept a single event argument + *types: list of strings + A list of event types, ex: "click", "data", "colors", "pointer_down" - @position_y.setter - def position_y(self, val): - self.world_object.world.y = val + For the available renderer event types, see + https://jupyter-rfb.readthedocs.io/en/stable/events.html - @position_z.setter - def position_z(self, val): - self.world_object.world.z = val + All feature support events, i.e. ``graphic.features`` will give a set of + all features that are evented - @property - def rotation(self): - return self.world_object.local.rotation + Can also be used as a decorator. - @rotation.setter - def rotation(self, val): - self.world_object.local.rotation = val + Example + ------- - @property - def visible(self) -> bool: - """Access or change the visibility.""" - return self.world_object.visible + .. code-block:: py - @visible.setter - def visible(self, v: bool): - """Access or change the visibility.""" - self.world_object.visible = v + def my_handler(event): + print(event) - @property - def children(self) -> list[WorldObject]: - """Return the children of the WorldObject.""" - return self.world_object.children + graphic.add_event_handler(my_handler, "pointer_up", "pointer_down") - def _fpl_add_plot_area_hook(self, plot_area): - self._plot_area = plot_area + Decorator usage example: + + .. code-block:: py + + @graphic.add_event_handler("click") + def my_handler(event): + print(event) + """ + + decorating = not callable(args[0]) + callback = None if decorating else args[0] + types = args if decorating else args[1:] - def __setattr__(self, key, value): - if hasattr(self, key): - attr = getattr(self, key) - if isinstance(attr, GraphicFeature): - attr._set(value) - return + unsupported_events = [t for t in types if t not in self.supported_events] - super().__setattr__(key, value) + if len(unsupported_events) > 0: + raise TypeError( + f"unsupported events passed: {unsupported_events} for {self.__class__.__name__}\n" + f"`graphic.events` will return a tuple of supported events" + ) + + def decorator(_callback): + _callback_wrapper = partial( + self._handle_event, _callback + ) # adds graphic instance as attribute and other things + + for t in types: + # add to our record + self._event_handlers[t].add(_callback) + + if t in self._features: + # fpl feature event + feature = getattr(self, f"_{t}") + feature.add_event_handler(_callback_wrapper) + else: + # wrap pygfx event + self.world_object._event_handlers[t].add(_callback_wrapper) + + # keep track of the partial too + self._event_handler_wrappers[t].add((_callback, _callback_wrapper)) + return _callback + + if decorating: + return decorator + + return decorator(callback) + + def clear_event_handlers(self): + """clear all event handlers added to this graphic""" + for ev, handlers in self.event_handlers: + handlers = list(handlers) + for h in handlers: + self.remove_event_handler(h, ev) + + def _handle_event(self, callback, event: pygfx.Event): + """Wrap pygfx event to add graphic to pick_info""" + event.graphic = self + + if self.block_events: + return + + if event.type in self._features: + # for feature events + event._target = self.world_object + + if isinstance(event, pygfx.PointerEvent): + # map from screen to world space and data space + world_xy = self._plot_area.map_screen_to_world(event) + + # subtract offset to map to data + data_xy = world_xy - self.offset + + # append attributes + event.x_world, event.y_world = world_xy[:2] + event.x_data, event.y_data = data_xy[:2] + + with log_exception(f"Error during handling {event.type} event"): + callback(event) + + def remove_event_handler(self, callback, *types): + # remove from our record first + for t in types: + for wrapper_map in self._event_handler_wrappers[t]: + # TODO: not sure if we can handle this mapping in a better way + if wrapper_map[0] == callback: + wrapper = wrapper_map[1] + self._event_handler_wrappers[t].remove(wrapper_map) + break + else: + raise KeyError( + f"event type: {t} with callback: {callback} is not registered" + ) + + self._event_handlers[t].remove(callback) + # remove callback wrapper from world object if pygfx event + if t in PYGFX_EVENTS: + print("pygfx event") + print(wrapper) + self.world_object.remove_event_handler(wrapper, t) + else: + feature = getattr(self, f"_{t}") + feature.remove_event_handler(wrapper) + + def _fpl_add_plot_area_hook(self, plot_area): + self._plot_area = plot_area def __repr__(self): rval = f"{self.__class__.__name__} @ {hex(id(self))}" @@ -211,6 +365,9 @@ def _fpl_cleanup(self): Optionally implemented in subclasses """ + # remove event handlers + self.clear_event_handlers() + # clear any attached event handlers and animation functions for attr in dir(self): try: @@ -237,9 +394,8 @@ def _fpl_cleanup(self): self.world_object._event_handlers.clear() - feature_names = getattr(self, "feature_events") - for n in feature_names: - fea = getattr(self, n) + for n in self._features: + fea = getattr(self, f"_{n}") fea.clear_event_handlers() def __del__(self): @@ -267,443 +423,3 @@ def rotate(self, alpha: float, axis: Literal["x", "y", "z"] = "y"): f"`axis` must be either `x`, `y`, or `z`. `{axis}` provided instead!" ) self.rotation = la.quat_mul(rot, self.rotation) - - -class Interaction(ABC): - """Mixin class that makes graphics interactive""" - - @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, - callback: callable = None, - bidirectional: bool = False, - ): - """ - Link this graphic to another graphic upon an ``event_type`` to change the ``feature`` - of a ``target`` graphic. - - Parameters - ---------- - event_type: str - can be a pygfx event ("key_down", "key_up","pointer_down", "pointer_move", "pointer_up", - "pointer_enter", "pointer_leave", "click", "double_click", "wheel", "close", "resize") - or appropriate feature event (ex. colors, data, etc.) associated with the graphic (can use - ``graphic_instance.feature_events`` to get a tuple of the valid feature events for the - graphic) - - target: Any - graphic to be linked to - - feature: str - feature (ex. colors, data, etc.) of the target graphic that will change following - the event - - new_data: Any - appropriate data that will be changed in the feature of the target graphic after - the event occurs - - callback: callable, optional - user-specified callable that will handle event, - the callable must take the following four arguments - | ''source'' - this graphic instance - | ''target'' - the graphic to be changed following the event - | ''event'' - the ''pygfx event'' or ''feature event'' that occurs - | ''new_data'' - the appropriate data of the ''target'' that will be changed - - bidirectional: bool, default False - if True, the target graphic is also linked back to this graphic instance using the - same arguments - - For example: - .. code-block::python - - Returns - ------- - None - - """ - if event_type in PYGFX_EVENTS: - self.world_object.add_event_handler(self._event_handler, event_type) - - # make sure event is valid - elif event_type in self.feature_events: - if isinstance(self, GraphicCollection): - feature_instance = getattr(self[:], event_type) - else: - feature_instance = getattr(self, event_type) - - feature_instance.add_event_handler(self._event_handler) - - else: - raise ValueError( - f"Invalid event, valid events are: {PYGFX_EVENTS + self.feature_events}" - ) - - # make sure target feature is valid - if feature is not None: - if feature not in target.feature_events: - raise ValueError( - f"Invalid feature for target, valid features are: {target.feature_events}" - ) - - if event_type not in self.registered_callbacks.keys(): - self.registered_callbacks[event_type] = list() - - callback_data = CallbackData( - target=target, - feature=feature, - new_data=new_data, - callback_function=callback, - ) - - for existing_callback_data in self.registered_callbacks[event_type]: - if existing_callback_data == callback_data: - warn( - "linkage already exists for given event, target, and data, skipping" - ) - return - - self.registered_callbacks[event_type].append(callback_data) - - if bidirectional: - if event_type in PYGFX_EVENTS: - warn("cannot use bidirectional link for pygfx events") - return - - target.link( - event_type=event_type, - target=self, - feature=feature, - new_data=new_data, - callback=callback, - bidirectional=False, # else infinite recursion, otherwise target will call - # this instance .link(), and then it will happen again etc. - ) - - def _event_handler(self, event): - """Handles the event after it occurs when two graphic have been linked together.""" - if event.type in self.registered_callbacks.keys(): - for target_info in self.registered_callbacks[event.type]: - if target_info.callback_function is not None: - # if callback_function is not None, then callback function should handle the entire event - target_info.callback_function( - source=self, - target=target_info.target, - event=event, - new_data=target_info.new_data, - ) - - elif isinstance(self, GraphicCollection): - # if target is a GraphicCollection, then indices will be stored in collection_index - if event.type in self.feature_events: - indices = event.pick_info["collection-index"] - - # for now we only have line collections so this works - else: - # get index of world object that made this event - for i, item in enumerate(self.graphics): - wo = WORLD_OBJECTS[item._fpl_address] - # we only store hex id of worldobject, but worldobject `pick_info` is always the real object - # so if pygfx worldobject triggers an event by itself, such as `click`, etc., this will be - # the real world object in the pick_info and not the proxy - if wo is event.pick_info["world_object"]: - indices = i - target_info.target.set_feature( - feature=target_info.feature, - new_data=target_info.new_data, - indices=indices, - ) - else: - # if target is a single graphic, then indices do not matter - target_info.target.set_feature( - feature=target_info.feature, - new_data=target_info.new_data, - indices=None, - ) - - -@dataclass -class CallbackData: - """Class for keeping track of the info necessary for interactivity after event occurs.""" - - target: Any - feature: str - new_data: Any - callback_function: callable = None - - def __eq__(self, other): - if not isinstance(other, CallbackData): - raise TypeError("Can only compare against other types") - - if other.target is not self.target: - return False - - if not other.feature == self.feature: - return False - - if not other.new_data == self.new_data: - return False - - if (self.callback_function is None) and (other.callback_function is None): - return True - - if other.callback_function is self.callback_function: - return True - - else: - return False - - -@dataclass -class PreviouslyModifiedData: - """Class for keeping track of previously modified data at indices""" - - data: Any - indices: Any - - -# Dict that holds all collection graphics in one python instance -COLLECTION_GRAPHICS: dict[HexStr, Graphic] = dict() - - -class GraphicCollection(Graphic): - """Graphic Collection base class""" - - def __init__(self, name: str = None): - super().__init__(name) - self._graphics: list[str] = list() - - self._graphics_changed: bool = True - self._graphics_array: np.ndarray[Graphic] = None - - @property - def graphics(self) -> np.ndarray[Graphic]: - """The Graphics within this collection. Always returns a proxy to the Graphics.""" - if self._graphics_changed: - proxies = [ - weakref.proxy(COLLECTION_GRAPHICS[addr]) for addr in self._graphics - ] - self._graphics_array = np.array(proxies) - self._graphics_array.flags["WRITEABLE"] = False - self._graphics_changed = False - - return self._graphics_array - - def add_graphic(self, graphic: Graphic, reset_index: False): - """ - Add a graphic to the collection. - - Parameters - ---------- - graphic: Graphic - graphic to add, must be a real ``Graphic`` not a proxy - - reset_index: bool, default ``False`` - reset the collection index - - """ - - if not type(graphic).__name__ == self.child_type: - raise TypeError( - f"Can only add graphics of the same type to a collection, " - f"You can only add {self.child_type} to a {self.__class__.__name__}, " - f"you are trying to add a {graphic.__class__.__name__}." - ) - - addr = graphic._fpl_address - COLLECTION_GRAPHICS[addr] = graphic - - self._graphics.append(addr) - - if reset_index: - self._reset_index() - elif graphic.collection_index is None: - graphic.collection_index = len(self) - - self.world_object.add(graphic.world_object) - - self._graphics_changed = True - - def remove_graphic(self, graphic: Graphic, reset_index: True): - """ - Remove a graphic from the collection. - - Parameters - ---------- - graphic: Graphic - graphic to remove - - reset_index: bool, default ``False`` - reset the collection index - - """ - - self._graphics.remove(graphic._fpl_address) - - if reset_index: - self._reset_index() - - self.world_object.remove(graphic.world_object) - - self._graphics_changed = True - - def __getitem__(self, key): - return CollectionIndexer( - parent=self, - selection=self.graphics[key], - ) - - def __del__(self): - self.world_object.clear() - - for addr in self._graphics: - del COLLECTION_GRAPHICS[addr] - - super().__del__() - - def _reset_index(self): - for new_index, graphic in enumerate(self._graphics): - graphic.collection_index = new_index - - def __len__(self): - return len(self._graphics) - - def __repr__(self): - rval = super().__repr__() - return f"{rval}\nCollection of <{len(self._graphics)}> Graphics" - - -class CollectionIndexer: - """Collection Indexer""" - - def __init__( - self, - parent: GraphicCollection, - selection: list[Graphic], - ): - """ - - Parameters - ---------- - parent: GraphicCollection - the GraphicCollection object that is being indexed - - selection: list of Graphics - a list of the selected Graphics from the parent GraphicCollection based on the ``selection_indices`` - - """ - - self._parent = weakref.proxy(parent) - self._selection = selection - - # we use parent.graphics[0] instead of selection[0] - # because the selection can be empty - for attr_name in self._parent.graphics[0].__dict__.keys(): - attr = getattr(self._parent.graphics[0], attr_name) - if isinstance(attr, GraphicFeature): - collection_feature = CollectionFeature( - self._selection, feature=attr_name - ) - collection_feature.__doc__ = ( - f"indexable <{attr_name}> feature for collection" - ) - setattr(self, attr_name, collection_feature) - - @property - def graphics(self) -> np.ndarray[Graphic]: - """Returns an array of the selected graphics. Always returns a proxy to the Graphic""" - return tuple(self._selection) - - def __setattr__(self, key, value): - if hasattr(self, key): - attr = getattr(self, key) - if isinstance(attr, CollectionFeature): - attr._set(value) - return - - super().__setattr__(key, value) - - def __len__(self): - return len(self._selection) - - def __repr__(self): - return ( - f"{self.__class__.__name__} @ {hex(id(self))}\n" - f"Selection of <{len(self._selection)}> {self._selection[0].__class__.__name__}" - ) - - -class CollectionFeature: - """Collection Feature""" - - def __init__(self, selection: list[Graphic], feature: str): - """ - selection: list of Graphics - a list of the selected Graphics from the parent GraphicCollection based on the ``selection_indices`` - - feature: str - feature of Graphics in the GraphicCollection being indexed - - """ - - self._selection = selection - self._feature = feature - - self._feature_instances: list[GraphicFeature] = list() - - if len(self._selection) > 0: - for graphic in self._selection: - fi = getattr(graphic, self._feature) - self._feature_instances.append(fi) - - if isinstance(fi, GraphicFeatureIndexable): - self._indexable = True - else: - self._indexable = False - else: # it's an empty selection so it doesn't really matter - self._indexable = False - - def _set(self, value): - self[:] = value - - def __getitem__(self, item): - # only for indexable graphic features - return [fi[item] for fi in self._feature_instances] - - def __setitem__(self, key, value): - if self._indexable: - for fi in self._feature_instances: - fi[key] = value - - else: - for fi in self._feature_instances: - fi._set(value) - - def add_event_handler(self, handler: callable): - """Adds an event handler to each of the selected Graphics from the parent GraphicCollection""" - for fi in self._feature_instances: - fi.add_event_handler(handler) - - def remove_event_handler(self, handler: callable): - """Removes an event handler from each of the selected Graphics of the parent GraphicCollection""" - for fi in self._feature_instances: - fi.remove_event_handler(handler) - - def block_events(self, b: bool): - """Blocks event handling from occurring.""" - for fi in self._feature_instances: - fi.block_events(b) - - def __repr__(self): - return f"Collection feature for: <{self._feature}>" diff --git a/fastplotlib/graphics/_collection_base.py b/fastplotlib/graphics/_collection_base.py new file mode 100644 index 000000000..2805c684d --- /dev/null +++ b/fastplotlib/graphics/_collection_base.py @@ -0,0 +1,388 @@ +from typing import Any +import weakref + +import numpy as np + +from ._base import HexStr, Graphic + +# Dict that holds all collection graphics in one python instance +COLLECTION_GRAPHICS: dict[HexStr, Graphic] = dict() + + +class CollectionProperties: + """ + Properties common to all Graphic Collections + + Allows getting and setting the common properties of the individual graphics in the collection + """ + + def _set_feature(self, feature, values): + if not len(values) == len(self): + raise IndexError + + for g, v in zip(self, values): + setattr(g, feature, v) + + @property + def names(self) -> np.ndarray[str | None]: + """get or set the name of the individual graphics in the collection""" + return np.asarray([g.name for g in self]) + + @names.setter + def names(self, values: np.ndarray[str] | list[str]): + self._set_feature("name", values) + + @property + def metadatas(self) -> np.ndarray[str | None]: + """get or set the metadata of the individual graphics in the collection""" + return np.asarray([g.metadata for g in self]) + + @metadatas.setter + def metadatas(self, values: np.ndarray[str] | list[str]): + self._set_feature("metadata", values) + + @property + def offsets(self) -> np.ndarray: + """get or set the offset of the individual graphics in the collection""" + return np.stack([g.offset for g in self]) + + @offsets.setter + def offsets(self, values: np.ndarray | list[np.ndarray]): + self._set_feature("offset", values) + + @property + def rotations(self) -> np.ndarray: + """get or set the rotation of the individual graphics in the collection""" + return np.stack([g.rotation for g in self]) + + @rotations.setter + def rotations(self, values: np.ndarray | list[np.ndarray]): + self._set_feature("rotation", values) + + # TODO: how to work with deleted feature in a collection + + @property + def visibles(self) -> np.ndarray[bool]: + """get or set the offsets of the individual graphics in the collection""" + return np.asarray([g.visible for g in self]) + + @visibles.setter + def visibles(self, values: np.ndarray[bool] | list[bool]): + self._set_feature("visible", values) + + +class CollectionIndexer(CollectionProperties): + """Collection Indexer""" + + def __init__(self, selection: np.ndarray[Graphic], features: set[str]): + """ + + Parameters + ---------- + + selection: np.ndarray of Graphics + array of the selected Graphics from the parent GraphicCollection based on the ``selection_indices`` + + """ + + if isinstance(selection, Graphic): + selection = np.asarray([selection]) + + self._selection = selection + self.features = features + + @property + def graphics(self) -> np.ndarray[Graphic]: + """Returns an array of the selected graphics""" + return tuple(self._selection) + + def add_event_handler(self, *args): + """ + Register an event handler. + + Parameters + ---------- + callback: callable, the first argument + Event handler, must accept a single event argument + *types: list of strings + A list of event types, ex: "click", "data", "colors", "pointer_down" + + For the available renderer event types, see + https://jupyter-rfb.readthedocs.io/en/stable/events.html + + All feature support events, i.e. ``graphic.features`` will give a set of + all features that are evented + + Can also be used as a decorator. + + Example + ------- + + .. code-block:: py + + def my_handler(event): + print(event) + + graphic.add_event_handler(my_handler, "pointer_up", "pointer_down") + + Decorator usage example: + + .. code-block:: py + + @graphic.add_event_handler("click") + def my_handler(event): + print(event) + """ + + decorating = not callable(args[0]) + types = args if decorating else args[1:] + + if decorating: + + def decorator(_callback): + for g in self: + g.add_event_handler(_callback, *types) + return _callback + + return decorator + + for g in self: + g.add_event_handler(*args) + + def remove_event_handler(self, callback, *types): + for g in self: + g.remove_event_handler(callback, *types) + + def clear_event_handlers(self): + for g in self: + g.clear_event_handlers() + + def __getitem__(self, item): + return self.graphics[item] + + def __len__(self): + return len(self._selection) + + def __iter__(self): + self._iter = iter(range(len(self))) + return self + + def __next__(self) -> Graphic: + index = next(self._iter) + + return self.graphics[index] + + def __repr__(self): + return ( + f"{self.__class__.__name__} @ {hex(id(self))}\n" + f"Selection of <{len(self._selection)}> {self._selection[0].__class__.__name__}" + ) + + +class GraphicCollection(Graphic, CollectionProperties): + """Graphic Collection base class""" + + _child_type: type + _indexer: type + + def __init_subclass__(cls, **kwargs): + super().__init_subclass__(**kwargs) + cls._features = cls._child_type._features + + def __init__(self, name: str = None, metadata: Any = None, **kwargs): + super().__init__(name=name, metadata=metadata, **kwargs) + + # list of mem locations of the graphics + self._graphics: list[str] = list() + + self._graphics_changed: bool = True + self._graphics_array: np.ndarray[Graphic] = None + + self._iter = None + + @property + def graphics(self) -> np.ndarray[Graphic]: + """The Graphics within this collection. Always returns a proxy to the Graphics.""" + if self._graphics_changed: + proxies = [ + weakref.proxy(COLLECTION_GRAPHICS[addr]) for addr in self._graphics + ] + self._graphics_array = np.array(proxies) + self._graphics_array.flags["WRITEABLE"] = False + self._graphics_changed = False + + return self._graphics_array + + def add_graphic(self, graphic: Graphic): + """ + Add a graphic to the collection. + + Parameters + ---------- + graphic: Graphic + graphic to add, must be a real ``Graphic`` not a proxy + + """ + + if not type(graphic) == self._child_type: + raise TypeError( + f"Can only add graphics of the same type to a collection.\n" + f"You can only add {self._child_type.__name__} to a {self.__class__.__name__}, " + f"you are trying to add a {graphic.__class__.__name__}." + ) + + addr = graphic._fpl_address + COLLECTION_GRAPHICS[addr] = graphic + + self._graphics.append(addr) + + self.world_object.add(graphic.world_object) + + self._graphics_changed = True + + def remove_graphic(self, graphic: Graphic): + """ + Remove a graphic from the collection. + + Note: Only removes the graphic from the collection. Does not remove + the graphic from the scene, and does not delete the graphic. + + Parameters + ---------- + graphic: Graphic + graphic to remove + + """ + + self._graphics.remove(graphic._fpl_address) + + self.world_object.remove(graphic.world_object) + + self._graphics_changed = True + + def add_event_handler(self, *args): + """ + Register an event handler. + + Parameters + ---------- + callback: callable, the first argument + Event handler, must accept a single event argument + *types: list of strings + A list of event types, ex: "click", "data", "colors", "pointer_down" + + For the available renderer event types, see + https://jupyter-rfb.readthedocs.io/en/stable/events.html + + All feature support events, i.e. ``graphic.features`` will give a set of + all features that are evented + + Can also be used as a decorator. + + Example + ------- + + .. code-block:: py + + def my_handler(event): + print(event) + + graphic.add_event_handler(my_handler, "pointer_up", "pointer_down") + + Decorator usage example: + + .. code-block:: py + + @graphic.add_event_handler("click") + def my_handler(event): + print(event) + """ + + return self[:].add_event_handler(*args) + + def remove_event_handler(self, callback, *types): + """remove an event handler""" + self[:].remove_event_handler(callback, *types) + + def clear_event_handlers(self): + self[:].clear_event_handlers() + + def _fpl_add_plot_area_hook(self, plot_area): + super()._fpl_add_plot_area_hook(plot_area) + + for g in self: + g._fpl_add_plot_area_hook(plot_area) + + def _fpl_cleanup(self): + """ + Cleans up the graphic in preparation for __del__(), such as removing event handlers from + plot renderer, feature event handlers, etc. + + Optionally implemented in subclasses + """ + # clear any attached event handlers and animation functions + self.world_object._event_handlers.clear() + + for g in self: + g._fpl_cleanup() + + def __getitem__(self, key) -> CollectionIndexer: + if np.issubdtype(type(key), np.integer): + addr = self._graphics[key] + return weakref.proxy(COLLECTION_GRAPHICS[addr]) + + return self._indexer(selection=self.graphics[key], features=self._features) + + def __del__(self): + self.world_object.clear() + + for addr in self._graphics: + del COLLECTION_GRAPHICS[addr] + + super().__del__() + + def __len__(self): + return len(self._graphics) + + def __iter__(self): + self._iter = iter(range(len(self))) + return self + + def __next__(self) -> Graphic: + index = next(self._iter) + addr = self._graphics[index] + + return weakref.proxy(COLLECTION_GRAPHICS[addr]) + + def __repr__(self): + rval = super().__repr__() + return f"{rval}\nCollection of <{len(self._graphics)}> Graphics" + + +class CollectionFeature: + """Collection Feature""" + + def __init__(self, selection: np.ndarray[Graphic], feature: str): + """ + selection: list of Graphics + a list of the selected Graphics from the parent GraphicCollection based on the ``selection_indices`` + + feature: str + feature of Graphics in the GraphicCollection being indexed + + """ + + self._selection = selection + self._feature = feature + + self._feature_instances = [getattr(g, feature) for g in self._selection] + + def __getitem__(self, item): + return np.stack([fi[item] for fi in self._feature_instances]) + + def __setitem__(self, key, value): + for fi in self._feature_instances: + fi[key] = value + + def __repr__(self): + return f"Collection feature for: <{self._feature}>" diff --git a/fastplotlib/graphics/_features/__init__.py b/fastplotlib/graphics/_features/__init__.py index fb25db287..e36de089e 100644 --- a/fastplotlib/graphics/_features/__init__.py +++ b/fastplotlib/graphics/_features/__init__.py @@ -1,33 +1,64 @@ -from ._colors import ColorFeature, CmapFeature, ImageCmapFeature, HeatmapCmapFeature -from ._data import PointsDataFeature, ImageDataFeature, HeatmapDataFeature -from ._sizes import PointsSizesFeature -from ._present import PresentFeature -from ._thickness import ThicknessFeature +from ._positions_graphics import ( + VertexColors, + UniformColor, + UniformSize, + Thickness, + VertexPositions, + PointsSizesFeature, + VertexCmap, +) +from ._image import ( + TextureArray, + ImageCmap, + ImageVmin, + ImageVmax, + ImageInterpolation, + ImageCmapInterpolation, + WGPU_MAX_TEXTURE_SIZE, +) from ._base import ( GraphicFeature, - GraphicFeatureIndexable, + BufferManager, FeatureEvent, to_gpu_supported_dtype, ) + +from ._text import ( + TextData, + FontSize, + TextFaceColor, + TextOutlineColor, + TextOutlineThickness, +) + from ._selection_features import LinearSelectionFeature, LinearRegionSelectionFeature -from ._deleted import Deleted +from ._common import Name, Offset, Rotation, Visible, Deleted + __all__ = [ - "ColorFeature", - "CmapFeature", - "ImageCmapFeature", - "HeatmapCmapFeature", - "PointsDataFeature", + "VertexColors", + "UniformColor", + "UniformSize", + "Thickness", + "VertexPositions", "PointsSizesFeature", - "ImageDataFeature", - "HeatmapDataFeature", - "PresentFeature", - "ThicknessFeature", - "GraphicFeature", - "GraphicFeatureIndexable", - "FeatureEvent", - "to_gpu_supported_dtype", + "VertexCmap", + "TextureArray", + "ImageCmap", + "ImageVmin", + "ImageVmax", + "ImageInterpolation", + "ImageCmapInterpolation", + "TextData", + "FontSize", + "TextFaceColor", + "TextOutlineColor", + "TextOutlineThickness", "LinearSelectionFeature", "LinearRegionSelectionFeature", + "Name", + "Offset", + "Rotation", + "Visible", "Deleted", ] diff --git a/fastplotlib/graphics/_features/_base.py b/fastplotlib/graphics/_features/_base.py index 99ebbf436..1b24d3b78 100644 --- a/fastplotlib/graphics/_features/_base.py +++ b/fastplotlib/graphics/_features/_base.py @@ -1,14 +1,17 @@ -from abc import ABC, abstractmethod -from inspect import getfullargspec from warnings import warn -from typing import * -import weakref +from typing import Any, Literal import numpy as np +from numpy.typing import NDArray + +from wgpu.gui.base import log_exception import pygfx +WGPU_MAX_TEXTURE_SIZE = 8192 + + supported_dtypes = [ np.uint8, np.uint16, @@ -41,64 +44,44 @@ def to_gpu_supported_dtype(array): return array -class FeatureEvent: +class FeatureEvent(pygfx.Event): """ - Dataclass that holds feature event information. Has ``type`` and ``pick_info`` attributes. - - Attributes - ---------- - type: str, example "colors" - - pick_info: dict: - - ============== ============================================================================= - key value - ============== ============================================================================= - "index" indices where feature data was changed, ``range`` object or ``List[int]`` - "world_object" world object the feature belongs to - "new_data: the new data for this feature - ============== ============================================================================= - - .. note:: - pick info varies between features, this is just the general structure + **All event instances have the following attributes** + + +------------+-------------+-----------------------------------------------+ + | attribute | type | description | + +============+=============+===============================================+ + | type | str | "colors" - name of the event | + +------------+-------------+-----------------------------------------------+ + | graphic | Graphic | graphic instance that the event is from | + +------------+-------------+-----------------------------------------------+ + | info | dict | event info dictionary (see below) | + +------------+-------------+-----------------------------------------------+ + | target | WorldObject | pygfx rendering engine object for the graphic | + +------------+-------------+-----------------------------------------------+ + | time_stamp | float | time when the event occured, in ms | + +------------+-------------+-----------------------------------------------+ """ - def __init__(self, type: str, pick_info: dict): - self.type = type - self.pick_info = pick_info - - def __repr__(self): - return ( - f"{self.__class__.__name__} @ {hex(id(self))}\n" - f"type: {self.type}\n" - f"pick_info: {self.pick_info}\n" - ) - - -class GraphicFeature(ABC): - def __init__(self, parent, data: Any, collection_index: int = None): - # not shown as a docstring so it doesn't show up in the docs - # - # Parameters - # ---------- - # parent - # - # data: Any - # - # collection_index: int - # if part of a collection, index of this graphic within the collection + def __init__(self, type: str, info: dict): + super().__init__(type=type) + self.info = info - self._parent = weakref.proxy(parent) - self._data = to_gpu_supported_dtype(data) - - self._collection_index = collection_index +class GraphicFeature: + def __init__(self, **kwargs): self._event_handlers = list() self._block_events = False - def __call__(self, *args, **kwargs): - return self._data + @property + def value(self) -> Any: + """Graphic Feature value, must be implemented in subclass""" + raise NotImplemented + + def set_value(self, graphic, value: float): + """Graphic Feature value setter, must be implemented in subclass""" + raise NotImplementedError def block_events(self, val: bool): """ @@ -112,23 +95,14 @@ def block_events(self, val: bool): """ self._block_events = val - @abstractmethod - def _set(self, value): - pass - - def _parse_set_value(self, value): - if isinstance(value, GraphicFeature): - return value() - - return value - def add_event_handler(self, handler: callable): """ Add an event handler. All added event handlers are called when this feature changes. - The ``handler`` can optionally accept a :class:`.FeatureEvent` as the first and only argument. - The ``FeatureEvent`` only has two attributes, ``type`` which denotes the type of event - as a ``str`` in the form of "", such as "color". And ``pick_info`` which contains - information about the event and Graphic that triggered it. + + Used by `Graphic` classes to add to their event handlers, not meant for users. Users + add handlers to Graphic instances only. + + The ``handler`` must accept a :class:`.FeatureEvent` as the first and only argument. Parameters ---------- @@ -164,196 +138,202 @@ def clear_event_handlers(self): """Clear all event handlers""" self._event_handlers.clear() - # TODO: maybe this can be implemented right here in the base class - @abstractmethod - def _feature_changed(self, key: Union[int, slice, Tuple[slice]], new_data: Any): - """Called whenever a feature changes, and it calls all funcs in self._event_handlers""" - pass - def _call_event_handlers(self, event_data: FeatureEvent): if self._block_events: return for func in self._event_handlers: - try: - args = getfullargspec(func).args - - if len(args) > 0: - if args[0] == "self" and not len(args) > 1: - func() - else: - func(event_data) - else: - func() - except TypeError: - warn( - f"Event handler {func} has an unresolvable argspec, calling it without arguments" - ) - func() - - @abstractmethod - def __repr__(self) -> str: - pass - - -def cleanup_slice(key: Union[int, slice], upper_bound) -> Union[slice, int]: - """ - - If the key in an `int`, it just returns it. Otherwise, - it parses it and removes the `None` vals and replaces - them with corresponding values that can be used to - create a `range`, get `len` etc. - - Parameters - ---------- - key - upper_bound - - Returns - ------- - - """ - if isinstance(key, int): - return key - - if isinstance(key, np.ndarray): - return cleanup_array_slice(key, upper_bound) - - if isinstance(key, tuple): - # if tuple of slice we only need the first obj - # since the first obj is the datapoint indices - if isinstance(key[0], slice): - key = key[0] + with log_exception( + f"Error during handling {self.__class__.__name__} event" + ): + func(event_data) + + +class BufferManager(GraphicFeature): + """Smaller wrapper for pygfx.Buffer""" + + def __init__( + self, + data: NDArray | pygfx.Buffer, + buffer_type: Literal["buffer", "texture", "texture-array"] = "buffer", + isolated_buffer: bool = True, + texture_dim: int = 2, + **kwargs, + ): + super().__init__() + if isolated_buffer and not isinstance(data, pygfx.Resource): + # useful if data is read-only, example: memmaps + bdata = np.zeros(data.shape, dtype=data.dtype) + bdata[:] = data[:] else: - raise TypeError("Tuple slicing must have slice object in first position") - - if not isinstance(key, slice): - raise TypeError("Must pass slice or int object") - - start = key.start - stop = key.stop - step = key.step - for attr in [start, stop, step]: - if attr is None: - continue - if attr < 0: - raise IndexError("Negative indexing not supported.") - - if start is None: - start = 0 - - if stop is None: - stop = upper_bound - - elif stop > upper_bound: - raise IndexError( - f"Index: `{stop}` out of bounds for feature array of size: `{upper_bound}`" - ) - - step = key.step - if step is None: - step = 1 + # user's input array is used as the buffer + bdata = data + + if isinstance(data, pygfx.Resource): + # already a buffer, probably used for + # managing another BufferManager, example: VertexCmap manages VertexColors + self._buffer = data + elif buffer_type == "buffer": + self._buffer = pygfx.Buffer(bdata) + elif buffer_type == "texture": + # TODO: placeholder, not currently used since TextureArray is used specifically for Image graphics + self._buffer = pygfx.Texture(bdata, dim=texture_dim) + else: + raise ValueError( + "`data` must be a pygfx.Buffer instance or `buffer_type` must be one of: 'buffer' or 'texture'" + ) - return slice(start, stop, step) + self._event_handlers: list[callable] = list() + self._shared: int = 0 -def cleanup_array_slice(key: np.ndarray, upper_bound) -> Union[np.ndarray, None]: - """ - Cleanup numpy array used for fancy indexing, make sure key[-1] <= upper_bound. + @property + def value(self) -> np.ndarray: + """numpy array object representing the data managed by this buffer""" + return self.buffer.data - Returns None if nothing to change. + def set_value(self, graphic, value): + """Sets values on entire array""" + self[:] = value - Parameters - ---------- - key: np.ndarray - integer or boolean array + @property + def buffer(self) -> pygfx.Buffer | pygfx.Texture: + """managed buffer""" + return self._buffer - upper_bound + @property + def shared(self) -> int: + """Number of graphics that share this buffer""" + return self._shared - Returns - ------- - np.ndarray - integer indexing array + @property + def __array_interface__(self): + raise BufferError( + f"Cannot use graphic feature buffer as an array, use .value instead.\n" + f"Examples: line.data.value, line.colors.value, scatter.data.value, scatter.sizes.value" + ) - """ + def __getitem__(self, item): + return self.buffer.data[item] - if key.ndim > 1: - raise TypeError(f"Can only use 1D boolean or integer arrays for fancy indexing") + def __setitem__(self, key, value): + raise NotImplementedError - # if boolean array convert to integer array of indices - if key.dtype == bool: - key = np.nonzero(key)[0] + def _parse_offset_size( + self, + key: int | slice | np.ndarray[int | bool] | list[bool | int], + upper_bound: int, + ): + """ + parse offset and size for first, i.e. n_datapoints, dimension + """ + if np.issubdtype(type(key), np.integer): + # simplest case, just an int + offset = key + size = 1 + + elif isinstance(key, slice): + # TODO: off-by-one sometimes when step is used + # the offset can be one to the left or the size + # is one extra so it's not really an issue for now + # parse slice + start, stop, step = key.indices(upper_bound) + + # account for backwards indexing + if (start > stop) and step < 0: + offset = stop + else: + offset = start - if key.size < 1: - return None + # slice.indices will give -1 if None is passed + # which just means 0 here since buffers do not + # use negative indexing + offset = max(0, offset) - # make sure indices within bounds of feature buffer range - if key[-1] > upper_bound: - raise IndexError( - f"Index: `{key[-1]}` out of bounds for feature array of size: `{upper_bound}`" - ) + # number of elements to upload + # this is indexing so do not add 1 + size = abs(stop - start) - # make sure indices are integers - if np.issubdtype(key.dtype, np.integer): - return key + elif isinstance(key, (np.ndarray, list)): + if isinstance(key, list): + # convert to array + key = np.array(key) - raise TypeError(f"Can only use 1D boolean or integer arrays for fancy indexing") + if not key.ndim == 1: + raise TypeError( + f"can only use 1D arrays for fancy indexing, you have passed a data with: {key.ndim} dimensions" + ) + if key.dtype == bool: + # convert bool mask to integer indices + key = np.nonzero(key)[0] -class GraphicFeatureIndexable(GraphicFeature): - """An indexable Graphic Feature, colors, data, sizes etc.""" + if not np.issubdtype(key.dtype, np.integer): + # fancy indexing doesn't make sense with non-integer types + raise TypeError( + f"can only using integer or booleans arrays for fancy indexing, your array is of type: {key.dtype}" + ) - def _set(self, value): - value = self._parse_set_value(value) - self[:] = value + if key.size < 1: + # nothing to update + return - @abstractmethod - def __getitem__(self, item): - pass + # convert any negative integer indices to positive indices + key %= upper_bound - @abstractmethod - def __setitem__(self, key, value): - pass + # index of first element to upload + offset = key.min() - @abstractmethod - def _update_range(self, key): - pass + # size range to upload + # add 1 because this is direct + # passing of indices, not a start:stop + size = np.ptp(key) + 1 - @property - @abstractmethod - def buffer(self) -> Union[pygfx.Buffer, pygfx.Texture]: - """Underlying buffer for this feature""" - pass + else: + raise TypeError( + f"invalid key for indexing buffer: {key}\n" + f"valid ways to index buffers are using integers, slices, or fancy indexing with integers or bool" + ) + + return offset, size + + def _update_range( + self, + key: ( + int | slice | np.ndarray[int | bool] | list[bool | int] | tuple[slice, ...] + ), + ): + """ + Uses key from slicing to determine the offset and + size of the buffer to mark for upload to the GPU + """ + upper_bound = self.value.shape[0] - @property - def _upper_bound(self) -> int: - return self._data.shape[0] + if isinstance(key, tuple): + if any([k is Ellipsis for k in key]): + # let's worry about ellipsis later + raise TypeError("ellipses not supported for indexing buffers") + # if multiple dims are sliced, we only need the key for + # the first dimension corresponding to n_datapoints + key: int | np.ndarray[int | bool] | slice = key[0] - def _update_range_indices(self, key): - """Currently used by colors and positions data""" - if not isinstance(key, np.ndarray): - key = cleanup_slice(key, self._upper_bound) + offset, size = self._parse_offset_size(key, upper_bound) + self.buffer.update_range(offset=offset, size=size) - if isinstance(key, int): - self.buffer.update_range(key, size=1) + def _emit_event(self, type: str, key, value): + if len(self._event_handlers) < 1: return - # else if it's a slice obj - if isinstance(key, slice): - if key.step == 1: # we cleaned up the slice obj so step of None becomes 1 - # update range according to size using the offset - self.buffer.update_range(offset=key.start, size=key.stop - key.start) + event_info = { + "key": key, + "value": value, + } + event = FeatureEvent(type, info=event_info) - else: - step = key.step - # convert slice to indices - ixs = range(key.start, key.stop, step) - for ix in ixs: - self.buffer.update_range(ix, size=1) + self._call_event_handlers(event) - # TODO: See how efficient this is with large indexing - elif isinstance(key, np.ndarray): - self.buffer.update_range() + def __len__(self): + raise NotImplementedError - else: - raise TypeError("must pass int or slice to update range") + def __repr__(self): + return f"{self.__class__.__name__} buffer data:\n" f"{self.value.__repr__()}" diff --git a/fastplotlib/graphics/_features/_colors.py b/fastplotlib/graphics/_features/_colors.py deleted file mode 100644 index 48405e74c..000000000 --- a/fastplotlib/graphics/_features/_colors.py +++ /dev/null @@ -1,434 +0,0 @@ -import numpy as np -import pygfx - -from ...utils import ( - make_colors, - get_cmap_texture, - make_pygfx_colors, - parse_cmap_values, - quick_min_max, -) -from ._base import ( - GraphicFeature, - GraphicFeatureIndexable, - cleanup_slice, - FeatureEvent, - cleanup_array_slice, -) - - -class ColorFeature(GraphicFeatureIndexable): - """ - Manages the color buffer for :class:`LineGraphic` or :class:`ScatterGraphic` - - **event pick info:** - - ==================== =============================== ========================================================================= - key type description - ==================== =============================== ========================================================================= - "index" ``numpy.ndarray`` or ``None`` changed indices in the buffer - "new_data" ``numpy.ndarray`` or ``None`` new buffer data at the changed indices - "collection-index" int the index of the graphic within the collection that triggered the event - "world_object" pygfx.WorldObject world object - ==================== =============================== ========================================================================= - - """ - - @property - def buffer(self) -> pygfx.Buffer: - return self._parent.world_object.geometry.colors - - def __getitem__(self, item): - return self.buffer.data[item] - - def __init__( - self, - parent, - colors, - n_colors: int, - alpha: float = 1.0, - collection_index: int = None, - ): - """ - ColorFeature - - Parameters - ---------- - parent: Graphic or GraphicCollection - - colors: str, array, or iterable - specify colors as a single human readable string, RGBA array, - or an iterable of strings or RGBA arrays - - n_colors: int - number of colors to hold, if passing in a single str or single RGBA array - - alpha: float - alpha value for the colors - - """ - # if provided as a numpy array of str - if isinstance(colors, np.ndarray): - if colors.dtype.kind in ["U", "S"]: - colors = colors.tolist() - # if the color is provided as a numpy array - if isinstance(colors, np.ndarray): - if colors.shape == (4,): # single RGBA array - data = np.repeat(np.array([colors]), n_colors, axis=0) - # else assume it's already a stack of RGBA arrays, keep this directly as the data - elif colors.ndim == 2: - if colors.shape[1] != 4 and colors.shape[0] != n_colors: - raise ValueError( - "Valid array color arguments must be a single RGBA array or a stack of " - "RGBA arrays for each datapoint in the shape [n_datapoints, 4]" - ) - data = colors - else: - raise ValueError( - "Valid array color arguments must be a single RGBA array or a stack of " - "RGBA arrays for each datapoint in the shape [n_datapoints, 4]" - ) - - # if the color is provided as an iterable - elif isinstance(colors, (list, tuple, np.ndarray)): - # if iterable of str - if all([isinstance(val, str) for val in colors]): - if not len(colors) == n_colors: - raise ValueError( - f"Valid iterable color arguments must be a `tuple` or `list` of `str` " - f"where the length of the iterable is the same as the number of datapoints." - ) - - data = np.vstack([np.array(pygfx.Color(c)) for c in colors]) - - # if it's a single RGBA array as a tuple/list - elif len(colors) == 4: - c = pygfx.Color(colors) - data = np.repeat(np.array([c]), n_colors, axis=0) - - else: - raise ValueError( - f"Valid iterable color arguments must be a `tuple` or `list` representing RGBA values or " - f"an iterable of `str` with the same length as the number of datapoints." - ) - elif isinstance(colors, str): - if colors == "random": - data = np.random.rand(n_colors, 4) - data[:, -1] = alpha - else: - data = make_pygfx_colors(colors, n_colors) - else: - # assume it's a single color, use pygfx.Color to parse it - data = make_pygfx_colors(colors, n_colors) - - if alpha != 1.0: - data[:, -1] = alpha - - super().__init__(parent, data, collection_index=collection_index) - - def __setitem__(self, key, value): - # parse numerical slice indices - if isinstance(key, slice): - _key = cleanup_slice(key, self._upper_bound) - indices = range(_key.start, _key.stop, _key.step) - - # or single numerical index - elif isinstance(key, (int, np.integer)): - key = cleanup_slice(key, self._upper_bound) - indices = [key] - - elif isinstance(key, tuple): - if not isinstance(value, (float, int, np.ndarray)): - raise ValueError( - "If using multiple-fancy indexing for color, you can only set numerical" - "values since this sets the RGBA array data directly." - ) - - if len(key) != 2: - raise ValueError( - "fancy indexing for colors must be 2-dimension, i.e. [n_datapoints, RGBA]" - ) - - # set the user passed data directly - self.buffer.data[key] = value - - # update range - # first slice obj is going to be the indexing so use key[0] - # key[1] is going to be RGBA so get rid of it to pass to _update_range - # _key = cleanup_slice(key[0], self._upper_bound) - self._update_range(key) - self._feature_changed(key, value) - return - - elif isinstance(key, np.ndarray): - key = cleanup_array_slice(key, self._upper_bound) - if key is None: - return - - indices = key - - else: - raise TypeError( - "Graphic features only support integer and numerical fancy indexing" - ) - - new_data_size = len(indices) - - if not isinstance(value, np.ndarray): - color = np.array(pygfx.Color(value)) # pygfx color parser - # make it of shape [n_colors_modify, 4] - new_colors = np.repeat( - np.array([color]).astype(np.float32), new_data_size, axis=0 - ) - - # if already a numpy array - elif isinstance(value, np.ndarray): - # if a single color provided as numpy array - if value.shape == (4,): - new_colors = value.astype(np.float32) - # if there are more than 1 datapoint color to modify - if new_data_size > 1: - new_colors = np.repeat( - np.array([new_colors]).astype(np.float32), new_data_size, axis=0 - ) - - elif value.ndim == 2: - if value.shape[1] != 4 and value.shape[0] != new_data_size: - raise ValueError( - "numpy array passed to color must be of shape (4,) or (n_colors_modify, 4)" - ) - # if there is a single datapoint to change color of but user has provided shape [1, 4] - if new_data_size == 1: - new_colors = value.ravel().astype(np.float32) - else: - new_colors = value.astype(np.float32) - - else: - raise ValueError( - "numpy array passed to color must be of shape (4,) or (n_colors_modify, 4)" - ) - - self.buffer.data[key] = new_colors - - self._update_range(key) - self._feature_changed(key, new_colors) - - def _update_range(self, key): - self._update_range_indices(key) - - def _feature_changed(self, key, new_data): - key = cleanup_slice(key, self._upper_bound) - if isinstance(key, int): - indices = [key] - elif isinstance(key, slice): - indices = range(key.start, key.stop, key.step) - elif isinstance(key, np.ndarray): - indices = key - else: - raise TypeError("feature changed key must be slice or int") - - pick_info = { - "index": indices, - "collection-index": self._collection_index, - "world_object": self._parent.world_object, - "new_data": new_data, - } - - event_data = FeatureEvent(type="colors", pick_info=pick_info) - - self._call_event_handlers(event_data) - - def __repr__(self) -> str: - s = f"ColorsFeature for {self._parent}. Call `.colors()` to get values." - return s - - -class CmapFeature(ColorFeature): - """ - Indexable colormap feature, mostly wraps colors and just provides a way to set colormaps. - - Same event pick info as :class:`ColorFeature` - """ - - def __init__(self, parent, colors, cmap_name: str, cmap_values: np.ndarray): - # Skip the ColorFeature's __init__ - super(ColorFeature, self).__init__(parent, colors) - - self._cmap_name = cmap_name - self._cmap_values = cmap_values - - def __setitem__(self, key, cmap_name): - key = cleanup_slice(key, self._upper_bound) - if not isinstance(key, (slice, np.ndarray)): - raise TypeError( - "Cannot set cmap on single indices, must pass a slice object, " - "numpy.ndarray or set it on the entire data." - ) - - if isinstance(key, slice): - n_colors = len(range(key.start, key.stop, key.step)) - - else: - # numpy array - n_colors = key.size - - colors = parse_cmap_values( - n_colors=n_colors, cmap_name=cmap_name, cmap_values=self._cmap_values - ) - - self._cmap_name = cmap_name - super().__setitem__(key, colors) - - @property - def name(self) -> str: - return self._cmap_name - - @property - def values(self) -> np.ndarray: - return self._cmap_values - - @values.setter - def values(self, values: np.ndarray): - if not isinstance(values, np.ndarray): - values = np.array(values) - - colors = parse_cmap_values( - n_colors=self().shape[0], cmap_name=self._cmap_name, cmap_values=values - ) - - self._cmap_values = values - - super().__setitem__(slice(None), colors) - - def __repr__(self) -> str: - s = f"CmapFeature for {self._parent}, to get name or values: `.cmap.name`, `.cmap.values`" - return s - - -class ImageCmapFeature(GraphicFeature): - """ - Colormap for :class:`ImageGraphic`. - - .cmap() returns the Texture buffer for the cmap. - - .cmap.name returns the cmap name as a str. - - **event pick info:** - - ================ =================== =============== - key type description - ================ =================== =============== - "index" ``None`` not used - "name" ``str`` colormap name - "world_object" pygfx.WorldObject world object - "vmin" ``float`` minimum value - "vmax" ``float`` maximum value - ================ =================== =============== - - """ - - def __init__(self, parent, cmap: str): - cmap_texture_view = get_cmap_texture(cmap) - super().__init__(parent, cmap_texture_view) - self._name = cmap - - def _set(self, cmap_name: str): - if self._parent.data().ndim > 2: - return - - self._parent.world_object.material.map.data[:] = make_colors(256, cmap_name) - self._parent.world_object.material.map.update_range((0, 0, 0), size=(256, 1, 1)) - self._name = cmap_name - - self._feature_changed(key=None, new_data=self._name) - - @property - def name(self) -> str: - return self._name - - @property - def vmin(self) -> float: - """Minimum contrast limit.""" - return self._parent.world_object.material.clim[0] - - @vmin.setter - def vmin(self, value: float): - """Minimum contrast limit.""" - self._parent.world_object.material.clim = ( - value, - self._parent.world_object.material.clim[1], - ) - self._feature_changed(key=None, new_data=None) - - @property - def vmax(self) -> float: - """Maximum contrast limit.""" - return self._parent.world_object.material.clim[1] - - @vmax.setter - def vmax(self, value: float): - """Maximum contrast limit.""" - self._parent.world_object.material.clim = ( - self._parent.world_object.material.clim[0], - value, - ) - self._feature_changed(key=None, new_data=None) - - def reset_vmin_vmax(self): - """Reset vmin vmax values based on current data""" - self.vmin, self.vmax = quick_min_max(self._parent.data()) - - def _feature_changed(self, key, new_data): - # this is a non-indexable feature so key=None - - pick_info = { - "index": None, - "world_object": self._parent.world_object, - "name": self._name, - "vmin": self.vmin, - "vmax": self.vmax, - } - - event_data = FeatureEvent(type="cmap", pick_info=pick_info) - - self._call_event_handlers(event_data) - - def __repr__(self) -> str: - s = f"ImageCmapFeature for {self._parent}. Use `.cmap.name` to get str name of cmap." - return s - - -class HeatmapCmapFeature(ImageCmapFeature): - """ - Colormap for :class:`HeatmapGraphic` - - Same event pick info as :class:`ImageCmapFeature` - """ - - def _set(self, cmap_name: str): - # in heatmap we use one material for all ImageTiles - self._parent._material.map.data[:] = make_colors(256, cmap_name) - self._parent._material.map.update_range((0, 0, 0), size=(256, 1, 1)) - self._name = cmap_name - - self._feature_changed(key=None, new_data=self.name) - - @property - def vmin(self) -> float: - """Minimum contrast limit.""" - return self._parent._material.clim[0] - - @vmin.setter - def vmin(self, value: float): - """Minimum contrast limit.""" - self._parent._material.clim = (value, self._parent._material.clim[1]) - - @property - def vmax(self) -> float: - """Maximum contrast limit.""" - return self._parent._material.clim[1] - - @vmax.setter - def vmax(self, value: float): - """Maximum contrast limit.""" - self._parent._material.clim = (self._parent._material.clim[0], value) diff --git a/fastplotlib/graphics/_features/_common.py b/fastplotlib/graphics/_features/_common.py new file mode 100644 index 000000000..fe32a485f --- /dev/null +++ b/fastplotlib/graphics/_features/_common.py @@ -0,0 +1,123 @@ +import numpy as np + +from ._base import GraphicFeature, FeatureEvent + + +class Name(GraphicFeature): + """Graphic name""" + + def __init__(self, value: str): + self._value = value + super().__init__() + + @property + def value(self) -> str: + return self._value + + def set_value(self, graphic, value: str): + if not isinstance(value, str): + raise TypeError("`Graphic` name must be of type ") + + if graphic._plot_area is not None: + graphic._plot_area._check_graphic_name_exists(value) + + self._value = value + + event = FeatureEvent(type="name", info={"value": value}) + self._call_event_handlers(event) + + +class Offset(GraphicFeature): + """Offset position of the graphic, [x, y, z]""" + + def __init__(self, value: np.ndarray | list | tuple): + self._validate(value) + self._value = np.array(value) + self._value.flags.writeable = False + super().__init__() + + def _validate(self, value): + if not len(value) == 3: + raise ValueError("offset must be a list, tuple, or array of 3 float values") + + @property + def value(self) -> np.ndarray: + return self._value + + def set_value(self, graphic, value: np.ndarray | list | tuple): + self._validate(value) + + graphic.world_object.world.position = value + self._value = graphic.world_object.world.position.copy() + self._value.flags.writeable = False + + event = FeatureEvent(type="offset", info={"value": value}) + self._call_event_handlers(event) + + +class Rotation(GraphicFeature): + """Graphic rotation quaternion""" + + def __init__(self, value: np.ndarray | list | tuple): + self._validate(value) + self._value = np.array(value) + self._value.flags.writeable = False + super().__init__() + + def _validate(self, value): + if not len(value) == 4: + raise ValueError( + "rotation quaternion must be a list, tuple, or array of 4 float values" + ) + + @property + def value(self) -> np.ndarray: + return self._value + + def set_value(self, graphic, value: np.ndarray | list | tuple): + self._validate(value) + + graphic.world_object.world.rotation = value + self._value = graphic.world_object.world.rotation.copy() + self._value.flags.writeable = False + + event = FeatureEvent(type="rotation", info={"value": value}) + self._call_event_handlers(event) + + +class Visible(GraphicFeature): + """Access or change the visibility.""" + + def __init__(self, value: bool): + self._value = value + super().__init__() + + @property + def value(self) -> bool: + return self._value + + def set_value(self, graphic, value: bool): + graphic.world_object.visible = value + self._value = value + + event = FeatureEvent(type="visible", info={"value": value}) + self._call_event_handlers(event) + + +class Deleted(GraphicFeature): + """ + Used when a graphic is deleted, triggers events that can be useful to indicate this graphic has been deleted + """ + + def __init__(self, value: bool): + self._value = value + super().__init__() + + @property + def value(self) -> bool: + return self._value + + def set_value(self, graphic, value: bool): + self._value = value + event = FeatureEvent(type="deleted", info={"value": value}) + self._call_event_handlers(event) diff --git a/fastplotlib/graphics/_features/_data.py b/fastplotlib/graphics/_features/_data.py deleted file mode 100644 index bcfe9446a..000000000 --- a/fastplotlib/graphics/_features/_data.py +++ /dev/null @@ -1,219 +0,0 @@ -from typing import * - -import numpy as np - -import pygfx - -from ._base import ( - GraphicFeatureIndexable, - cleanup_slice, - FeatureEvent, - to_gpu_supported_dtype, - cleanup_array_slice, -) - - -class PointsDataFeature(GraphicFeatureIndexable): - """ - Access to the vertex buffer data shown in the graphic. - Supports fancy indexing if the data array also supports it. - """ - - def __init__(self, parent, data: Any, collection_index: int = None): - data = self._fix_data(data, parent) - super().__init__(parent, data, collection_index=collection_index) - - @property - def buffer(self) -> pygfx.Buffer: - return self._parent.world_object.geometry.positions - - def __getitem__(self, item): - return self.buffer.data[item] - - def _fix_data(self, data, parent): - graphic_type = parent.__class__.__name__ - - data = to_gpu_supported_dtype(data) - - if data.ndim == 1: - # for scatter if we receive just 3 points in a 1d array, treat it as just a single datapoint - # this is different from fix_data for LineGraphic since there we assume that a 1d array - # is just y-values - if graphic_type == "ScatterGraphic": - data = np.array([data]) - elif graphic_type == "LineGraphic": - data = np.dstack([np.arange(data.size, dtype=data.dtype), data])[0] - - if data.shape[1] != 3: - if data.shape[1] != 2: - raise ValueError(f"Must pass 1D, 2D or 3D data to {graphic_type}") - - # zeros for z - zs = np.zeros(data.shape[0], dtype=data.dtype) - - data = np.dstack([data[:, 0], data[:, 1], zs])[0] - - return data - - def __setitem__(self, key, value): - if isinstance(key, np.ndarray): - # make sure 1D array of int or boolean - key = cleanup_array_slice(key, self._upper_bound) - - # put data into right shape if they're only indexing datapoints - if isinstance(key, (slice, int, np.ndarray, np.integer)): - value = self._fix_data(value, self._parent) - # otherwise assume that they have the right shape - # numpy will throw errors if it can't broadcast - - self.buffer.data[key] = value - self._update_range(key) - # avoid creating dicts constantly if there are no events to handle - if len(self._event_handlers) > 0: - self._feature_changed(key, value) - - def _update_range(self, key): - self._update_range_indices(key) - - def _feature_changed(self, key, new_data): - if key is not None: - key = cleanup_slice(key, self._upper_bound) - if isinstance(key, (int, np.integer)): - indices = [key] - elif isinstance(key, slice): - indices = range(key.start, key.stop, key.step) - elif isinstance(key, np.ndarray): - indices = key - elif key is None: - indices = None - - pick_info = { - "index": indices, - "collection-index": self._collection_index, - "world_object": self._parent.world_object, - "new_data": new_data, - } - - event_data = FeatureEvent(type="data", pick_info=pick_info) - - self._call_event_handlers(event_data) - - def __repr__(self) -> str: - s = f"PointsDataFeature for {self._parent}, call `.data()` to get values" - return s - - -class ImageDataFeature(GraphicFeatureIndexable): - """ - Access to the Texture buffer shown in an ImageGraphic. - """ - - def __init__(self, parent, data: Any): - if data.ndim not in (2, 3): - raise ValueError( - "`data.ndim` must be 2 or 3, ImageGraphic data shape must be " - "``[x_dim, y_dim]`` or ``[x_dim, y_dim, rgb]``" - ) - - super().__init__(parent, data) - - @property - def buffer(self) -> pygfx.Texture: - """Texture buffer for the image data""" - return self._parent.world_object.geometry.grid - - def update_gpu(self): - """Update the GPU with the buffer""" - self._update_range(None) - - def __call__(self, *args, **kwargs): - return self.buffer.data - - def __getitem__(self, item): - return self.buffer.data[item] - - def __setitem__(self, key, value): - # make sure float32 - value = to_gpu_supported_dtype(value) - - self.buffer.data[key] = value - self._update_range(key) - - # avoid creating dicts constantly if there are no events to handle - if len(self._event_handlers) > 0: - self._feature_changed(key, value) - - def _update_range(self, key): - self.buffer.update_range((0, 0, 0), size=self.buffer.size) - - def _feature_changed(self, key, new_data): - if key is not None: - key = cleanup_slice(key, self._upper_bound) - if isinstance(key, int): - indices = [key] - elif isinstance(key, slice): - indices = range(key.start, key.stop, key.step) - elif key is None: - indices = None - - pick_info = { - "index": indices, - "world_object": self._parent.world_object, - "new_data": new_data, - } - - event_data = FeatureEvent(type="data", pick_info=pick_info) - - self._call_event_handlers(event_data) - - def __repr__(self) -> str: - s = f"ImageDataFeature for {self._parent}, call `.data()` to get values" - return s - - -class HeatmapDataFeature(ImageDataFeature): - @property - def buffer(self) -> List[pygfx.Texture]: - """list of Texture buffer for the image data""" - return [img.geometry.grid for img in self._parent.world_object.children] - - def __getitem__(self, item): - return self._data[item] - - def __call__(self, *args, **kwargs): - return self._data - - def __setitem__(self, key, value): - # make sure supported type, not float64 etc. - value = to_gpu_supported_dtype(value) - - self._data[key] = value - self._update_range(key) - - # avoid creating dicts constantly if there are no events to handle - if len(self._event_handlers) > 0: - self._feature_changed(key, value) - - def _update_range(self, key): - for buffer in self.buffer: - buffer.update_range((0, 0, 0), size=buffer.size) - - def _feature_changed(self, key, new_data): - if key is not None: - key = cleanup_slice(key, self._upper_bound) - if isinstance(key, int): - indices = [key] - elif isinstance(key, slice): - indices = range(key.start, key.stop, key.step) - elif key is None: - indices = None - - pick_info = { - "index": indices, - "world_object": self._parent.world_object, - "new_data": new_data, - } - - event_data = FeatureEvent(type="data", pick_info=pick_info) - - self._call_event_handlers(event_data) diff --git a/fastplotlib/graphics/_features/_deleted.py b/fastplotlib/graphics/_features/_deleted.py deleted file mode 100644 index 7900385eb..000000000 --- a/fastplotlib/graphics/_features/_deleted.py +++ /dev/null @@ -1,41 +0,0 @@ -from ._base import GraphicFeature, FeatureEvent - - -class Deleted(GraphicFeature): - """ - Used when a graphic is deleted, triggers events that can be useful to indicate this graphic has been deleted - - **event pick info:** - - ==================== ======================== ========================================================================= - key type description - ==================== ======================== ========================================================================= - "collection-index" int the index of the graphic within the collection that triggered the event - "world_object" pygfx.WorldObject world object - ==================== ======================== ========================================================================= - """ - - def __init__(self, parent, value: bool): - super().__init__(parent, value) - - def _set(self, value: bool): - value = self._parse_set_value(value) - self._feature_changed(key=None, new_data=value) - - def _feature_changed(self, key, new_data): - # this is a non-indexable feature so key=None - - pick_info = { - "index": None, - "collection-index": self._collection_index, - "world_object": self._parent.world_object, - "new_data": new_data, - } - - event_data = FeatureEvent(type="deleted", pick_info=pick_info) - - self._call_event_handlers(event_data) - - def __repr__(self) -> str: - s = f"DeletedFeature for {self._parent}" - return s diff --git a/fastplotlib/graphics/_features/_image.py b/fastplotlib/graphics/_features/_image.py new file mode 100644 index 000000000..e31184c4b --- /dev/null +++ b/fastplotlib/graphics/_features/_image.py @@ -0,0 +1,262 @@ +from itertools import product + +from math import ceil + +import numpy as np + +import pygfx +from ._base import GraphicFeature, FeatureEvent, WGPU_MAX_TEXTURE_SIZE + +from ...utils import ( + make_colors, + get_cmap_texture, +) + + +# manages an array of 8192x8192 Textures representing chunks of an image +class TextureArray(GraphicFeature): + def __init__(self, data, isolated_buffer: bool = True): + super().__init__() + + data = self._fix_data(data) + + if isolated_buffer: + # useful if data is read-only, example: memmaps + self._value = np.zeros(data.shape, dtype=data.dtype) + self.value[:] = data[:] + else: + # user's input array is used as the buffer + self._value = data + + # data start indices for each Texture + self._row_indices = np.arange( + 0, + ceil(self.value.shape[0] / WGPU_MAX_TEXTURE_SIZE) * WGPU_MAX_TEXTURE_SIZE, + WGPU_MAX_TEXTURE_SIZE, + ) + self._col_indices = np.arange( + 0, + ceil(self.value.shape[1] / WGPU_MAX_TEXTURE_SIZE) * WGPU_MAX_TEXTURE_SIZE, + WGPU_MAX_TEXTURE_SIZE, + ) + + # buffer will be an array of textures + self._buffer: np.ndarray[pygfx.Texture] = np.empty( + shape=(self.row_indices.size, self.col_indices.size), dtype=object + ) + + self._iter = None + + # iterate through each chunk of passed `data` + # create a pygfx.Texture from this chunk + for _, buffer_index, data_slice in self: + texture = pygfx.Texture(self.value[data_slice], dim=2) + + self.buffer[buffer_index] = texture + + self._shared: int = 0 + + @property + def value(self) -> np.ndarray: + return self._value + + def set_value(self, graphic, value): + self[:] = value + + @property + def buffer(self) -> np.ndarray[pygfx.Texture]: + return self._buffer + + @property + def row_indices(self) -> np.ndarray: + """ + row indices that are used to chunk the big data array + into individual Textures on the GPU + """ + return self._row_indices + + @property + def col_indices(self) -> np.ndarray: + """ + column indices that are used to chunk the big data array + into individual Textures on the GPU + """ + return self._col_indices + + @property + def shared(self) -> int: + return self._shared + + def _fix_data(self, data): + if data.ndim not in (2, 3): + raise ValueError( + "image data must be 2D with or without an RGB(A) dimension, i.e. " + "it must be of shape [x, y], [x, y, 3] or [x, y, 4]" + ) + + # let's just cast to float32 always + return data.astype(np.float32) + + def __iter__(self): + self._iter = product(enumerate(self.row_indices), enumerate(self.col_indices)) + return self + + def __next__(self) -> tuple[pygfx.Texture, tuple[int, int], tuple[slice, slice]]: + """ + Iterate through each Texture within the texture array + + Returns + ------- + Texture, tuple[int, int], tuple[slice, slice] + | Texture: pygfx.Texture + | tuple[int, int]: chunk index, i.e corresponding index of ``self.buffer`` array + | tuple[slice, slice]: data slice of big array in this chunk and Texture + """ + (chunk_row, data_row_start), (chunk_col, data_col_start) = next(self._iter) + + # indices for to self.buffer for this chunk + chunk_index = (chunk_row, chunk_col) + + # stop indices of big data array for this chunk + row_stop = min(self.value.shape[0] - 1, data_row_start + WGPU_MAX_TEXTURE_SIZE) + col_stop = min(self.value.shape[1] - 1, data_col_start + WGPU_MAX_TEXTURE_SIZE) + + # row and column slices that slice the data for this chunk from the big data array + data_slice = (slice(data_row_start, row_stop), slice(data_col_start, col_stop)) + + # texture for this chunk + texture = self.buffer[chunk_index] + + return texture, chunk_index, data_slice + + def __getitem__(self, item): + return self.value[item] + + def __setitem__(self, key, value): + self.value[key] = value + + for texture in self.buffer.ravel(): + texture.update_range((0, 0, 0), texture.size) + + event = FeatureEvent("data", info={"key": key, "value": value}) + self._call_event_handlers(event) + + def __len__(self): + return self.buffer.size + + +class ImageVmin(GraphicFeature): + """lower contrast limit""" + + def __init__(self, value: float): + self._value = value + super().__init__() + + @property + def value(self) -> float: + return self._value + + def set_value(self, graphic, value: float): + vmax = graphic._material.clim[1] + graphic._material.clim = (value, vmax) + self._value = value + + event = FeatureEvent(type="vmin", info={"value": value}) + self._call_event_handlers(event) + + +class ImageVmax(GraphicFeature): + """upper contrast limit""" + + def __init__(self, value: float): + self._value = value + super().__init__() + + @property + def value(self) -> float: + return self._value + + def set_value(self, graphic, value: float): + vmin = graphic._material.clim[0] + graphic._material.clim = (vmin, value) + self._value = value + + event = FeatureEvent(type="vmax", info={"value": value}) + self._call_event_handlers(event) + + +class ImageCmap(GraphicFeature): + """colormap for texture""" + + def __init__(self, value: str): + self._value = value + self.texture = get_cmap_texture(value) + super().__init__() + + @property + def value(self) -> str: + return self._value + + def set_value(self, graphic, value: str): + new_colors = make_colors(256, value) + graphic._material.map.data[:] = new_colors + graphic._material.map.update_range((0, 0, 0), size=(256, 1, 1)) + + self._value = value + event = FeatureEvent(type="cmap", info={"value": value}) + self._call_event_handlers(event) + + +class ImageInterpolation(GraphicFeature): + """Image interpolation method""" + + def __init__(self, value: str): + self._validate(value) + self._value = value + super().__init__() + + def _validate(self, value): + if value not in ["nearest", "linear"]: + raise ValueError("`interpolation` must be one of 'nearest' or 'linear'") + + @property + def value(self) -> str: + return self._value + + def set_value(self, graphic, value: str): + self._validate(value) + + graphic._material.interpolation = value + + self._value = value + event = FeatureEvent(type="interpolation", info={"value": value}) + self._call_event_handlers(event) + + +class ImageCmapInterpolation(GraphicFeature): + """Image cmap interpolation method""" + + def __init__(self, value: str): + self._validate(value) + self._value = value + super().__init__() + + def _validate(self, value): + if value not in ["nearest", "linear"]: + raise ValueError( + "`cmap_interpolation` must be one of 'nearest' or 'linear'" + ) + + @property + def value(self) -> str: + return self._value + + def set_value(self, graphic, value: str): + self._validate(value) + + # common material for all image tiles + graphic._material.map_interpolation = value + + self._value = value + event = FeatureEvent(type="cmap_interpolation", info={"value": value}) + self._call_event_handlers(event) diff --git a/fastplotlib/graphics/_features/_positions_graphics.py b/fastplotlib/graphics/_features/_positions_graphics.py new file mode 100644 index 000000000..ee7927a36 --- /dev/null +++ b/fastplotlib/graphics/_features/_positions_graphics.py @@ -0,0 +1,458 @@ +from typing import Any, List + +import numpy as np +import pygfx + +from ...utils import ( + parse_cmap_values, +) +from ._base import ( + GraphicFeature, + BufferManager, + FeatureEvent, + to_gpu_supported_dtype, +) +from .utils import parse_colors + + +class VertexColors(BufferManager): + """ + + **info dict** + +------------+-----------------------------------------------------------+----------------------------------------------------------------------------------+ + | dict key | value type | value description | + +============+===========================================================+==================================================================================+ + | key | int | slice | np.ndarray[int | bool] | tuple[slice, ...] | key at which colors were indexed/sliced | + +------------+-----------------------------------------------------------+----------------------------------------------------------------------------------+ + | value | np.ndarray | new color values for points that were changed, shape is [n_points_changed, RGBA] | + +------------+-----------------------------------------------------------+----------------------------------------------------------------------------------+ + | user_value | str | np.ndarray | tuple[float] | list[float] | list[str] | user input value that was parsed into the RGBA array | + +------------+-----------------------------------------------------------+----------------------------------------------------------------------------------+ + + """ + + def __init__( + self, + colors: str | np.ndarray | tuple[float] | list[float] | list[str], + n_colors: int, + alpha: float = None, + isolated_buffer: bool = True, + ): + """ + Manages the vertex color buffer for :class:`LineGraphic` or :class:`ScatterGraphic` + + Parameters + ---------- + colors: str | np.ndarray | tuple[float, float, float, float] | list[str] | list[float] | int | float + specify colors as a single human-readable string, RGBA array, + or an iterable of strings or RGBA arrays + + n_colors: int + number of colors, if passing in a single str or single RGBA array + + alpha: float, optional + alpha value for the colors + + """ + data = parse_colors(colors, n_colors, alpha) + + super().__init__(data=data, isolated_buffer=isolated_buffer) + + def __setitem__( + self, + key: int | slice | np.ndarray[int | bool] | tuple[slice, ...], + user_value: str | np.ndarray | tuple[float] | list[float] | list[str], + ): + user_key = key + + if isinstance(key, tuple): + # directly setting RGBA values for points, we do no parsing + if not isinstance(user_value, (int, float, np.ndarray)): + raise TypeError( + "Can only set from int, float, or array to set colors directly by slicing the entire array" + ) + value = user_value + + elif isinstance(key, int): + # set color of one point + n_colors = 1 + value = parse_colors(user_value, n_colors) + + elif isinstance(key, slice): + # find n_colors by converting slice to range and then parse colors + start, stop, step = key.indices(self.value.shape[0]) + + n_colors = len(range(start, stop, step)) + + value = parse_colors(user_value, n_colors) + + elif isinstance(key, (np.ndarray, list)): + if isinstance(key, list): + # convert to array + key = np.array(key) + + # make sure it's 1D + if not key.ndim == 1: + raise TypeError( + "If slicing colors with an array, it must be a 1D bool or int array" + ) + + if key.dtype == bool: + # make sure len is same + if not key.size == self.buffer.data.shape[0]: + raise IndexError( + f"Length of array for fancy indexing must match number of datapoints.\n" + f"There are {len(self.buffer.data.shape[0])} datapoints and you have passed {key.size} indices" + ) + n_colors = np.count_nonzero(key) + + elif np.issubdtype(key.dtype, np.integer): + n_colors = key.size + + else: + raise TypeError( + "If slicing colors with an array, it must be a 1D bool or int array" + ) + + value = parse_colors(user_value, n_colors) + + else: + raise TypeError( + f"invalid key for setting colors, you may set colors using integer indices, slices, or " + f"fancy indexing using an array of integers or bool" + ) + + self.buffer.data[key] = value + + self._update_range(key) + + if len(self._event_handlers) < 1: + return + + event_info = { + "key": user_key, + "value": value, + "user_value": user_value, + } + + event = FeatureEvent("colors", info=event_info) + self._call_event_handlers(event) + + def __len__(self): + return len(self.buffer.data) + + +# Manages uniform color for line or scatter material +class UniformColor(GraphicFeature): + def __init__( + self, value: str | np.ndarray | tuple | list | pygfx.Color, alpha: float = 1.0 + ): + v = (*tuple(pygfx.Color(value))[:-1], alpha) # apply alpha + self._value = pygfx.Color(v) + super().__init__() + + @property + def value(self) -> pygfx.Color: + return self._value + + def set_value(self, graphic, value: str | np.ndarray | tuple | list | pygfx.Color): + value = pygfx.Color(value) + graphic.world_object.material.color = value + self._value = value + + event = FeatureEvent(type="colors", info={"value": value}) + self._call_event_handlers(event) + + +# manages uniform size for scatter material +class UniformSize(GraphicFeature): + def __init__(self, value: int | float): + self._value = float(value) + super().__init__() + + @property + def value(self) -> float: + return self._value + + def set_value(self, graphic, value: float | int): + graphic.world_object.material.size = float(value) + self._value = value + + event = FeatureEvent(type="sizes", info={"value": value}) + self._call_event_handlers(event) + + +class VertexPositions(BufferManager): + """ + +----------+----------------------------------------------------------+------------------------------------------------------------------------------------------+ + | dict key | value type | value description | + +==========+==========================================================+==========================================================================================+ + | key | int | slice | np.ndarray[int | bool] | tuple[slice, ...] | key at which vertex positions data were indexed/sliced | + +----------+----------------------------------------------------------+------------------------------------------------------------------------------------------+ + | value | np.ndarray | float | list[float] | new data values for points that were changed, shape depends on the indices that were set | + +----------+----------------------------------------------------------+------------------------------------------------------------------------------------------+ + + """ + + def __init__(self, data: Any, isolated_buffer: bool = True): + """ + Manages the vertex positions buffer shown in the graphic. + Supports fancy indexing if the data array also supports it. + """ + + data = self._fix_data(data) + super().__init__(data, isolated_buffer=isolated_buffer) + + def _fix_data(self, data): + # data = to_gpu_supported_dtype(data) + + if data.ndim == 1: + # if user provides a 1D array, assume these are y-values + data = np.column_stack([np.arange(data.size, dtype=data.dtype), data]) + + if data.shape[1] != 3: + if data.shape[1] != 2: + raise ValueError(f"Must pass 1D, 2D or 3D data") + + # zeros for z + zs = np.zeros(data.shape[0], dtype=data.dtype) + + # column stack [x, y, z] to make data of shape [n_points, 3] + data = np.column_stack([data[:, 0], data[:, 1], zs]) + + return to_gpu_supported_dtype(data) + + def __setitem__( + self, + key: int | slice | np.ndarray[int | bool] | tuple[slice, ...], + value: np.ndarray | float | list[float], + ): + # directly use the key to slice the buffer + self.buffer.data[key] = value + + # _update_range handles parsing the key to + # determine offset and size for GPU upload + self._update_range(key) + + self._emit_event("data", key, value) + + def __len__(self): + return len(self.buffer.data) + + +class PointsSizesFeature(BufferManager): + """ + +----------+-------------------------------------------------------------------+----------------------------------------------+ + | dict key | value type | value description | + +==========+===================================================================+==============================================+ + | key | int | slice | np.ndarray[int | bool] | list[int | bool] | key at which point sizes indexed/sliced | + +----------+-------------------------------------------------------------------+----------------------------------------------+ + | value | int | float | np.ndarray | list[int | float] | tuple[int | float] | new size values for points that were changed | + +----------+-------------------------------------------------------------------+----------------------------------------------+ + """ + + def __init__( + self, + sizes: int | float | np.ndarray | list[int | float] | tuple[int | float], + n_datapoints: int, + isolated_buffer: bool = True, + ): + """ + Manages sizes buffer of scatter points. + """ + sizes = self._fix_sizes(sizes, n_datapoints) + super().__init__(data=sizes, isolated_buffer=isolated_buffer) + + def _fix_sizes( + self, + sizes: int | float | np.ndarray | list[int | float] | tuple[int | float], + n_datapoints: int, + ): + if np.issubdtype(type(sizes), np.number): + # single value given + sizes = np.full( + n_datapoints, sizes, dtype=np.float32 + ) # force it into a float to avoid weird gpu errors + + elif isinstance( + sizes, (np.ndarray, tuple, list) + ): # if it's not a ndarray already, make it one + sizes = np.asarray(sizes, dtype=np.float32) # read it in as a numpy.float32 + if (sizes.ndim != 1) or (sizes.size != n_datapoints): + raise ValueError( + f"sequence of `sizes` must be 1 dimensional with " + f"the same length as the number of datapoints" + ) + + else: + raise TypeError( + "sizes must be a single , , or a sequence (array, list, tuple) of int" + "or float with the length equal to the number of datapoints" + ) + + if np.count_nonzero(sizes < 0) > 1: + raise ValueError( + "All sizes must be positive numbers greater than or equal to 0.0." + ) + + return sizes + + def __setitem__( + self, + key: int | slice | np.ndarray[int | bool] | list[int | bool], + value: int | float | np.ndarray | list[int | float] | tuple[int | float], + ): + # this is a very simple 1D buffer, no parsing required, directly set buffer + self.buffer.data[key] = value + self._update_range(key) + + self._emit_event("sizes", key, value) + + def __len__(self): + return len(self.buffer.data) + + +class Thickness(GraphicFeature): + """line thickness""" + + def __init__(self, value: float): + self._value = value + super().__init__() + + @property + def value(self) -> float: + return self._value + + def set_value(self, graphic, value: float): + graphic.world_object.material.thickness = value + self._value = value + + event = FeatureEvent(type="thickness", info={"value": value}) + self._call_event_handlers(event) + + +class VertexCmap(BufferManager): + """ + Sliceable colormap feature, manages a VertexColors instance and + provides a way to set colormaps with arbitrary transforms + """ + + def __init__( + self, + vertex_colors: VertexColors, + cmap_name: str | None, + transform: np.ndarray | None, + alpha: float = 1.0, + ): + super().__init__(data=vertex_colors.buffer) + + self._vertex_colors = vertex_colors + self._cmap_name = cmap_name + self._transform = transform + self._alpha = alpha + + if self._cmap_name is not None: + if not isinstance(self._cmap_name, str): + raise TypeError( + f"cmap name must be of type , you have passed: {self._cmap_name} of type: {type(self._cmap_name)}" + ) + + if self._transform is not None: + self._transform = np.asarray(self._transform) + + n_datapoints = vertex_colors.value.shape[0] + + colors = parse_cmap_values( + n_colors=n_datapoints, + cmap_name=self._cmap_name, + transform=self._transform, + ) + colors[:, -1] = alpha + # set vertex colors from cmap + self._vertex_colors[:] = colors + + def __setitem__(self, key: slice, cmap_name): + if not isinstance(key, slice): + raise TypeError( + "fancy indexing not supported for VertexCmap, only slices " + "of a continuous are supported for apply a cmap" + ) + if key.step is not None: + raise TypeError( + "step sized indexing not currently supported for setting VertexCmap, " + "slices must be a continuous region" + ) + + # parse slice + start, stop, step = key.indices(self.value.shape[0]) + n_elements = len(range(start, stop, step)) + + colors = parse_cmap_values( + n_colors=n_elements, cmap_name=cmap_name, transform=self._transform + ) + colors[:, -1] = self.alpha + + self._cmap_name = cmap_name + self._vertex_colors[key] = colors + + # TODO: should we block vertex_colors from emitting an event? + # Because currently this will result in 2 emitted events, one + # for cmap and another from the colors + self._emit_event("cmap", key, cmap_name) + + @property + def name(self) -> str: + return self._cmap_name + + @property + def transform(self) -> np.ndarray | None: + """Get or set the cmap transform. Maps values from the transform array to the cmap colors""" + return self._transform + + @transform.setter + def transform( + self, + values: np.ndarray | list[float | int], + indices: slice | list | np.ndarray = None, + ): + if self._cmap_name is None: + raise AttributeError( + "cmap name is not set, set the cmap name before setting the transform" + ) + + values = np.asarray(values) + + colors = parse_cmap_values( + n_colors=self.value.shape[0], cmap_name=self._cmap_name, transform=values + ) + + colors[:, -1] = self.alpha + + self._transform = values + + if indices is None: + indices = slice(None) + + self._vertex_colors[indices] = colors + + self._emit_event("cmap.transform", indices, values) + + @property + def alpha(self) -> float: + """Get or set the alpha level""" + return self._alpha + + @alpha.setter + def alpha(self, value: float, indices: slice | list | np.ndarray = None): + self._vertex_colors[indices, -1] = value + self._alpha = value + + self._emit_event("cmap.alpha", indices, value) + + def __len__(self): + raise NotImplementedError( + "len not implemented for `cmap`, use len(colors) instead" + ) + + def __repr__(self): + return f"{self.__class__.__name__} | cmap: {self.name}\ntransform: {self.transform}" diff --git a/fastplotlib/graphics/_features/_present.py b/fastplotlib/graphics/_features/_present.py deleted file mode 100644 index a73d66523..000000000 --- a/fastplotlib/graphics/_features/_present.py +++ /dev/null @@ -1,72 +0,0 @@ -from pygfx import Scene, Group - -from ._base import GraphicFeature, FeatureEvent - - -class PresentFeature(GraphicFeature): - """ - Toggles if the object is present in the scene, different from visible. - Useful for computing bounding boxes from the Scene to only include graphics - that are present. - - **event pick info:** - - ==================== ======================== ========================================================================= - key type description - ==================== ======================== ========================================================================= - "index" ``None`` not used - "new_data" ``bool`` new data, ``True`` or ``False`` - "collection-index" int the index of the graphic within the collection that triggered the event - "world_object" pygfx.WorldObject world object - ==================== ======================== ========================================================================= - """ - - def __init__(self, parent, present: bool = True, collection_index: int = False): - self._scene = None - super().__init__(parent, present, collection_index) - - def _set(self, present: bool): - present = self._parse_set_value(present) - - i = 0 - wo = self._parent.world_object - while not isinstance(self._scene, (Group, Scene)): - wo_parent = wo.parent - self._scene = wo_parent - wo = wo_parent - i += 1 - - if i > 100: - raise RecursionError( - "Exceeded scene graph depth threshold, cannot find Scene associated with" - "this graphic." - ) - - if present: - if self._parent.world_object not in self._scene.children: - self._scene.add(self._parent.world_object) - - else: - if self._parent.world_object in self._scene.children: - self._scene.remove(self._parent.world_object) - - self._data = present - self._feature_changed(key=None, new_data=present) - - def _feature_changed(self, key, new_data): - # this is a non-indexable feature so key=None - - pick_info = { - "index": None, - "collection-index": self._collection_index, - "world_object": self._parent.world_object, - "new_data": new_data, - } - - event_data = FeatureEvent(type="present", pick_info=pick_info) - - self._call_event_handlers(event_data) - - def __repr__(self) -> str: - s = f"PresentFeature for {self._parent}, call `.present()` to get values" - return s diff --git a/fastplotlib/graphics/_features/_selection_features.py b/fastplotlib/graphics/_features/_selection_features.py index 21e5d0a09..71ba53425 100644 --- a/fastplotlib/graphics/_features/_selection_features.py +++ b/fastplotlib/graphics/_features/_selection_features.py @@ -1,4 +1,4 @@ -from typing import Tuple, Union, Any +from typing import Sequence import numpy as np @@ -7,196 +7,186 @@ class LinearSelectionFeature(GraphicFeature): - # A bit much to have a class for this but this allows it to integrate with the fastplotlib callback system """ - Manages the linear selection and callbacks + **additional event attributes:** - **event pick info** + +--------------------+----------+------------------------------------+ + | attribute | type | description | + +====================+==========+====================================+ + | get_selected_index | callable | returns indices under the selector | + +--------------------+----------+------------------------------------+ - =================== =============================== ================================================================================================= - key type selection - =================== =============================== ================================================================================================= - "selected_index" ``int`` the graphic data index that corresponds to the selector position - "world_object" ``pygfx.WorldObject`` pygfx WorldObject - "new_data" ``numpy.ndarray`` or ``None`` the new selector position in world coordinates, not necessarily the same as "selected_index" - "graphic" ``Graphic`` the selector graphic - "delta" ``numpy.ndarray`` the delta vector of the graphic in NDC - "pygfx_event" ``pygfx.Event`` pygfx Event - =================== =============================== ================================================================================================= + **info dict:** + + +----------+------------+-------------------------------+ + | dict key | value type | value description | + +==========+============+===============================+ + | value | np.ndarray | new x or y value of selection | + +----------+------------+-------------------------------+ """ - def __init__(self, parent, axis: str, value: float, limits: Tuple[int, int]): - super().__init__(parent, data=value) + def __init__(self, axis: str, value: float, limits: tuple[float, float]): + """ - self._axis = axis - self._limits = limits + Parameters + ---------- + axis: "x" | "y" + axis the selector is restricted to - def _set(self, value: float): - if not (self._limits[0] <= value <= self._limits[1]): - return + value: float + position of the slider in world space, NOT data space + limits: (float, float) + min, max limits of the selector - if self._axis == "x": - self._parent.position_x = value - else: - self._parent.position_y = value + """ - self._data = value - self._feature_changed(key=None, new_data=value) + super().__init__() - def _feature_changed(self, key: Union[int, slice, Tuple[slice]], new_data: Any): - if len(self._event_handlers) < 1: - return + self._axis = axis + self._limits = limits + self._value = value + + @property + def value(self) -> float: + """ + selection, data x or y value + """ + return self._value - if self._parent.parent is not None: - g_ix = self._parent.get_selected_index() - else: - g_ix = None + def set_value(self, selector, value: float): + # clip value between limits + value = np.clip(value, self._limits[0], self._limits[1]) - # get pygfx event and reset it - pygfx_ev = self._parent._pygfx_event - self._parent._pygfx_event = None + # set position + if self._axis == "x": + dim = 0 + elif self._axis == "y": + dim = 1 - pick_info = { - "world_object": self._parent.world_object, - "new_data": new_data, - "selected_index": g_ix, - "graphic": self._parent, - "pygfx_event": pygfx_ev, - "delta": self._parent.delta, - } + for edge in selector._edges: + edge.geometry.positions.data[:, dim] = value + edge.geometry.positions.update_range() - event_data = FeatureEvent(type="selection", pick_info=pick_info) + self._value = value - self._call_event_handlers(event_data) + event = FeatureEvent("selection", {"value": value}) + event.get_selected_index = selector.get_selected_index - def __repr__(self) -> str: - s = f"LinearSelectionFeature for {self._parent}" - return s + self._call_event_handlers(event) class LinearRegionSelectionFeature(GraphicFeature): """ - Feature for a linearly bounding region - - **event pick info** - - ===================== =============================== ======================================================================================= - key type description - ===================== =============================== ======================================================================================= - "selected_indices" ``numpy.ndarray`` or ``None`` selected graphic data indices - "world_object" ``pygfx.WorldObject`` pygfx World Object - "new_data" ``(float, float)`` current bounds in world coordinates, NOT necessarily the same as "selected_indices". - "graphic" ``Graphic`` the selection graphic - "delta" ``numpy.ndarray`` the delta vector of the graphic in NDC - "pygfx_event" ``pygfx.Event`` pygfx Event - "selected_data" ``numpy.ndarray`` or ``None`` selected graphic data - "move_info" ``MoveInfo`` last position and event source (pygfx.Mesh or pygfx.Line) - ===================== =============================== ======================================================================================= + **additional event attributes:** + + +----------------------+----------+------------------------------------+ + | attribute | type | description | + +======================+==========+====================================+ + | get_selected_indices | callable | returns indices under the selector | + +----------------------+----------+------------------------------------+ + | get_selected_data | callable | returns data under the selector | + +----------------------+----------+------------------------------------+ + + **info dict:** + + +----------+------------+-----------------------------+ + | dict key | value type | value description | + +==========+============+=============================+ + | value | np.ndarray | new [min, max] of selection | + +----------+------------+-----------------------------+ """ - def __init__( - self, parent, selection: Tuple[int, int], axis: str, limits: Tuple[int, int] - ): - super().__init__(parent, data=selection) + def __init__(self, value: tuple[int, int], axis: str, limits: tuple[float, float]): + super().__init__() self._axis = axis self._limits = limits + self._value = tuple(int(v) for v in value) - self._set(selection) + @property + def value(self) -> np.ndarray[float]: + """ + (min, max) of the selection, in data space + """ + return self._value @property def axis(self) -> str: """one of "x" | "y" """ return self._axis - def _set(self, value: Tuple[float, float]): - # sets new bounds - if not isinstance(value, tuple): + def set_value(self, selector, value: Sequence[float]): + """ + Set start, stop range of selector + + Parameters + ---------- + selector: LinearRegionSelector + + value: (float, float) + (min, max) values in data space + + """ + if not len(value) == 2: raise TypeError( - "Bounds must be a tuple in the form of `(min_bound, max_bound)`, " - "where `min_bound` and `max_bound` are numeric values." + "selection must be a array, tuple, list, or sequence in the form of `(min, max)`, " + "where `min` and `max` are numeric values." ) - # make sure bounds not exceeded - for v in value: - if not (self._limits[0] <= v <= self._limits[1]): - return + # convert to array, clip values if they are beyond the limits + value = np.asarray(value, dtype=np.float32).clip(*self._limits) # make sure `selector width >= 2`, left edge must not move past right edge! # or bottom edge must not move past top edge! - # has to be at least 2 otherwise can't join datapoints for lines - if not (value[1] - value[0]) >= 2: + if not (value[1] - value[0]) >= 0: return if self.axis == "x": # change left x position of the fill mesh - self._parent.fill.geometry.positions.data[mesh_masks.x_left] = value[0] + selector.fill.geometry.positions.data[mesh_masks.x_left] = value[0] # change right x position of the fill mesh - self._parent.fill.geometry.positions.data[mesh_masks.x_right] = value[1] + selector.fill.geometry.positions.data[mesh_masks.x_right] = value[1] # change x position of the left edge line - self._parent.edges[0].geometry.positions.data[:, 0] = value[0] + selector.edges[0].geometry.positions.data[:, 0] = value[0] # change x position of the right edge line - self._parent.edges[1].geometry.positions.data[:, 0] = value[1] + selector.edges[1].geometry.positions.data[:, 0] = value[1] elif self.axis == "y": # change bottom y position of the fill mesh - self._parent.fill.geometry.positions.data[mesh_masks.y_bottom] = value[0] + selector.fill.geometry.positions.data[mesh_masks.y_bottom] = value[0] # change top position of the fill mesh - self._parent.fill.geometry.positions.data[mesh_masks.y_top] = value[1] + selector.fill.geometry.positions.data[mesh_masks.y_top] = value[1] # change y position of the bottom edge line - self._parent.edges[0].geometry.positions.data[:, 1] = value[0] + selector.edges[0].geometry.positions.data[:, 1] = value[0] # change y position of the top edge line - self._parent.edges[1].geometry.positions.data[:, 1] = value[1] + selector.edges[1].geometry.positions.data[:, 1] = value[1] - self._data = value # (value[0], value[1]) + self._value = value # send changes to GPU - self._parent.fill.geometry.positions.update_range() - - self._parent.edges[0].geometry.positions.update_range() - self._parent.edges[1].geometry.positions.update_range() + selector.fill.geometry.positions.update_range() - # calls any events - self._feature_changed(key=None, new_data=value) + selector.edges[0].geometry.positions.update_range() + selector.edges[1].geometry.positions.update_range() - def _feature_changed(self, key: Union[int, slice, Tuple[slice]], new_data: Any): + # send event if len(self._event_handlers) < 1: return - if self._parent.parent is not None: - selected_ixs = self._parent.get_selected_indices() - selected_data = self._parent.get_selected_data() - else: - selected_ixs = None - selected_data = None - - # get pygfx event and reset it - pygfx_ev = self._parent._pygfx_event - self._parent._pygfx_event = None - - pick_info = { - "world_object": self._parent.world_object, - "new_data": new_data, - "selected_indices": selected_ixs, - "selected_data": selected_data, - "graphic": self._parent, - "delta": self._parent.delta, - "pygfx_event": pygfx_ev, - "move_info": self._parent._move_info, - } - - event_data = FeatureEvent(type="selection", pick_info=pick_info) - - self._call_event_handlers(event_data) - - def __repr__(self) -> str: - s = f"LinearRegionSelectionFeature for {self._parent}" - return s + event = FeatureEvent("selection", {"value": self.value}) + + event.get_selected_indices = selector.get_selected_indices + event.get_selected_data = selector.get_selected_data + + self._call_event_handlers(event) + # TODO: user's selector event handlers can call event.graphic.get_selected_indices() to get the data index, + # and event.graphic.get_selected_data() to get the data under the selection + # this is probably a good idea so that the data isn't sliced until it's actually necessary diff --git a/fastplotlib/graphics/_features/_sizes.py b/fastplotlib/graphics/_features/_sizes.py deleted file mode 100644 index 2ceeb7862..000000000 --- a/fastplotlib/graphics/_features/_sizes.py +++ /dev/null @@ -1,120 +0,0 @@ -from typing import Any - -import numpy as np - -import pygfx - -from ._base import ( - GraphicFeatureIndexable, - cleanup_slice, - FeatureEvent, - to_gpu_supported_dtype, - cleanup_array_slice, -) - - -class PointsSizesFeature(GraphicFeatureIndexable): - """ - Access to the vertex buffer data shown in the graphic. - Supports fancy indexing if the data array also supports it. - """ - - def __init__(self, parent, sizes: Any, collection_index: int = None): - sizes = self._fix_sizes(sizes, parent) - super().__init__(parent, sizes, collection_index=collection_index) - - @property - def buffer(self) -> pygfx.Buffer: - return self._parent.world_object.geometry.sizes - - def __getitem__(self, item): - return self.buffer.data[item] - - def _fix_sizes(self, sizes, parent): - graphic_type = parent.__class__.__name__ - - n_datapoints = parent.data().shape[0] - if not isinstance(sizes, (list, tuple, np.ndarray)): - sizes = np.full( - n_datapoints, sizes, dtype=np.float32 - ) # force it into a float to avoid weird gpu errors - elif not isinstance( - sizes, np.ndarray - ): # if it's not a ndarray already, make it one - sizes = np.array(sizes, dtype=np.float32) # read it in as a numpy.float32 - if (sizes.ndim != 1) or (sizes.size != parent.data().shape[0]): - raise ValueError( - f"sequence of `sizes` must be 1 dimensional with " - f"the same length as the number of datapoints" - ) - - sizes = to_gpu_supported_dtype(sizes) - - if any(s < 0 for s in sizes): - raise ValueError( - "All sizes must be positive numbers greater than or equal to 0.0." - ) - - if sizes.ndim == 1: - if graphic_type == "ScatterGraphic": - sizes = np.array(sizes) - else: - raise ValueError( - f"Sizes must be an array of shape (n,) where n == the number of data points provided.\ - Received shape={sizes.shape}." - ) - - return np.array(sizes) - - def __setitem__(self, key, value): - if isinstance(key, np.ndarray): - # make sure 1D array of int or boolean - key = cleanup_array_slice(key, self._upper_bound) - - # put sizes into right shape if they're only indexing datapoints - if isinstance(key, (slice, int, np.ndarray, np.integer)): - value = self._fix_sizes(value, self._parent) - # otherwise assume that they have the right shape - # numpy will throw errors if it can't broadcast - - if value.size != self.buffer.data[key].size: - raise ValueError( - f"{value.size} is not equal to buffer size {self.buffer.data[key].size}.\ - If you want to set size to a non-scalar value, make sure it's the right length!" - ) - - self.buffer.data[key] = value - self._update_range(key) - # avoid creating dicts constantly if there are no events to handle - if len(self._event_handlers) > 0: - self._feature_changed(key, value) - - def _update_range(self, key): - self._update_range_indices(key) - - def _feature_changed(self, key, new_data): - if key is not None: - key = cleanup_slice(key, self._upper_bound) - if isinstance(key, (int, np.integer)): - indices = [key] - elif isinstance(key, slice): - indices = range(key.start, key.stop, key.step) - elif isinstance(key, np.ndarray): - indices = key - elif key is None: - indices = None - - pick_info = { - "index": indices, - "collection-index": self._collection_index, - "world_object": self._parent.world_object, - "new_data": new_data, - } - - event_data = FeatureEvent(type="sizes", pick_info=pick_info) - - self._call_event_handlers(event_data) - - def __repr__(self) -> str: - s = f"PointsSizesFeature for {self._parent}, call `.sizes()` to get values" - return s diff --git a/fastplotlib/graphics/_features/_text.py b/fastplotlib/graphics/_features/_text.py new file mode 100644 index 000000000..baa2734d5 --- /dev/null +++ b/fastplotlib/graphics/_features/_text.py @@ -0,0 +1,92 @@ +import numpy as np + +import pygfx + +from ._base import GraphicFeature, FeatureEvent + + +class TextData(GraphicFeature): + def __init__(self, value: str): + self._value = value + super().__init__() + + @property + def value(self) -> str: + return self._value + + def set_value(self, graphic, value: str): + graphic.world_object.geometry.set_text(value) + self._value = value + + event = FeatureEvent(type="text", info={"value": value}) + self._call_event_handlers(event) + + +class FontSize(GraphicFeature): + def __init__(self, value: float | int): + self._value = value + super().__init__() + + @property + def value(self) -> float | int: + return self._value + + def set_value(self, graphic, value: float | int): + graphic.world_object.geometry.font_size = value + self._value = graphic.world_object.geometry.font_size + + event = FeatureEvent(type="font_size", info={"value": value}) + self._call_event_handlers(event) + + +class TextFaceColor(GraphicFeature): + def __init__(self, value: str | np.ndarray | list[float] | tuple[float]): + self._value = pygfx.Color(value) + super().__init__() + + @property + def value(self) -> pygfx.Color: + return self._value + + def set_value(self, graphic, value: str | np.ndarray | list[float] | tuple[float]): + value = pygfx.Color(value) + graphic.world_object.material.color = value + self._value = graphic.world_object.material.color + + event = FeatureEvent(type="face_color", info={"value": value}) + self._call_event_handlers(event) + + +class TextOutlineColor(GraphicFeature): + def __init__(self, value: str | np.ndarray | list[float] | tuple[float]): + self._value = pygfx.Color(value) + super().__init__() + + @property + def value(self) -> pygfx.Color: + return self._value + + def set_value(self, graphic, value: str | np.ndarray | list[float] | tuple[float]): + value = pygfx.Color(value) + graphic.world_object.material.outline_color = value + self._value = graphic.world_object.material.outline_color + + event = FeatureEvent(type="outline_color", info={"value": value}) + self._call_event_handlers(event) + + +class TextOutlineThickness(GraphicFeature): + def __init__(self, value: float): + self._value = value + super().__init__() + + @property + def value(self) -> float: + return self._value + + def set_value(self, graphic, value: float): + graphic.world_object.material.outline_thickness = value + self._value = graphic.world_object.material.outline_thickness + + event = FeatureEvent(type="outline_thickness", info={"value": value}) + self._call_event_handlers(event) diff --git a/fastplotlib/graphics/_features/_thickness.py b/fastplotlib/graphics/_features/_thickness.py deleted file mode 100644 index fc90ef96f..000000000 --- a/fastplotlib/graphics/_features/_thickness.py +++ /dev/null @@ -1,46 +0,0 @@ -from ._base import GraphicFeature, FeatureEvent - - -class ThicknessFeature(GraphicFeature): - """ - Used by Line graphics for line material thickness. - - **event pick info:** - - ==================== ======================== ========================================================================= - key type description - ==================== ======================== ========================================================================= - "index" ``None`` not used - "new_data" ``float`` new thickness value - "collection-index" int the index of the graphic within the collection that triggered the event - "world_object" pygfx.WorldObject world object - ==================== ======================== ========================================================================= - """ - - def __init__(self, parent, thickness: float): - self._scene = None - super().__init__(parent, thickness) - - def _set(self, value: float): - value = self._parse_set_value(value) - - self._parent.world_object.material.thickness = value - self._feature_changed(key=None, new_data=value) - - def _feature_changed(self, key, new_data): - # this is a non-indexable feature so key=None - - pick_info = { - "index": None, - "collection-index": self._collection_index, - "world_object": self._parent.world_object, - "new_data": new_data, - } - - event_data = FeatureEvent(type="thickness", pick_info=pick_info) - - self._call_event_handlers(event_data) - - def __repr__(self) -> str: - s = f"ThicknessFeature for {self._parent}, call `.thickness()` to get value" - return s diff --git a/fastplotlib/graphics/_features/utils.py b/fastplotlib/graphics/_features/utils.py new file mode 100644 index 000000000..e2f6e3428 --- /dev/null +++ b/fastplotlib/graphics/_features/utils.py @@ -0,0 +1,87 @@ +import pygfx +import numpy as np + +from ._base import to_gpu_supported_dtype +from ...utils import make_pygfx_colors + + +def parse_colors( + colors: str | np.ndarray | list[str] | tuple[str], + n_colors: int | None, + alpha: float | None = None, +): + """ + + Parameters + ---------- + colors + n_colors + alpha + key + + Returns + ------- + + """ + + # if provided as a numpy array of str + if isinstance(colors, np.ndarray): + if colors.dtype.kind in ["U", "S"]: + colors = colors.tolist() + # if the color is provided as a numpy array + if isinstance(colors, np.ndarray): + if colors.shape == (4,): # single RGBA array + data = np.repeat(np.array([colors]), n_colors, axis=0) + # else assume it's already a stack of RGBA arrays, keep this directly as the data + elif colors.ndim == 2: + if colors.shape[1] != 4 and colors.shape[0] != n_colors: + raise ValueError( + "Valid array color arguments must be a single RGBA array or a stack of " + "RGBA arrays for each datapoint in the shape [n_datapoints, 4]" + ) + data = colors + else: + raise ValueError( + "Valid array color arguments must be a single RGBA array or a stack of " + "RGBA arrays for each datapoint in the shape [n_datapoints, 4]" + ) + + # if the color is provided as list or tuple + elif isinstance(colors, (list, tuple)): + # if iterable of str + if all([isinstance(val, str) for val in colors]): + if not len(colors) == n_colors: + raise ValueError( + f"Valid iterable color arguments must be a `tuple` or `list` of `str` " + f"where the length of the iterable is the same as the number of datapoints." + ) + + data = np.vstack([np.array(pygfx.Color(c)) for c in colors]) + + # if it's a single RGBA array as a tuple/list + elif len(colors) == 4: + c = pygfx.Color(colors) + data = np.repeat(np.array([c]), n_colors, axis=0) + + else: + raise ValueError( + f"Valid iterable color arguments must be a `tuple` or `list` representing RGBA values or " + f"an iterable of `str` with the same length as the number of datapoints." + ) + elif isinstance(colors, str): + if colors == "random": + data = np.random.rand(n_colors, 4) + data[:, -1] = alpha + else: + data = make_pygfx_colors(colors, n_colors) + else: + # assume it's a single color, use pygfx.Color to parse it + data = make_pygfx_colors(colors, n_colors) + + if alpha is not None: + if isinstance(alpha, float): + data[:, -1] = alpha + else: + raise TypeError("if alpha is provided it must be of type `float`") + + return to_gpu_supported_dtype(data) diff --git a/fastplotlib/graphics/_positions_base.py b/fastplotlib/graphics/_positions_base.py new file mode 100644 index 000000000..3727087cc --- /dev/null +++ b/fastplotlib/graphics/_positions_base.py @@ -0,0 +1,185 @@ +from typing import Any + +import numpy as np + +import pygfx +from ._base import Graphic +from ._features import ( + VertexPositions, + VertexColors, + UniformColor, + VertexCmap, + PointsSizesFeature, +) + + +class PositionsGraphic(Graphic): + """Base class for LineGraphic and ScatterGraphic""" + + @property + def data(self) -> VertexPositions: + """Get or set the vertex positions data""" + return self._data + + @data.setter + def data(self, value): + self._data[:] = value + + @property + def colors(self) -> VertexColors | pygfx.Color: + """Get or set the colors data""" + if isinstance(self._colors, VertexColors): + return self._colors + + elif isinstance(self._colors, UniformColor): + return self._colors.value + + @colors.setter + def colors(self, value: str | np.ndarray | tuple[float] | list[float] | list[str]): + if isinstance(self._colors, VertexColors): + self._colors[:] = value + + elif isinstance(self._colors, UniformColor): + self._colors.set_value(self, value) + + @property + def cmap(self) -> VertexCmap: + """Control the cmap, cmap transform, or cmap alpha""" + return self._cmap + + @cmap.setter + def cmap(self, name: str): + if self._cmap is None: + raise BufferError("Cannot use cmap with uniform_colors=True") + + self._cmap[:] = name + + def __init__( + self, + data: Any, + colors: str | np.ndarray | tuple[float] | list[float] | list[str] = "w", + uniform_color: bool = False, + alpha: float = 1.0, + cmap: str | VertexCmap = None, + cmap_transform: np.ndarray = None, + isolated_buffer: bool = True, + *args, + **kwargs, + ): + if isinstance(data, VertexPositions): + self._data = data + else: + self._data = VertexPositions(data, isolated_buffer=isolated_buffer) + + if cmap_transform is not None and cmap is None: + raise ValueError("must pass `cmap` if passing `cmap_transform`") + + if cmap is not None: + # if a cmap is specified it overrides colors argument + if uniform_color: + raise TypeError("Cannot use cmap if uniform_color=True") + + if isinstance(cmap, str): + # make colors from cmap + if isinstance(colors, VertexColors): + # share buffer with existing colors instance for the cmap + self._colors = colors + self._colors._shared += 1 + else: + # create vertex colors buffer + self._colors = VertexColors("w", n_colors=self._data.value.shape[0]) + # make cmap using vertex colors buffer + self._cmap = VertexCmap( + self._colors, + cmap_name=cmap, + transform=cmap_transform, + alpha=alpha, + ) + elif isinstance(cmap, VertexCmap): + # use existing cmap instance + self._cmap = cmap + self._colors = cmap._vertex_colors + else: + raise TypeError( + "`cmap` argument must be a cmap name or an existing `VertexCmap` instance" + ) + else: + # no cmap given + if isinstance(colors, VertexColors): + # share buffer with existing colors instance + self._colors = colors + self._colors._shared += 1 + # blank colormap instance + self._cmap = VertexCmap( + self._colors, cmap_name=None, transform=None, alpha=alpha + ) + else: + if uniform_color: + if not isinstance(colors, str): # not a single color + if not len(colors) in [3, 4]: # not an RGB(A) array + raise TypeError( + "must pass a single color if using `uniform_colors=True`" + ) + self._colors = UniformColor(colors, alpha=alpha) + self._cmap = None + else: + self._colors = VertexColors( + colors, + n_colors=self._data.value.shape[0], + alpha=alpha, + ) + self._cmap = VertexCmap( + self._colors, cmap_name=None, transform=None, alpha=alpha + ) + + super().__init__(*args, **kwargs) + + def unshare_property(self, property: str): + """unshare a shared property. Experimental and untested!""" + if not isinstance(property, str): + raise TypeError + + f = getattr(self, property) + if f.shared == 0: + raise BufferError("Cannot detach an independent buffer") + + if property == "colors" and isinstance(property, VertexColors): + self._colors._buffer = pygfx.Buffer(self._colors.value.copy()) + self.world_object.geometry.colors = self._colors.buffer + self._colors._shared -= 1 + + elif property == "data": + self._data._buffer = pygfx.Buffer(self._data.value.copy()) + self.world_object.geometry.positions = self._data.buffer + self._data._shared -= 1 + + elif property == "sizes": + self._sizes._buffer = pygfx.Buffer(self._sizes.value.copy()) + self.world_object.geometry.positions = self._sizes.buffer + self._sizes._shared -= 1 + + def share_property( + self, property: VertexPositions | VertexColors | PointsSizesFeature + ): + """share a property from another graphic. Experimental and untested!""" + if isinstance(property, VertexPositions): + # TODO: check if this causes a memory leak + self._data._shared -= 1 + + self._data = property + self._data._shared += 1 + self.world_object.geometry.positions = self._data.buffer + + elif isinstance(property, VertexColors): + self._colors._shared -= 1 + + self._colors = property + self._colors._shared += 1 + self.world_object.geometry.colors = self._colors.buffer + + elif isinstance(property, PointsSizesFeature): + self._sizes._shared -= 1 + + self._sizes = property + self._sizes._shared += 1 + self.world_object.geometry.sizes = self._sizes.buffer diff --git a/fastplotlib/graphics/histogram.py b/fastplotlib/graphics/histogram.py deleted file mode 100644 index b78be39d3..000000000 --- a/fastplotlib/graphics/histogram.py +++ /dev/null @@ -1,114 +0,0 @@ -from warnings import warn -from typing import Union, Dict - -import numpy as np - -import pygfx - -from ._base import Graphic - - -class _HistogramBin(pygfx.Mesh): - def __int__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.bin_center: float = None - self.frequency: Union[int, float] = None - - -class HistogramGraphic(Graphic): - def __init__( - self, - data: np.ndarray = None, - bins: Union[int, str] = "auto", - pre_computed: Dict[str, np.ndarray] = None, - colors: np.ndarray = "w", - draw_scale_factor: float = 100.0, - draw_bin_width_scale: float = 1.0, - **kwargs, - ): - """ - Create a Histogram Graphic - - Parameters - ---------- - data: np.ndarray or None, optional - data to create a histogram from, can be ``None`` if pre-computed values are provided to ``pre_computed`` - - bins: int or str, default is "auto", optional - this is directly just passed to ``numpy.histogram`` - - pre_computed: dict in the form {"hist": vals, "bin_edges" : vals}, optional - pre-computed histogram values - - colors: np.ndarray, optional - - draw_scale_factor: float, default ``100.0``, optional - scale the drawing of the entire Graphic - - draw_bin_width_scale: float, default ``1.0`` - scale the drawing of the bin widths - - kwargs - passed to Graphic - """ - - if pre_computed is None: - self.hist, self.bin_edges = np.histogram(data, bins) - else: - if not set(pre_computed.keys()) == {"hist", "bin_edges"}: - raise ValueError( - "argument to `pre_computed` must be a `dict` with keys 'hist' and 'bin_edges'" - ) - if not all(isinstance(v, np.ndarray) for v in pre_computed.values()): - raise ValueError( - "argument to `pre_computed` must be a `dict` where the values are numpy.ndarray" - ) - self.hist, self.bin_edges = pre_computed["hist"], pre_computed["bin_edges"] - - self.bin_interval = (self.bin_edges[1] - self.bin_edges[0]) / 2 - self.bin_centers = (self.bin_edges + self.bin_interval)[:-1] - - # scale between 0 - draw_scale_factor - scaled_bin_edges = ( - (self.bin_edges - self.bin_edges.min()) / (np.ptp(self.bin_edges)) - ) * draw_scale_factor - - bin_interval_scaled = scaled_bin_edges[1] / 2 - # get the centers of the bins from the edges - x_positions_bins = (scaled_bin_edges + bin_interval_scaled)[:-1].astype( - np.float32 - ) - - n_bins = x_positions_bins.shape[0] - bin_width = (draw_scale_factor / n_bins) * draw_bin_width_scale - - self.hist = self.hist.astype(np.float32) - - for bad_val in [np.nan, np.inf, -np.inf]: - if bad_val in self.hist: - warn( - f"Problematic value <{bad_val}> found in histogram, replacing with zero" - ) - self.hist[self.hist == bad_val] = 0 - - data = np.vstack([x_positions_bins, self.hist]) - - super().__init__(data=data, colors=colors, n_colors=n_bins, **kwargs) - - self._world_object: pygfx.Group = pygfx.Group() - - for x_val, y_val, bin_center in zip( - x_positions_bins, self.hist, self.bin_centers - ): - geometry = pygfx.plane_geometry( - width=bin_width, - height=y_val, - ) - - material = pygfx.MeshBasicMaterial() - hist_bin_graphic = _HistogramBin(geometry, material) - hist_bin_graphic.position.set(x_val, (y_val) / 2, 0) - hist_bin_graphic.bin_center = bin_center - hist_bin_graphic.frequency = y_val - - self.world_object.add(hist_bin_graphic) diff --git a/fastplotlib/graphics/image.py b/fastplotlib/graphics/image.py index ce736dab2..d6576c12d 100644 --- a/fastplotlib/graphics/image.py +++ b/fastplotlib/graphics/image.py @@ -1,202 +1,77 @@ from typing import * -from math import ceil -from itertools import product import weakref -import numpy as np - import pygfx from ..utils import quick_min_max -from ._base import Graphic, Interaction +from ._base import Graphic from .selectors import LinearSelector, LinearRegionSelector from ._features import ( - ImageCmapFeature, - ImageDataFeature, - HeatmapDataFeature, - HeatmapCmapFeature, - to_gpu_supported_dtype, + TextureArray, + ImageCmap, + ImageVmin, + ImageVmax, + ImageInterpolation, + ImageCmapInterpolation, ) -class _AddSelectorsMixin: - def add_linear_selector( - self, selection: int = None, padding: float = None, **kwargs - ) -> LinearSelector: - """ - Adds a :class:`.LinearSelector`. - - Parameters - ---------- - selection: int, optional - initial position of the selector - - padding: float, optional - pad the length of the selector - - kwargs: - passed to :class:`.LinearSelector` - - Returns - ------- - LinearSelector - - """ - - # default padding is 15% the height or width of the image - if "axis" in kwargs.keys(): - axis = kwargs["axis"] - else: - axis = "x" - - ( - bounds_init, - limits, - size, - origin, - axis, - end_points, - ) = self._get_linear_selector_init_args(padding, **kwargs) - - if selection is None: - selection = limits[0] - - if selection < limits[0] or selection > limits[1]: - raise ValueError( - f"the passed selection: {selection} is beyond the limits: {limits}" - ) - - selector = LinearSelector( - selection=selection, - limits=limits, - end_points=end_points, - parent=weakref.proxy(self), - **kwargs, - ) - - self._plot_area.add_graphic(selector, center=False) - selector.position_z = self.position_z + 1 - - return weakref.proxy(selector) - - def add_linear_region_selector( - self, padding: float = None, **kwargs - ) -> LinearRegionSelector: - """ - Add a :class:`.LinearRegionSelector`. - - Parameters - ---------- - padding: float, optional - Extends the linear selector along the y-axis to make it easier to interact with. - - kwargs: optional - passed to ``LinearRegionSelector`` +class _ImageTile(pygfx.Image): + """ + Similar to pygfx.Image, only difference is that it modifies the pick_info + by adding the data row start indices that correspond to this chunk of the big image + """ - Returns - ------- - LinearRegionSelector - linear selection graphic + def __init__( + self, + geometry, + material, + data_slice: tuple[slice, slice], + chunk_index: tuple[int, int], + **kwargs, + ): + super().__init__(geometry, material, **kwargs) - """ + self._data_slice = data_slice + self._chunk_index = chunk_index - ( - bounds_init, - limits, - size, - origin, - axis, - end_points, - ) = self._get_linear_selector_init_args(padding, **kwargs) + def _wgpu_get_pick_info(self, pick_value): + pick_info = super()._wgpu_get_pick_info(pick_value) - # create selector - selector = LinearRegionSelector( - bounds=bounds_init, - limits=limits, - size=size, - origin=origin, - parent=weakref.proxy(self), - fill_color=(0, 0, 0.35, 0.2), - **kwargs, + data_row_start, data_col_start = ( + self.data_slice[0].start, + self.data_slice[1].start, ) - self._plot_area.add_graphic(selector, center=False) - # so that it is above this graphic - selector.position_z = self.position_z + 3 - - # PlotArea manages this for garbage collection etc. just like all other Graphics - # so we should only work with a proxy on the user-end - return weakref.proxy(selector) - - # TODO: this method is a bit of a mess, can refactor later - def _get_linear_selector_init_args(self, padding: float, **kwargs): - # computes initial bounds, limits, size and origin of linear selectors - data = self.data() - - if "axis" in kwargs.keys(): - axis = kwargs["axis"] - else: - axis = "x" - - if padding is None: - if axis == "x": - # based on number of rows - padding = int(data.shape[0] * 0.15) - elif axis == "y": - # based on number of columns - padding = int(data.shape[1] * 0.15) - - if axis == "x": - offset = self.position_x - # x limits, number of columns - limits = (offset, data.shape[1] - 1) - - # size is number of rows + padding - # used by LinearRegionSelector but not LinearSelector - size = data.shape[0] + padding - - # initial position of the selector - # center row - position_y = data.shape[0] / 2 - - # need y offset too for this - origin = (limits[0] - offset, position_y + self.position_y) - - # endpoints of the data range - # used by linear selector but not linear region - # padding, n_rows + padding - end_points = (0 - padding, data.shape[0] + padding) - else: - offset = self.position_y - # y limits - limits = (offset, data.shape[0] - 1) - - # width + padding - # used by LinearRegionSelector but not LinearSelector - size = data.shape[1] + padding - - # initial position of the selector - position_x = data.shape[1] / 2 + # add the actual data row and col start indices + x, y = pick_info["index"] + x += data_col_start + y += data_row_start + pick_info["index"] = (x, y) - # need x offset too for this - origin = (position_x + self.position_x, limits[0] - offset) + xp, yp = pick_info["pixel_coord"] + xp += data_col_start + yp += data_row_start + pick_info["pixel_coord"] = (xp, yp) - # endpoints of the data range - # used by linear selector but not linear region - end_points = (0 - padding, data.shape[1] + padding) - - # initial bounds are 20% of the limits range - # used by LinearRegionSelector but not LinearSelector - bounds_init = (limits[0], int(np.ptp(limits) * 0.2) + offset) + # add row chunk and col chunk index to pick_info dict + return { + **pick_info, + "data_slice": self.data_slice, + "chunk_index": self.chunk_index, + } - return bounds_init, limits, size, origin, axis, end_points + @property + def data_slice(self) -> tuple[slice, slice]: + return self._data_slice - def _add_plot_area_hook(self, plot_area): - self._plot_area = plot_area + @property + def chunk_index(self) -> tuple[int, int]: + return self._chunk_index -class ImageGraphic(Graphic, Interaction, _AddSelectorsMixin): - feature_events = {"data", "cmap", "present"} +class ImageGraphic(Graphic): + _features = {"data", "cmap", "vmin", "vmax", "interpolation", "cmap_interpolation"} def __init__( self, @@ -204,9 +79,9 @@ def __init__( vmin: int = None, vmax: int = None, cmap: str = "plasma", - filter: str = "nearest", + interpolation: str = "nearest", + cmap_interpolation: str = "linear", isolated_buffer: bool = True, - *args, **kwargs, ): """ @@ -216,8 +91,7 @@ def __init__( ---------- data: array-like array-like, usually numpy.ndarray, must support ``memoryview()`` - Tensorflow Tensors also work **probably**, but not thoroughly tested - | shape must be ``[x_dim, y_dim]`` or ``[x_dim, y_dim, rgb]`` + | shape must be ``[x_dim, y_dim]`` vmin: int, optional minimum value for color scaling, calculated from data if not provided @@ -226,265 +100,289 @@ def __init__( maximum value for color scaling, calculated from data if not provided cmap: str, optional, default "plasma" - colormap to use to display the image data, ignored if data is RGB + colormap to use to display the data - filter: str, optional, default "nearest" + interpolation: str, optional, default "nearest" interpolation filter, one of "nearest" or "linear" + cmap_interpolation: str, optional, default "linear" + colormap interpolation method, one of "nearest" or "linear" + isolated_buffer: bool, default True If True, initialize a buffer with the same shape as the input data and then set the data, useful if the data arrays are ready-only such as memmaps. If False, the input array is itself used as the buffer. - args: - additional arguments passed to Graphic - kwargs: additional keyword arguments passed to Graphic - Features - -------- - - **data**: :class:`.ImageDataFeature` - Manages the data buffer displayed in the ImageGraphic - - **cmap**: :class:`.ImageCmapFeature` - Manages the colormap - - **present**: :class:`.PresentFeature` - Control the presence of the Graphic in the scene - """ - super().__init__(*args, **kwargs) + super().__init__(**kwargs) - data = to_gpu_supported_dtype(data) + world_object = pygfx.Group() - # TODO: we need to organize and do this better - if isolated_buffer: - # initialize a buffer with the same shape as the input data - # we do not directly use the input data array as the buffer - # because if the input array is a read-only type, such as - # numpy memmaps, we would not be able to change the image data - buffer_init = np.zeros(shape=data.shape, dtype=data.dtype) - else: - buffer_init = data + # texture array that manages the textures on the GPU for displaying this image + self._data = TextureArray(data, isolated_buffer=isolated_buffer) if (vmin is None) or (vmax is None): vmin, vmax = quick_min_max(data) - texture = pygfx.Texture(buffer_init, dim=2) + # other graphic features + self._vmin = ImageVmin(vmin) + self._vmax = ImageVmax(vmax) - geometry = pygfx.Geometry(grid=texture) + self._cmap = ImageCmap(cmap) - self.cmap = ImageCmapFeature(self, cmap) + self._interpolation = ImageInterpolation(interpolation) + self._cmap_interpolation = ImageCmapInterpolation(cmap_interpolation) - # if data is RGB or RGBA - if data.ndim > 2: - material = pygfx.ImageBasicMaterial( - clim=(vmin, vmax), map_interpolation=filter, pick_write=True - ) - # if data is just 2D without color information, use colormap LUT + # use cmap if not RGB + if self._data.value.ndim == 2: + _map = self._cmap.texture else: - material = pygfx.ImageBasicMaterial( - clim=(vmin, vmax), - map=self.cmap(), - map_interpolation=filter, - pick_write=True, - ) + _map = None - world_object = pygfx.Image(geometry, material) + # one common material is used for every Texture chunk + self._material = pygfx.ImageBasicMaterial( + clim=(vmin, vmax), + map=_map, + interpolation=self._interpolation.value, + map_interpolation=self._cmap_interpolation.value, + pick_write=True, + ) - self._set_world_object(world_object) + # iterate through each texture chunk and create + # an _ImageTIle, offset the tile using the data indices + for texture, chunk_index, data_slice in self._data: - self.cmap.vmin = vmin - self.cmap.vmax = vmax + # create an ImageTile using the texture for this chunk + img = _ImageTile( + geometry=pygfx.Geometry(grid=texture), + material=self._material, + data_slice=data_slice, # used to parse pick_info + chunk_index=chunk_index, + ) - self.data = ImageDataFeature(self, data) - # TODO: we need to organize and do this better - if isolated_buffer: - # if the buffer was initialized with zeros - # set it with the actual data - self.data = data + # row and column start index for this chunk + data_row_start = data_slice[0].start + data_col_start = data_slice[1].start - def set_feature(self, feature: str, new_data: Any, indices: Any): - pass + # offset tile position using the indices from the big data array + # that correspond to this chunk + img.world.x = data_col_start + img.world.y = data_row_start - def reset_feature(self, feature: str): - pass + world_object.add(img) + self._set_world_object(world_object) -class _ImageTile(pygfx.Image): - """ - Similar to pygfx.Image, only difference is that it contains a few properties to keep track of - row chunk index, column chunk index - """ + @property + def data(self) -> TextureArray: + """Get or set the image data""" + return self._data - def _wgpu_get_pick_info(self, pick_value): - pick_info = super()._wgpu_get_pick_info(pick_value) + @data.setter + def data(self, data): + self._data[:] = data - # add row chunk and col chunk index to pick_info dict - return { - **pick_info, - "row_chunk_index": self.row_chunk_index, - "col_chunk_index": self.col_chunk_index, - } + @property + def cmap(self) -> str: + """colormap name""" + return self._cmap.value + + @cmap.setter + def cmap(self, name: str): + self._cmap.set_value(self, name) @property - def row_chunk_index(self) -> int: - return self._row_chunk_index + def vmin(self) -> float: + """lower contrast limit""" + return self._vmin.value - @row_chunk_index.setter - def row_chunk_index(self, index: int): - self._row_chunk_index = index + @vmin.setter + def vmin(self, value: float): + self._vmin.set_value(self, value) @property - def col_chunk_index(self) -> int: - return self._col_chunk_index + def vmax(self) -> float: + """upper contrast limit""" + return self._vmax.value - @col_chunk_index.setter - def col_chunk_index(self, index: int): - self._col_chunk_index = index + @vmax.setter + def vmax(self, value: float): + self._vmax.set_value(self, value) + @property + def interpolation(self) -> str: + """image data interpolation method""" + return self._interpolation.value -class HeatmapGraphic(Graphic, Interaction, _AddSelectorsMixin): - feature_events = {"data", "cmap", "present"} + @interpolation.setter + def interpolation(self, value: str): + self._interpolation.set_value(self, value) - def __init__( - self, - data: Any, - vmin: int = None, - vmax: int = None, - cmap: str = "plasma", - filter: str = "nearest", - chunk_size: int = 8192, - isolated_buffer: bool = True, - *args, - **kwargs, - ): - """ - Create an Image Graphic + @property + def cmap_interpolation(self) -> str: + """cmap interpolation method""" + return self._cmap_interpolation.value - Parameters - ---------- - data: array-like - array-like, usually numpy.ndarray, must support ``memoryview()`` - Tensorflow Tensors also work **probably**, but not thoroughly tested - | shape must be ``[x_dim, y_dim]`` + @cmap_interpolation.setter + def cmap_interpolation(self, value: str): + self._cmap_interpolation.set_value(self, value) - vmin: int, optional - minimum value for color scaling, calculated from data if not provided + def reset_vmin_vmax(self): + """ + Reset the vmin, vmax by estimating it from the data - vmax: int, optional - maximum value for color scaling, calculated from data if not provided + Returns + ------- + None - cmap: str, optional, default "plasma" - colormap to use to display the data + """ - filter: str, optional, default "nearest" - interpolation filter, one of "nearest" or "linear" + vmin, vmax = quick_min_max(self._data.value) + self.vmin = vmin + self.vmax = vmax - chunk_size: int, default 8192, max 8192 - chunk size for each tile used to make up the heatmap texture + def add_linear_selector( + self, selection: int = None, axis: str = "x", padding: float = None, **kwargs + ) -> LinearSelector: + """ + Adds a :class:`.LinearSelector`. - isolated_buffer: bool, default True - If True, initialize a buffer with the same shape as the input data and then - set the data, useful if the data arrays are ready-only such as memmaps. - If False, the input array is itself used as the buffer. + Parameters + ---------- + selection: int, optional + initial position of the selector - args: - additional arguments passed to Graphic + padding: float, optional + pad the length of the selector kwargs: - additional keyword arguments passed to Graphic + passed to :class:`.LinearSelector` - Features - -------- + Returns + ------- + LinearSelector + + """ - **data**: :class:`.HeatmapDataFeature` - Manages the data buffer displayed in the HeatmapGraphic + if axis == "x": + size = self._data.value.shape[0] + center = size / 2 + limits = (0, self._data.value.shape[1]) + elif axis == "y": + size = self._data.value.shape[1] + center = size / 2 + limits = (0, self._data.value.shape[0]) + else: + raise ValueError("`axis` must be one of 'x' | 'y'") - **cmap**: :class:`.HeatmapCmapFeature` - Manages the colormap + # default padding is 25% the height or width of the image + if padding is None: + size *= 1.25 + else: + size += padding - **present**: :class:`.PresentFeature` - Control the presence of the Graphic in the scene + if selection is None: + selection = limits[0] - """ + if selection < limits[0] or selection > limits[1]: + raise ValueError( + f"the passed selection: {selection} is beyond the limits: {limits}" + ) - super().__init__(*args, **kwargs) + selector = LinearSelector( + selection=selection, + limits=limits, + size=size, + center=center, + axis=axis, + parent=weakref.proxy(self), + **kwargs, + ) - if chunk_size > 8192: - raise ValueError("Maximum chunk size is 8192") + self._plot_area.add_graphic(selector, center=False) - data = to_gpu_supported_dtype(data) + # place selector above this graphic + selector.offset = selector.offset + (0.0, 0.0, self.offset[-1] + 1) - # TODO: we need to organize and do this better - if isolated_buffer: - # initialize a buffer with the same shape as the input data - # we do not directly use the input data array as the buffer - # because if the input array is a read-only type, such as - # numpy memmaps, we would not be able to change the image data - buffer_init = np.zeros(shape=data.shape, dtype=data.dtype) - else: - buffer_init = data + return weakref.proxy(selector) - row_chunks = range(ceil(data.shape[0] / chunk_size)) - col_chunks = range(ceil(data.shape[1] / chunk_size)) + def add_linear_region_selector( + self, + selection: tuple[float, float] = None, + axis: str = "x", + padding: float = 0.0, + fill_color=(0, 0, 0.35, 0.2), + **kwargs, + ) -> LinearRegionSelector: + """ + Add a :class:`.LinearRegionSelector`. Selectors are just ``Graphic`` objects, so you can manage, + remove, or delete them from a plot area just like any other ``Graphic``. - chunks = list(product(row_chunks, col_chunks)) - # chunks is the index position of each chunk + Parameters + ---------- + selection: (float, float) + initial (min, max) of the selection - start_ixs = [list(map(lambda c: c * chunk_size, chunk)) for chunk in chunks] - stop_ixs = [list(map(lambda c: c + chunk_size, chunk)) for chunk in start_ixs] + axis: "x" | "y" + axis the selector can move along - world_object = pygfx.Group() - self._set_world_object(world_object) + padding: float, default 100.0 + Extends the linear selector along the perpendicular axis to make it easier to interact with. - if (vmin is None) or (vmax is None): - vmin, vmax = quick_min_max(data) + kwargs + passed to ``LinearRegionSelector`` - self.cmap = HeatmapCmapFeature(self, cmap) - self._material = pygfx.ImageBasicMaterial( - clim=(vmin, vmax), - map=self.cmap(), - map_interpolation=filter, - pick_write=True, - ) + Returns + ------- + LinearRegionSelector + linear selection graphic - for start, stop, chunk in zip(start_ixs, stop_ixs, chunks): - row_start, col_start = start - row_stop, col_stop = stop + """ - # x and y positions of the Tile in world space coordinates - y_pos, x_pos = row_start, col_start + if axis == "x": + size = self._data.value.shape[0] + center = size / 2 + limits = (0, self._data.value.shape[1]) + elif axis == "y": + size = self._data.value.shape[1] + center = size / 2 + limits = (0, self._data.value.shape[0]) + else: + raise ValueError("`axis` must be one of 'x' | 'y'") - texture = pygfx.Texture( - buffer_init[row_start:row_stop, col_start:col_stop], dim=2 - ) - geometry = pygfx.Geometry(grid=texture) - # material = pygfx.ImageBasicMaterial(clim=(0, 1), map=self.cmap()) + # default padding is 25% the height or width of the image + if padding is None: + size *= 1.25 + else: + size += padding - img = _ImageTile(geometry, self._material) + if selection is None: + selection = limits[0], int(limits[1] * 0.25) - # row and column chunk index for this Tile - img.row_chunk_index = chunk[0] - img.col_chunk_index = chunk[1] + if padding is None: + size *= 1.25 - img.world.x = x_pos - img.world.y = y_pos + else: + size += padding - self.world_object.add(img) + selector = LinearRegionSelector( + selection=selection, + limits=limits, + size=size, + center=center, + axis=axis, + fill_color=fill_color, + parent=weakref.proxy(self), + **kwargs, + ) - self.data = HeatmapDataFeature(self, buffer_init) - # TODO: we need to organize and do this better - if isolated_buffer: - # if the buffer was initialized with zeros - # set it with the actual data - self.data = data + self._plot_area.add_graphic(selector, center=False) - def set_feature(self, feature: str, new_data: Any, indices: Any): - pass + # place above this graphic + selector.offset = selector.offset + (0.0, 0.0, self.offset[-1] + 1) - def reset_feature(self, feature: str): - pass + return weakref.proxy(selector) diff --git a/fastplotlib/graphics/line.py b/fastplotlib/graphics/line.py index 0371fe59b..d0a8cc336 100644 --- a/fastplotlib/graphics/line.py +++ b/fastplotlib/graphics/line.py @@ -5,26 +5,24 @@ import pygfx -from ..utils import parse_cmap_values -from ._base import Graphic, Interaction, PreviouslyModifiedData -from ._features import PointsDataFeature, ColorFeature, CmapFeature, ThicknessFeature +from ._positions_base import PositionsGraphic from .selectors import LinearRegionSelector, LinearSelector +from ._features import Thickness -class LineGraphic(Graphic, Interaction): - feature_events = {"data", "colors", "cmap", "thickness", "present"} +class LineGraphic(PositionsGraphic): + _features = {"data", "colors", "cmap", "thickness"} def __init__( self, data: Any, thickness: float = 2.0, colors: str | np.ndarray | Iterable = "w", + uniform_color: bool = False, alpha: float = 1.0, cmap: str = None, - cmap_values: np.ndarray | Iterable = None, - z_position: float = None, - collection_index: int = None, - *args, + cmap_transform: np.ndarray | Iterable = None, + isolated_buffer: bool = True, **kwargs, ): """ @@ -42,101 +40,90 @@ def __init__( 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, this - overrides any argument passed to "colors" - - cmap_values: 1D array-like or Iterable of numerical values, optional - if provided, these values are used to map the colors from the cmap + uniform_color: bool, default ``False`` + if True, uses a uniform buffer for the line color, + basically saves GPU VRAM when the entire line has a single color alpha: float, optional, default 1.0 alpha value for the colors - z_position: float, optional - z-axis position for placing the graphic + cmap: str, optional + apply a colormap to the line instead of assigning colors manually, this + overrides any argument passed to "colors" - args - passed to Graphic + cmap_transform: 1D array-like of numerical values, optional + if provided, these values are used to map the colors from the cmap - kwargs + **kwargs passed to Graphic - Features - -------- - - **data**: :class:`.ImageDataFeature` - Manages the line [x, y, z] positions data buffer, allows regular and fancy indexing. - - **colors**: :class:`.ColorFeature` - Manages the color buffer, allows regular and fancy indexing. - - **cmap**: :class:`.CmapFeature` - Manages the cmap, wraps :class:`.ColorFeature` to add additional functionality relevant to cmaps. - - **thickness**: :class:`.ThicknessFeature` - Manages the thickness feature of the lines. - - **present**: :class:`.PresentFeature` - Control the presence of the Graphic in the scene, set to ``True`` or ``False`` - """ - self.data = PointsDataFeature(self, data, collection_index=collection_index) - - if cmap is not None: - n_datapoints = self.data().shape[0] - - colors = parse_cmap_values( - n_colors=n_datapoints, cmap_name=cmap, cmap_values=cmap_values - ) - - self.colors = ColorFeature( - self, - colors, - n_colors=self.data().shape[0], + super().__init__( + data=data, + colors=colors, + uniform_color=uniform_color, alpha=alpha, - collection_index=collection_index, - ) - - self.cmap = CmapFeature( - self, self.colors(), cmap_name=cmap, cmap_values=cmap_values + cmap=cmap, + cmap_transform=cmap_transform, + isolated_buffer=isolated_buffer, + **kwargs, ) - super().__init__(*args, **kwargs) + self._thickness = Thickness(thickness) if thickness < 1.1: - material = pygfx.LineThinMaterial + MaterialCls = pygfx.LineThinMaterial else: - material = pygfx.LineMaterial - - self.thickness = ThicknessFeature(self, thickness) + MaterialCls = pygfx.LineMaterial + + if uniform_color: + geometry = pygfx.Geometry(positions=self._data.buffer) + material = MaterialCls( + thickness=self.thickness, + color_mode="uniform", + color=self.colors, + pick_write=True, + ) + else: + material = MaterialCls( + thickness=self.thickness, color_mode="vertex", pick_write=True + ) + geometry = pygfx.Geometry( + positions=self._data.buffer, colors=self._colors.buffer + ) - world_object: pygfx.Line = pygfx.Line( - # self.data.feature_data because data is a Buffer - geometry=pygfx.Geometry(positions=self.data(), colors=self.colors()), - material=material( - thickness=self.thickness(), color_mode="vertex", pick_write=True - ), - ) + world_object: pygfx.Line = pygfx.Line(geometry=geometry, material=material) self._set_world_object(world_object) - if z_position is not None: - self.position_z = z_position + @property + def thickness(self) -> float: + """line thickness""" + return self._thickness.value + + @thickness.setter + def thickness(self, value: float): + self._thickness.set_value(self, value) def add_linear_selector( - self, selection: int = None, padding: float = 50, **kwargs + self, selection: float = None, padding: float = 0.0, axis: str = "x", **kwargs ) -> LinearSelector: """ Adds a linear selector. Parameters ---------- - selection: int - initial position of the selector + Parameters + ---------- + selection: float, optional + selected point on the linear selector, computed from data if not provided - padding: float - pad the length of the selector + axis: str, default "x" + axis that the selector resides on + + padding: float, default 0.0 + Extra padding to extend the linear selector along the orthogonal axis to make it easier to interact with. kwargs passed to :class:`.LinearSelector` @@ -147,38 +134,36 @@ def add_linear_selector( """ - ( - bounds_init, - limits, - size, - origin, - axis, - end_points, - ) = self._get_linear_selector_init_args(padding, **kwargs) + bounds_init, limits, size, center = self._get_linear_selector_init_args( + axis, padding + ) if selection is None: - selection = limits[0] - - if selection < limits[0] or selection > limits[1]: - raise ValueError( - f"the passed selection: {selection} is beyond the limits: {limits}" - ) + selection = bounds_init[0] selector = LinearSelector( selection=selection, limits=limits, - end_points=end_points, - parent=self, + size=size, + center=center, + axis=axis, + parent=weakref.proxy(self), **kwargs, ) self._plot_area.add_graphic(selector, center=False) - selector.position_z = self.position_z + 1 + + # place selector above this graphic + selector.offset = selector.offset + (0.0, 0.0, self.offset[-1] + 1) return weakref.proxy(selector) def add_linear_region_selector( - self, padding: float = 100.0, **kwargs + self, + selection: tuple[float, float] = None, + padding: float = 0.0, + axis: str = "x", + **kwargs, ) -> LinearRegionSelector: """ Add a :class:`.LinearRegionSelector`. Selectors are just ``Graphic`` objects, so you can manage, @@ -186,8 +171,14 @@ def add_linear_region_selector( Parameters ---------- - padding: float, default 100.0 - Extends the linear selector along the y-axis to make it easier to interact with. + selection: (float, float), optional + the starting bounds of the linear region selector, computed from data if not provided + + axis: str, default "x" + axis that the selector resides on + + padding: float, default 0.0 + Extra padding to extend the linear region selector along the orthogonal axis to make it easier to interact with. kwargs passed to ``LinearRegionSelector`` @@ -199,118 +190,61 @@ def add_linear_region_selector( """ - ( - bounds_init, - limits, - size, - origin, - axis, - end_points, - ) = self._get_linear_selector_init_args(padding, **kwargs) + bounds_init, limits, size, center = self._get_linear_selector_init_args( + axis, padding + ) + + if selection is None: + selection = bounds_init # create selector selector = LinearRegionSelector( - bounds=bounds_init, + selection=selection, limits=limits, size=size, - origin=origin, - parent=self, + center=center, + axis=axis, + parent=weakref.proxy(self), **kwargs, ) self._plot_area.add_graphic(selector, center=False) - # so that it is below this graphic - selector.position_z = self.position_z - 1 + + # place selector below this graphic + selector.offset = selector.offset + (0.0, 0.0, self.offset[-1] - 1) # PlotArea manages this for garbage collection etc. just like all other Graphics # so we should only work with a proxy on the user-end return weakref.proxy(selector) # TODO: this method is a bit of a mess, can refactor later - def _get_linear_selector_init_args(self, padding: float, **kwargs): - # computes initial bounds, limits, size and origin of linear selectors - data = self.data() + def _get_linear_selector_init_args( + self, axis: str, padding + ) -> tuple[tuple[float, float], tuple[float, float], float, float]: + # computes args to create selectors + n_datapoints = self.data.value.shape[0] + value_25p = int(n_datapoints / 4) - if "axis" in kwargs.keys(): - axis = kwargs["axis"] - else: - axis = "x" + # remove any nans + data = self.data.value[~np.any(np.isnan(self.data.value), axis=1)] if axis == "x": - offset = self.position_x - # x limits - limits = (data[0, 0] + offset, data[-1, 0] + offset) + # xvals + axis_vals = data[:, 0] - # height + padding - size = np.ptp(data[:, 1]) + padding + # yvals to get size and center + magn_vals = data[:, 1] + elif axis == "y": + axis_vals = data[:, 1] + magn_vals = data[:, 0] - # initial position of the selector - position_y = (data[:, 1].min() + data[:, 1].max()) / 2 + bounds_init = axis_vals[0], axis_vals[value_25p] + limits = axis_vals[0], axis_vals[-1] - # need y offset too for this - origin = (limits[0] - offset, position_y + self.position_y) + # width or height of selector + size = int(np.ptp(magn_vals) * 1.5 + padding) - # endpoints of the data range - # used by linear selector but not linear region - end_points = ( - self.data()[:, 1].min() - padding, - self.data()[:, 1].max() + padding, - ) - else: - offset = self.position_y - # y limits - limits = (data[0, 1] + offset, data[-1, 1] + offset) - - # width + padding - size = np.ptp(data[:, 0]) + padding - - # initial position of the selector - position_x = (data[:, 0].min() + data[:, 0].max()) / 2 - - # need x offset too for this - origin = (position_x + self.position_x, limits[0] - offset) - - end_points = ( - self.data()[:, 0].min() - padding, - self.data()[:, 0].max() + padding, - ) - - # initial bounds are 20% of the limits range - bounds_init = (limits[0], int(np.ptp(limits) * 0.2) + offset) - - return bounds_init, limits, size, origin, axis, end_points + # center of selector along the other axis + center = np.nanmean(magn_vals) - def _fpl_add_plot_area_hook(self, plot_area): - self._plot_area = plot_area - - def set_feature(self, feature: str, new_data: Any, indices: Any = None): - if not hasattr(self, "_previous_data"): - self._previous_data = dict() - elif hasattr(self, "_previous_data"): - self.reset_feature(feature) - - feature_instance = getattr(self, feature) - if indices is not None: - previous = feature_instance[indices].copy() - feature_instance[indices] = new_data - else: - previous = feature_instance._data.copy() - feature_instance._set(new_data) - if feature in self._previous_data.keys(): - self._previous_data[feature].data = previous - self._previous_data[feature].indices = indices - else: - self._previous_data[feature] = PreviouslyModifiedData( - data=previous, indices=indices - ) - - def reset_feature(self, feature: str): - if feature not in self._previous_data.keys(): - return - - prev_ixs = self._previous_data[feature].indices - feature_instance = getattr(self, feature) - if prev_ixs is not None: - feature_instance[prev_ixs] = self._previous_data[feature].data - else: - feature_instance._set(self._previous_data[feature].data) + return bounds_init, limits, size, center diff --git a/fastplotlib/graphics/line_collection.py b/fastplotlib/graphics/line_collection.py index da74cc54e..92aad56b2 100644 --- a/fastplotlib/graphics/line_collection.py +++ b/fastplotlib/graphics/line_collection.py @@ -1,5 +1,4 @@ from typing import * -from copy import deepcopy import weakref import numpy as np @@ -7,27 +6,137 @@ import pygfx from ..utils import parse_cmap_values -from ._base import Interaction, PreviouslyModifiedData, GraphicCollection -from ._features import GraphicFeature +from ._collection_base import CollectionIndexer, GraphicCollection, CollectionFeature from .line import LineGraphic from .selectors import LinearRegionSelector, LinearSelector -class LineCollection(GraphicCollection, Interaction): - child_type = LineGraphic.__name__ +class _LineCollectionProperties: + """Mix-in class for LineCollection properties""" + + @property + def colors(self) -> CollectionFeature: + """get or set colors of lines in the collection""" + return CollectionFeature(self.graphics, "colors") + + @colors.setter + def colors(self, values: str | np.ndarray | tuple[float] | list[float] | list[str]): + if isinstance(values, str): + # set colors of all lines to one str color + for g in self: + g.colors = values + return + + elif all(isinstance(v, str) for v in values): + # individual str colors for each line + if not len(values) == len(self): + raise IndexError + + for g, v in zip(self.graphics, values): + g.colors = v + + return + + if isinstance(values, np.ndarray): + if values.ndim == 2: + # assume individual colors for each + for g, v in zip(self, values): + g.colors = v + return + + elif len(values) == 4: + # assume RGBA + self.colors[:] = values + + else: + # assume individual colors for each + for g, v in zip(self, values): + g.colors = v + + @property + def data(self) -> CollectionFeature: + """get or set data of lines in the collection""" + return CollectionFeature(self.graphics, "data") + + @data.setter + def data(self, values): + for g, v in zip(self, values): + g.data = v + + @property + def cmap(self) -> CollectionFeature: + """ + Get or set a cmap along the line collection. + + Optionally set using a tuple ("cmap", , ) to set the transform and/or alpha. + Example: + + line_collection.cmap = ("jet", sine_transform_vals, 0.7) + + """ + return CollectionFeature(self.graphics, "cmap") + + @cmap.setter + def cmap(self, args): + if isinstance(args, str): + name = args + transform, alpha = None, 1.0 + if len(args) == 1: + name = args[0] + transform, alpha = None, None + + elif len(args) == 2: + name, transform = args + alpha = None + + elif len(args) == 3: + name, transform, alpha = args + + colors = parse_cmap_values( + n_colors=len(self), cmap_name=name, transform=transform + ) + colors[:, -1] = alpha + self.colors = colors + + @property + def thickness(self) -> np.ndarray: + """get or set the thickness of the lines""" + return np.asarray([g.thickness for g in self]) + + @thickness.setter + def thickness(self, values: np.ndarray | list[float]): + if not len(values) == len(self): + raise IndexError + + for g, v in zip(self, values): + g.thickness = v + + +class LineCollectionIndexer(CollectionIndexer, _LineCollectionProperties): + """Indexer for line collections""" + + pass + + +class LineCollection(GraphicCollection, _LineCollectionProperties): + _child_type = LineGraphic + _indexer = LineCollectionIndexer def __init__( self, - data: List[np.ndarray], - z_offset: Iterable[float | int] | float | int = None, - thickness: float | Iterable[float] = 2.0, - colors: str | Iterable[str] | np.ndarray | Iterable[np.ndarray] = "w", + data: np.ndarray | List[np.ndarray], + thickness: float | Sequence[float] = 2.0, + colors: str | Sequence[str] | np.ndarray | Sequence[np.ndarray] = "w", + uniform_colors: bool = False, alpha: float = 1.0, - cmap: Iterable[str] | str = None, - cmap_values: np.ndarray | List = None, + cmap: Sequence[str] | str = None, + cmap_transform: np.ndarray | List = None, name: str = None, - metadata: Iterable[Any] | np.ndarray = None, - *args, + names: list[str] = None, + metadata: Any = None, + metadatas: Sequence[Any] | np.ndarray = None, + isolated_buffer: bool = True, + kwargs_lines: list[dict] = None, **kwargs, ): """ @@ -35,13 +144,11 @@ def __init__( Parameters ---------- - data: list of array-like or array - List of line data to plot, each element must be a 1D, 2D, or 3D numpy array - if elements are 2D, interpreted as [y_vals, n_lines] + data: list of array-like + List or array-like of multiple line data to plot - z_offset: Iterable of float or float, optional - | if ``float`` | ``int``, single offset will be used for all lines - | if ``list`` of ``float`` | ``int``, each value will apply to the individual lines + | if ``list`` each item in the list must be a 1D, 2D, or 3D numpy array + | if array-like, must be of shape [n_lines, n_points_line, y | xy | xyz] thickness: float or Iterable of float, default 2.0 | if ``float``, single thickness will be used for all lines @@ -63,52 +170,58 @@ def __init__( .. note:: ``cmap`` overrides any arguments passed to ``colors`` - cmap_values: 1D array-like or Iterable of numerical values, optional + cmap_transform: 1D array-like of numerical values, optional if provided, these values are used to map the colors from the cmap name: str, optional - name of the line collection + name of the line collection as a whole - metadata: Iterable or array - metadata associated with this collection, this is for the user to manage. - ``len(metadata)`` must be same as ``len(data)`` + names: list[str], optional + names of the individual lines in the collection, ``len(names)`` must equal ``len(data)`` - args - passed to GraphicCollection + metadata: Any + meatadata associated with the collection as a whole - kwargs - passed to GraphicCollection - - Features - -------- + metadatas: Iterable or array + metadata for each individual line associated with this collection, this is for the user to manage. + ``len(metadata)`` must be same as ``len(data)`` - Collections support the same features as the underlying graphic. You just have to slice the selection. + kwargs_lines: list[dict], optional + list of kwargs passed to the individual lines, ``len(kwargs_lines)`` must equal ``len(data)`` - See :class:`LineGraphic` details on the features. + kwargs_collection + kwargs for the collection, passed to GraphicCollection """ - super().__init__(name) + super().__init__(name=name, metadata=metadata, **kwargs) - if not isinstance(z_offset, (float, int)) and z_offset is not None: - if len(data) != len(z_offset): + if not isinstance(thickness, (float, int)): + if len(thickness) != len(data): raise ValueError( - "z_position must be a single float or an iterable with same length as data" + f"len(thickness) != len(data)\n" f"{len(thickness)} != {len(data)}" ) - if not isinstance(thickness, (float, int)): - if len(thickness) != len(data): + if names is not None: + if len(names) != len(data): raise ValueError( - "args must be a single float or an iterable with same length as data" + f"len(names) != len(data)\n" f"{len(names)} != {len(data)}" ) - if metadata is not None: - if len(metadata) != len(data): + if metadatas is not None: + if len(metadatas) != len(data): raise ValueError( - f"len(metadata) != len(data)\n" f"{len(metadata)} != {len(data)}" + f"len(metadata) != len(data)\n" f"{len(metadatas)} != {len(data)}" ) - self._cmap_values = cmap_values + if kwargs_lines is not None: + if len(kwargs_lines) != len(data): + raise ValueError( + f"len(kwargs_lines) != len(data)\n" + f"{len(kwargs_lines)} != {len(data)}" + ) + + self._cmap_transform = cmap_transform self._cmap_str = cmap # cmap takes priority over colors @@ -116,7 +229,7 @@ def __init__( # cmap across lines if isinstance(cmap, str): colors = parse_cmap_values( - n_colors=len(data), cmap_name=cmap, cmap_values=cmap_values + n_colors=len(data), cmap_name=cmap, transform=cmap_transform ) single_color = False cmap = None @@ -175,14 +288,12 @@ def __init__( "or must be a tuple/list of colors represented by a string with the same length as the data" ) + if kwargs_lines is None: + kwargs_lines = dict() + self._set_world_object(pygfx.Group()) for i, d in enumerate(data): - if isinstance(z_offset, list): - _z = z_offset[i] - else: - _z = z_offset - if isinstance(thickness, list): _s = thickness[i] else: @@ -199,66 +310,51 @@ def __init__( _cmap = cmap[i] _c = None - if metadata is not None: - _m = metadata[i] + if metadatas is not None: + _m = metadatas[i] else: _m = None + if names is not None: + _name = names[i] + else: + _name = None + lg = LineGraphic( data=d, thickness=_s, colors=_c, - z_position=_z, + uniform_color=uniform_colors, cmap=_cmap, - collection_index=i, + name=_name, metadata=_m, + isolated_buffer=isolated_buffer, + **kwargs_lines, ) - self.add_graphic(lg, reset_index=False) - - @property - def cmap(self) -> str: - return self._cmap_str - - @cmap.setter - def cmap(self, cmap: str): - colors = parse_cmap_values( - n_colors=len(self), cmap_name=cmap, cmap_values=self.cmap_values - ) - - for i, g in enumerate(self.graphics): - g.colors = colors[i] + self.add_graphic(lg) - self._cmap_str = cmap - - @property - def cmap_values(self) -> np.ndarray: - return self._cmap_values - - @cmap_values.setter - def cmap_values(self, values: np.ndarray | Iterable): - colors = parse_cmap_values( - n_colors=len(self), cmap_name=self.cmap, cmap_values=values - ) - - for i, g in enumerate(self.graphics): - g.colors = colors[i] - - self._cmap_values = values + def __getitem__(self, item) -> LineCollectionIndexer: + return super().__getitem__(item) def add_linear_selector( - self, selection: int = None, padding: float = 50, **kwargs + self, selection: float = None, padding: float = 0.0, axis: str = "x", **kwargs ) -> LinearSelector: """ - Adds a :class:`.LinearSelector` . + Adds a linear selector. Parameters ---------- - selection: int - initial position of the selector + Parameters + ---------- + selection: float, optional + selected point on the linear selector, computed from data if not provided + + axis: str, default "x" + axis that the selector resides on - padding: float - pad the length of the selector + padding: float, default 0.0 + Extra padding to extend the linear selector along the orthogonal axis to make it easier to interact with. kwargs passed to :class:`.LinearSelector` @@ -269,46 +365,51 @@ def add_linear_selector( """ - ( - bounds, - limits, - size, - origin, - axis, - end_points, - ) = self._get_linear_selector_init_args(padding, **kwargs) + bounds_init, limits, size, center = self._get_linear_selector_init_args( + axis, padding + ) if selection is None: - selection = limits[0] - - if selection < limits[0] or selection > limits[1]: - raise ValueError( - f"the passed selection: {selection} is beyond the limits: {limits}" - ) + selection = bounds_init[0] selector = LinearSelector( selection=selection, limits=limits, - end_points=end_points, - parent=self, + size=size, + center=center, + axis=axis, + parent=weakref.proxy(self), **kwargs, ) self._plot_area.add_graphic(selector, center=False) - selector.position_z = self.position_z + 1 + + # place selector above this graphic + selector.offset = selector.offset + (0.0, 0.0, self.offset[-1] + 1) return weakref.proxy(selector) def add_linear_region_selector( - self, padding: float = 100.0, **kwargs + self, + selection: tuple[float, float] = None, + padding: float = 0.0, + axis: str = "x", + **kwargs, ) -> LinearRegionSelector: """ - Add a :class:`.LinearRegionSelector` + Add a :class:`.LinearRegionSelector`. Selectors are just ``Graphic`` objects, so you can manage, + remove, or delete them from a plot area just like any other ``Graphic``. Parameters ---------- - padding: float, default 100.0 - Extends the linear selector along the y-axis to make it easier to interact with. + selection: (float, float), optional + the starting bounds of the linear region selector, computed from data if not provided + + axis: str, default "x" + axis that the selector resides on + + padding: float, default 0.0 + Extra padding to extend the linear region selector along the orthogonal axis to make it easier to interact with. kwargs passed to ``LinearRegionSelector`` @@ -320,155 +421,62 @@ def add_linear_region_selector( """ - ( - bounds, - limits, - size, - origin, - axis, - end_points, - ) = self._get_linear_selector_init_args(padding, **kwargs) + bounds_init, limits, size, center = self._get_linear_selector_init_args( + axis, padding + ) + + if selection is None: + selection = bounds_init + # create selector selector = LinearRegionSelector( - bounds=bounds, + selection=selection, limits=limits, size=size, - origin=origin, - parent=self, + center=center, + axis=axis, + parent=weakref.proxy(self), **kwargs, ) self._plot_area.add_graphic(selector, center=False) - selector.position_z = self.position_z - 1 - - return weakref.proxy(selector) - def _get_linear_selector_init_args(self, padding, **kwargs): - bounds_init = list() - limits = list() - sizes = list() - origin = list() - end_points = list() - - for g in self.graphics: - ( - _bounds_init, - _limits, - _size, - _origin, - axis, - _end_points, - ) = g._get_linear_selector_init_args(padding=0, **kwargs) - - bounds_init.append(_bounds_init) - limits.append(_limits) - sizes.append(_size) - origin.append(_origin) - end_points.append(_end_points) - - # set the init bounds using the extents of the collection - b = np.vstack(bounds_init) - bounds = (b[:, 0].min(), b[:, 1].max()) - - # set the limits using the extents of the collection - limits = np.vstack(limits) - limits = (limits[:, 0].min(), limits[:, 1].max()) - - # stack endpoints - end_points = np.vstack(end_points) - # use the min endpoint for index 0, highest endpoint for index 1 - end_points = [ - end_points[:, 0].min() - padding, - end_points[:, 1].max() + padding, - ] - - # TODO: refactor this to use `LineStack.graphics[-1].position.y` - if isinstance(self, LineStack): - stack_offset = self.separation * len(sizes) - # sum them if it's a stack - size = sum(sizes) - # add the separations - size += stack_offset - - # a better way to get the max y value? - # graphics y-position + data y-max + padding - end_points[1] = ( - self.graphics[-1].position_y - + self.graphics[-1].data()[:, 1].max() - + padding - ) + # place selector below this graphic + selector.offset = selector.offset + (0.0, 0.0, self.offset[-1] - 1) - else: - # just the biggest one if not stacked - size = max(sizes) + # PlotArea manages this for garbage collection etc. just like all other Graphics + # so we should only work with a proxy on the user-end + return weakref.proxy(selector) - size += padding + def _get_linear_selector_init_args(self, axis, padding): + # use bbox to get size and center + bbox = self.world_object.get_world_bounding_box() if axis == "x": - o = np.vstack(origin) - origin_y = (o[:, 1].min() + o[:, 1].max()) / 2 - origin = (limits[0], origin_y) - else: - o = np.vstack(origin) - origin_x = (o[:, 0].min() + o[:, 0].max()) / 2 - origin = (origin_x, limits[0]) - - return bounds, limits, size, origin, axis, end_points - - def _fpl_add_plot_area_hook(self, plot_area): - self._plot_area = plot_area - - def set_feature(self, feature: str, new_data: Any, indices: Any): - # if single value force to be an array of size 1 - if isinstance(indices, (np.integer, int)): - indices = np.array([indices]) - if not hasattr(self, "_previous_data"): - self._previous_data = dict() - elif hasattr(self, "_previous_data"): - if feature in self._previous_data.keys(): - # for now assume same index won't be changed with diff data - # I can't think of a usecase where we'd have to check the data too - # so unless there is a bug we keep it like this - if self._previous_data[feature].indices == indices: - return # nothing to change, and this allows bidirectional linking without infinite recursion - - self.reset_feature(feature) + xdata = np.array(self.data[:, 0]) + xmin, xmax = (np.nanmin(xdata), np.nanmax(xdata)) + value_25p = (xmax - xmin) / 4 - # coll_feature = getattr(self[indices], feature) + bounds = (xmin, value_25p) + limits = (xmin, xmax) + # size from orthogonal axis + size = bbox[:, 1].ptp() * 1.5 + # center on orthogonal axis + center = bbox[:, 1].mean() - data = list() + elif axis == "y": + ydata = np.array(self.data[:, 1]) + xmin, xmax = (np.nanmin(ydata), np.nanmax(ydata)) + value_25p = (xmax - xmin) / 4 - for graphic in self.graphics[indices]: - feature_instance: GraphicFeature = getattr(graphic, feature) - data.append(feature_instance()) - - # later we can think about multi-index events - previous_data = deepcopy(data[0]) - - if feature in self._previous_data.keys(): - self._previous_data[feature].data = previous_data - self._previous_data[feature].indices = indices - else: - self._previous_data[feature] = PreviouslyModifiedData( - data=previous_data, indices=indices - ) - - # finally set the new data - # this MUST occur after setting the previous data attribute to prevent recursion - # since calling `feature._set()` triggers all the feature callbacks - feature_instance._set(new_data) - - def reset_feature(self, feature: str): - if feature not in self._previous_data.keys(): - return + bounds = (xmin, value_25p) + limits = (xmin, xmax) - # implemented for a single index at moment - prev_ixs = self._previous_data[feature].indices - coll_feature = getattr(self[prev_ixs], feature) + size = bbox[:, 0].ptp() * 1.5 + # center on orthogonal axis + center = bbox[:, 0].mean() - coll_feature.block_events(True) - coll_feature._set(self._previous_data[feature].data) - coll_feature.block_events(False) + return bounds, limits, size, center axes = {"x": 0, "y": 1, "z": 2} @@ -478,17 +486,19 @@ class LineStack(LineCollection): def __init__( self, data: List[np.ndarray], - z_offset: Iterable[float] | float = None, thickness: float | Iterable[float] = 2.0, colors: str | Iterable[str] | np.ndarray | Iterable[np.ndarray] = "w", alpha: float = 1.0, cmap: Iterable[str] | str = None, - cmap_values: np.ndarray | List = None, + cmap_transform: np.ndarray | List = None, name: str = None, - metadata: Iterable[Any] | np.ndarray = None, + names: list[str] = None, + metadata: Any = None, + metadatas: Sequence[Any] | np.ndarray = None, + isolated_buffer: bool = True, separation: float = 10.0, separation_axis: str = "y", - *args, + kwargs_lines: list[dict] = None, **kwargs, ): """ @@ -496,13 +506,11 @@ def __init__( Parameters ---------- - data: list of array-like or array - List of line data to plot, each element must be a 1D, 2D, or 3D numpy array - if elements are 2D, interpreted as [y_vals, n_lines] + data: list of array-like + List or array-like of multiple line data to plot - z_offset: Iterable of float or float, optional - | if ``float``, single offset will be used for all lines - | if ``list`` of ``float``, each value will apply to the individual lines + | if ``list`` each item in the list must be a 1D, 2D, or 3D numpy array + | if array-like, must be of shape [n_lines, n_points_line, y | xy | xyz] thickness: float or Iterable of float, default 2.0 | if ``float``, single thickness will be used for all lines @@ -514,6 +522,9 @@ def __init__( | if ``list`` of ``str``, represents color for each individual line, example ["w", "b", "r",...] | if ``RGBA array`` of shape [data_size, 4], represents a single RGBA array for each line + alpha: float, optional + alpha value for colors, if colors is a ``str`` + cmap: Iterable of str or str, optional | if ``str``, single cmap will be used for all lines | if ``list`` of ``str``, each cmap will apply to the individual lines @@ -521,11 +532,20 @@ def __init__( .. note:: ``cmap`` overrides any arguments passed to ``colors`` - cmap_values: 1D array-like or Iterable of numerical values, optional + cmap_transform: 1D array-like of numerical values, optional if provided, these values are used to map the colors from the cmap - metadata: Iterable or array - metadata associated with this collection, this is for the user to manage. + name: str, optional + name of the line collection as a whole + + names: list[str], optional + names of the individual lines in the collection, ``len(names)`` must equal ``len(data)`` + + metadata: Any + metadata associated with the collection as a whole + + metadatas: Iterable or array + metadata for each individual line associated with this collection, this is for the user to manage. ``len(metadata)`` must be same as ``len(data)`` separation: float, default 10 @@ -534,43 +554,40 @@ def __init__( separation_axis: str, default "y" axis in which the line graphics in the stack should be separated - name: str, optional - name of the line stack - - kwargs - passed to LineCollection - - Features - -------- - Collections support the same features as the underlying graphic. You just have to slice the selection. + kwargs_lines: list[dict], optional + list of kwargs passed to the individual lines, ``len(kwargs_lines)`` must equal ``len(data)`` - See :class:`LineGraphic` details on the features. + kwargs_collection + kwargs for the collection, passed to GraphicCollection """ super().__init__( data=data, - z_offset=z_offset, thickness=thickness, colors=colors, alpha=alpha, cmap=cmap, - cmap_values=cmap_values, - metadata=metadata, + cmap_transform=cmap_transform, name=name, - *args, + names=names, + metadata=metadata, + metadatas=metadatas, + isolated_buffer=isolated_buffer, + kwargs_lines=kwargs_lines, **kwargs, ) axis_zero = 0 for i, line in enumerate(self.graphics): if separation_axis == "x": - line.position_x = axis_zero + line.offset = (axis_zero, *line.offset[1:]) + elif separation_axis == "y": - line.position_y = axis_zero + line.offset = (line.offset[0], axis_zero, line.offset[2]) axis_zero = ( - axis_zero + line.data()[:, axes[separation_axis]].max() + separation + axis_zero + line.data.value[:, axes[separation_axis]].max() + separation ) self.separation = separation diff --git a/fastplotlib/graphics/scatter.py b/fastplotlib/graphics/scatter.py index 8682df3d5..39d815c95 100644 --- a/fastplotlib/graphics/scatter.py +++ b/fastplotlib/graphics/scatter.py @@ -3,24 +3,24 @@ import numpy as np import pygfx -from ..utils import parse_cmap_values -from ._base import Graphic -from ._features import PointsDataFeature, ColorFeature, CmapFeature, PointsSizesFeature +from ._positions_base import PositionsGraphic +from ._features import PointsSizesFeature, UniformSize -class ScatterGraphic(Graphic): - feature_events = {"data", "sizes", "colors", "cmap", "present"} +class ScatterGraphic(PositionsGraphic): + _features = {"data", "sizes", "colors", "cmap"} def __init__( self, - data: np.ndarray, - sizes: float | np.ndarray | Iterable[float] = 1, - colors: str | np.ndarray | Iterable[str] = "w", + data: Any, + colors: str | np.ndarray | tuple[float] | list[float] | list[str] = "w", + uniform_color: bool = False, alpha: float = 1.0, cmap: str = None, - cmap_values: np.ndarray | List = None, - z_position: float = 0.0, - *args, + cmap_transform: np.ndarray = None, + isolated_buffer: bool = True, + sizes: float | np.ndarray | Iterable[float] = 1, + uniform_size: bool = False, **kwargs, ): """ @@ -31,73 +31,92 @@ def __init__( data: array-like Scatter data to plot, 2D must be of shape [n_points, 2], 3D must be of shape [n_points, 3] - sizes: float or iterable of float, optional, default 1.0 - size of the scatter points - colors: str, array, or iterable, default "w" specify colors as a single human readable string, a single RGBA array, or an iterable of strings or RGBA arrays + uniform_color: bool, default False + if True, uses a uniform buffer for the scatter point colors, + basically saves GPU VRAM when the entire line has a single color + + alpha: float, optional, default 1.0 + alpha value for the colors + cmap: str, optional apply a colormap to the scatter instead of assigning colors manually, this overrides any argument passed to "colors" - cmap_values: 1D array-like or list of numerical values, optional + cmap_transform: 1D array-like or list of numerical values, optional if provided, these values are used to map the colors from the cmap - alpha: float, optional, default 1.0 - alpha value for the colors + isolated_buffer: bool, default True + whether the buffers should be isolated from the user input array. + Generally always ``True``, ``False`` is for rare advanced use. - z_position: float, optional - z-axis position for placing the graphic + sizes: float or iterable of float, optional, default 1.0 + size of the scatter points - args - passed to Graphic + uniform_size: bool, default False + if True, uses a uniform buffer for the scatter point sizes, + basically saves GPU VRAM when all scatter points are the same size kwargs passed to Graphic - Features - -------- - - **data**: :class:`.ImageDataFeature` - Manages the line [x, y, z] positions data buffer, allows regular and fancy indexing. - - **colors**: :class:`.ColorFeature` - Manages the color buffer, allows regular and fancy indexing. - - **cmap**: :class:`.CmapFeature` - Manages the cmap, wraps :class:`.ColorFeature` to add additional functionality relevant to cmaps. + """ - **present**: :class:`.PresentFeature` - Control the presence of the Graphic in the scene, set to ``True`` or ``False`` + super().__init__( + data=data, + colors=colors, + uniform_color=uniform_color, + alpha=alpha, + cmap=cmap, + cmap_transform=cmap_transform, + isolated_buffer=isolated_buffer, + **kwargs, + ) - """ - self.data = PointsDataFeature(self, data) - n_datapoints = self.data().shape[0] + n_datapoints = self.data.value.shape[0] - if cmap is not None: - colors = parse_cmap_values( - n_colors=n_datapoints, cmap_name=cmap, cmap_values=cmap_values - ) + geo_kwargs = {"positions": self._data.buffer} + material_kwargs = {"pick_write": True} - self.colors = ColorFeature(self, colors, n_colors=n_datapoints, alpha=alpha) - self.cmap = CmapFeature( - self, self.colors(), cmap_name=cmap, cmap_values=cmap_values - ) + if uniform_color: + material_kwargs["color_mode"] = "uniform" + material_kwargs["color"] = self.colors + else: + material_kwargs["color_mode"] = "vertex" + geo_kwargs["colors"] = self.colors.buffer - self.sizes = PointsSizesFeature(self, sizes) - super().__init__(*args, **kwargs) + if uniform_size: + material_kwargs["size_mode"] = "uniform" + self._sizes = UniformSize(sizes) + material_kwargs["size"] = self.sizes + else: + material_kwargs["size_mode"] = "vertex" + self._sizes = PointsSizesFeature(sizes, n_datapoints=n_datapoints) + geo_kwargs["sizes"] = self.sizes.buffer world_object = pygfx.Points( - pygfx.Geometry( - positions=self.data(), sizes=self.sizes(), colors=self.colors() - ), - material=pygfx.PointsMaterial( - color_mode="vertex", size_mode="vertex", pick_write=True - ), + pygfx.Geometry(**geo_kwargs), + material=pygfx.PointsMaterial(**material_kwargs), ) self._set_world_object(world_object) - self.position_z = z_position + @property + def sizes(self) -> PointsSizesFeature | float: + """Get or set the scatter point size(s)""" + if isinstance(self._sizes, PointsSizesFeature): + return self._sizes + + elif isinstance(self._sizes, UniformSize): + return self._sizes.value + + @sizes.setter + def sizes(self, value): + if isinstance(self._sizes, PointsSizesFeature): + self._sizes[:] = value + + elif isinstance(self._sizes, UniformSize): + self._sizes.set_value(self, value) diff --git a/fastplotlib/graphics/selectors/__init__.py b/fastplotlib/graphics/selectors/__init__.py index 1fb0c453e..4f28f571c 100644 --- a/fastplotlib/graphics/selectors/__init__.py +++ b/fastplotlib/graphics/selectors/__init__.py @@ -2,11 +2,5 @@ from ._linear_region import LinearRegionSelector from ._polygon import PolygonSelector -from ._sync import Synchronizer -__all__ = [ - "LinearSelector", - "LinearRegionSelector", - "PolygonSelector", - "Synchronizer", -] +__all__ = ["LinearSelector", "LinearRegionSelector"] diff --git a/fastplotlib/graphics/selectors/_base_selector.py b/fastplotlib/graphics/selectors/_base_selector.py index f20eba4a0..0fc48058d 100644 --- a/fastplotlib/graphics/selectors/_base_selector.py +++ b/fastplotlib/graphics/selectors/_base_selector.py @@ -35,7 +35,11 @@ class MoveInfo: # Selector base class class BaseSelector(Graphic): - feature_events = ("selection",) + _features = {"selection"} + + @property + def axis(self) -> str: + return self._axis def __init__( self, @@ -45,7 +49,8 @@ def __init__( hover_responsive: Tuple[WorldObject, ...] = None, arrow_keys_modifier: str = None, axis: str = None, - name: str = None, + parent: Graphic = None, + **kwargs, ): if edges is None: edges = tuple() @@ -71,7 +76,7 @@ def __init__( for wo in self._hover_responsive: self._original_colors[wo] = wo.material.color - self.axis = axis + self._axis = axis # current delta in world coordinates self.delta: np.ndarray = None @@ -95,7 +100,9 @@ def __init__( self._pygfx_event = None - Graphic.__init__(self, name=name) + self._parent = parent + + Graphic.__init__(self, **kwargs) def get_selected_index(self): """Not implemented for this selector""" @@ -110,7 +117,7 @@ def get_selected_data(self): raise NotImplementedError def _get_source(self, graphic): - if self.parent is None and graphic is None: + if self._parent is None and graphic is None: raise AttributeError( "No Graphic to apply selector. " "You must either set a ``parent`` Graphic on the selector, or pass a graphic." @@ -120,7 +127,7 @@ def _get_source(self, graphic): if graphic is not None: source = graphic else: - source = self.parent + source = self._parent return source @@ -262,7 +269,7 @@ def _move_to_pointer(self, ev): """ Calculates delta just using current world object position and calls self._move_graphic(). """ - current_position: np.ndarray = self.position + current_position: np.ndarray = self.offset # middle mouse button clicks if ev.button != 3: @@ -348,8 +355,6 @@ def _key_down(self, ev): if ev.key not in key_bind_direction.keys(): return - # print(ev.key) - self._key_move_value = ev.key def _key_up(self, ev): diff --git a/fastplotlib/graphics/selectors/_linear.py b/fastplotlib/graphics/selectors/_linear.py index 82e553f0a..22ba96a28 100644 --- a/fastplotlib/graphics/selectors/_linear.py +++ b/fastplotlib/graphics/selectors/_linear.py @@ -6,7 +6,8 @@ import pygfx from ...utils.gui import IS_JUPYTER -from .._base import Graphic, GraphicCollection +from .._base import Graphic +from .._collection_base import GraphicCollection from .._features._selection_features import LinearSelectionFeature from ._base_selector import BaseSelector @@ -17,6 +18,26 @@ class LinearSelector(BaseSelector): + @property + def parent(self) -> Graphic: + return self._parent + + @property + def selection(self) -> float: + """ + x or y value of selector's current position + """ + return self._selection.value + + @selection.setter + def selection(self, value: int): + graphic = self._parent + + if isinstance(graphic, GraphicCollection): + pass + + self._selection.set_value(self, value) + @property def limits(self) -> Tuple[float, float]: return self._limits @@ -35,14 +56,15 @@ def limits(self, values: Tuple[float, float]): # TODO: make `selection` arg in graphics data space not world space def __init__( self, - selection: int, - limits: Tuple[int, int], + selection: float, + limits: Sequence[float], + size: float, + center: float, axis: str = "x", parent: Graphic = None, - end_points: Tuple[int, int] = None, - arrow_keys_modifier: str = "Shift", + color: str | tuple = "w", thickness: float = 2.5, - color: Any = "w", + arrow_keys_modifier: str = "Shift", name: str = None, ): """ @@ -59,12 +81,12 @@ def __init__( axis: str, default "x" "x" | "y", the axis which the slider can move along + center: float + center offset of the selector on the orthogonal axis, by default the data mean + parent: Graphic parent graphic for this LineSelector - end_points: (int, int) - set length of slider by bounding it between two x-pos or two y-pos - arrow_keys_modifier: str modifier key that must be pressed to initiate movement using arrow keys, must be one of: "Control", "Shift", "Alt" or ``None``. Double click on the selector first to enable the @@ -79,34 +101,23 @@ def __init__( name: str, optional name of line slider - Features - -------- - - selection: :class:`.LinearSelectionFeature` - ``selection()`` returns the current selector position in world coordinates. - Use ``get_selected_index()`` to get the currently selected index in data - space. - Use ``selection.add_event_handler()`` to add callback functions that are - called when the LinearSelector selection changes. See feature class for - event pick_info table - """ if len(limits) != 2: raise ValueError("limits must be a tuple of 2 integers, i.e. (int, int)") - self._limits = tuple(map(round, limits)) + self._limits = np.asarray(limits) - selection = round(selection) + end_points = [-size / 2, size / 2] if axis == "x": - xs = np.zeros(2) + xs = np.array([selection, selection]) ys = np.array(end_points) zs = np.zeros(2) line_data = np.column_stack([xs, ys, zs]) elif axis == "y": xs = np.array(end_points) - ys = np.zeros(2) + ys = np.array([selection, selection]) zs = np.zeros(2) line_data = np.column_stack([xs, ys, zs]) @@ -144,12 +155,15 @@ def __init__( self._move_info: dict = None - self.parent = parent - self._block_ipywidget_call = False self._handled_widgets = list() + if axis == "x": + offset = (parent.offset[0], center, 0) + elif axis == "y": + offset = (center, parent.offset[1], 0) + # init base selector BaseSelector.__init__( self, @@ -157,20 +171,28 @@ def __init__( hover_responsive=(line_inner, self.line_outer), arrow_keys_modifier=arrow_keys_modifier, axis=axis, + parent=parent, name=name, + offset=offset, ) self._set_world_object(world_object) - self.selection = LinearSelectionFeature( - self, axis=axis, value=selection, limits=self._limits + self._selection = LinearSelectionFeature( + axis=axis, value=selection, limits=self._limits ) - self.selection = selection + if self._parent is not None: + self.selection = selection + else: + self._selection.set_value(self, selection) + + # update any ipywidgets + self.add_event_handler(self._update_ipywidgets, "selection") def _setup_ipywidget_slider(self, widget): # setup an ipywidget slider with bidirectional callbacks to this LinearSelector - value = self.selection() + value = self.selection if isinstance(widget, ipywidgets.IntSlider): value = int(value) @@ -180,16 +202,13 @@ def _setup_ipywidget_slider(self, widget): # user changes widget -> linear selection changes widget.observe(self._ipywidget_callback, "value") - # user changes linear selection -> widget changes - self.selection.add_event_handler(self._update_ipywidgets) - self._handled_widgets.append(widget) def _update_ipywidgets(self, ev): # update the ipywidget sliders when LinearSelector value changes self._block_ipywidget_call = True # prevent infinite recursion - value = ev.pick_info["new_data"] + value = ev.info["value"] # update all the handled slider widgets for widget in self._handled_widgets: if isinstance(widget, ipywidgets.IntSlider): @@ -200,7 +219,7 @@ def _update_ipywidgets(self, ev): self._block_ipywidget_call = False def _ipywidget_callback(self, change): - # update the LinearSelector if the ipywidget value changes + # update the LinearSelector when the ipywidget value changes if self._block_ipywidget_call or self._moving: return @@ -249,9 +268,9 @@ def make_ipywidget_slider(self, kind: str = "IntSlider", **kwargs): cls = getattr(ipywidgets, kind) - value = self.selection() + value = self.selection if "Int" in kind: - value = int(self.selection()) + value = int(self.selection) slider = cls( min=self.limits[0], @@ -327,34 +346,32 @@ def get_selected_index(self, graphic: Graphic = None) -> Union[int, List[int]]: def _get_selected_index(self, graphic): # the array to search for the closest value along that axis if self.axis == "x": - geo_positions = graphic.data()[:, 0] - offset = getattr(graphic, f"position_{self.axis}") - else: - geo_positions = graphic.data()[:, 1] - offset = getattr(graphic, f"position_{self.axis}") + data = graphic.data[:, 0] + elif self.axis == "y": + data = graphic.data[:, 1] - if "Line" in graphic.__class__.__name__: - # we want to find the index of the geometry position that is closest to the slider's geometry position - find_value = self.selection() - offset + if ( + "Line" in graphic.__class__.__name__ + or "Scatter" in graphic.__class__.__name__ + ): + # we want to find the index of the data closest to the slider position + find_value = self.selection # get closest data index to the world space position of the slider - idx = np.searchsorted(geo_positions, find_value, side="left") + idx = np.searchsorted(data, find_value, side="left") if idx > 0 and ( - idx == len(geo_positions) - or math.fabs(find_value - geo_positions[idx - 1]) - < math.fabs(find_value - geo_positions[idx]) + idx == len(data) + or math.fabs(find_value - data[idx - 1]) + < math.fabs(find_value - data[idx]) ): return round(idx - 1) else: return round(idx) - if ( - "Heatmap" in graphic.__class__.__name__ - or "Image" in graphic.__class__.__name__ - ): + if "Image" in graphic.__class__.__name__: # indices map directly to grid geometry for image data buffer - index = self.selection() - offset + index = self.selection return round(index) def _move_graphic(self, delta: np.ndarray): @@ -369,9 +386,9 @@ def _move_graphic(self, delta: np.ndarray): """ if self.axis == "x": - self.selection = self.selection() + delta[0] + self.selection = self.selection + delta[0] else: - self.selection = self.selection() + delta[1] + self.selection = self.selection + delta[1] def _fpl_cleanup(self): for widget in self._handled_widgets: diff --git a/fastplotlib/graphics/selectors/_linear_region.py b/fastplotlib/graphics/selectors/_linear_region.py index 09c134800..ecc67b885 100644 --- a/fastplotlib/graphics/selectors/_linear_region.py +++ b/fastplotlib/graphics/selectors/_linear_region.py @@ -5,7 +5,8 @@ import pygfx from ...utils.gui import IS_JUPYTER -from .._base import Graphic, GraphicCollection +from .._base import Graphic +from .._collection_base import GraphicCollection from .._features._selection_features import LinearRegionSelectionFeature from ._base_selector import BaseSelector @@ -16,6 +17,35 @@ class LinearRegionSelector(BaseSelector): + @property + def parent(self) -> Graphic | None: + """graphic that the selector is associated with""" + return self._parent + + @property + def selection(self) -> Sequence[float] | List[Sequence[float]]: + """ + (min, max) of data value along selector's axis + """ + # TODO: This probably does not account for rotation since world.position + # does not account for rotation, we can do this later + + return self._selection.value.copy() + + # TODO: if no parent graphic is set, this just returns world positions + # but should we change it? + # return self._selection.value + + @selection.setter + def selection(self, selection: Sequence[float]): + # set (xmin, xmax), or (ymin, ymax) of the selector in data space + graphic = self._parent + + if isinstance(graphic, GraphicCollection): + pass + + self._selection.set_value(self, selection) + @property def limits(self) -> Tuple[float, float]: return self._limits @@ -29,54 +59,50 @@ def limits(self, values: Tuple[float, float]): self._limits = tuple( map(round, values) ) # if values are close to zero things get weird so round them - self.selection._limits = self._limits + self._selection._limits = self._limits def __init__( self, - bounds: Tuple[int, int], - limits: Tuple[int, int], - size: int, - origin: Tuple[int, int], + selection: Sequence[float], + limits: Sequence[float], + size: float, + center: float, axis: str = "x", parent: Graphic = None, resizable: bool = True, fill_color=(0, 0, 0.35), - edge_color=(0.8, 0.8, 0), - edge_thickness: int = 3, + edge_color=(0.8, 0.6, 0), + edge_thickness: float = 8, arrow_keys_modifier: str = "Shift", name: str = None, ): """ Create a LinearRegionSelector graphic which can be moved only along either the x-axis or y-axis. - Allows sub-selecting data from a ``Graphic`` or from multiple Graphics. - - bounds[0], limits[0], and position[0] must be identical. - - Holding the right mouse button while dragging an edge will force the entire region selector to move. This is - a when using transparent fill areas due to ``pygfx`` picking limitations. + Allows sub-selecting data from a parent ``Graphic`` or from multiple Graphics. - **Note:** Events get very weird if the values of bounds, limits and origin are close to zero. If you need - a linear selector with small data, we recommend scaling the data and then using the selector. + Assumes that the data under the selector is a function of the axis on which the selector moves + along. Example: if the selector is along the x-axis, then there must be only one y-value for each + x-value, otherwise functions such as ``get_selected_data()`` do not make sense. Parameters ---------- - bounds: (int, int) - the initial bounds of the linear selector + selection: (float, float) + initial (min, max) x or y values - limits: (int, int) - (min limit, max limit) for the selector + limits: (float, float) + (min limit, max limit) within which the selector can move size: int height or width of the selector - origin: (int, int) - initial position of the selector + center: float + center offset of the selector on the orthogonal axis, by default the data mean axis: str, default "x" - "x" | "y", axis for the selector + "x" | "y", axis the selected can move on parent: Graphic, default ``None`` - associate this selector with a parent Graphic + associate this selector with a parent Graphic from which to fetch data or indices resizable: bool if ``True``, the edges can be dragged to resize the width of the linear selection @@ -87,6 +113,9 @@ def __init__( edge_color: str, array, or tuple edge color for the selector, passed to pygfx.Color + edge_thickness: float, default 8 + edge thickness + arrow_keys_modifier: str modifier key that must be pressed to initiate movement using arrow keys, must be one of: "Control", "Shift", "Alt" or ``None`` @@ -94,46 +123,22 @@ def __init__( name: str name for this selector graphic - Features - -------- - - selection: :class:`.LinearRegionSelectionFeature` - ``selection()`` returns the current selector bounds in world coordinates. - Use ``get_selected_indices()`` to return the selected indices in data - space, and ``get_selected_data()`` to return the selected data. - Use ``selection.add_event_handler()`` to add callback functions that are - called when the LinearSelector selection changes. See feature class for - event pick_info table. - """ # lots of very close to zero values etc. so round them, otherwise things get weird - bounds = tuple(map(round, bounds)) - self._limits = tuple(map(round, limits)) - origin = tuple(map(round, origin)) + if not len(selection) == 2: + raise ValueError + + selection = np.asarray(selection) + + if not len(limits) == 2: + raise ValueError + + self._limits = np.asarray(limits) # TODO: sanity checks, we recommend users to add LinearSelection using the add_linear_selector() methods # TODO: so we can worry about the sanity checks later - # if axis == "x": - # if limits[0] != origin[0] != bounds[0]: - # raise ValueError( - # f"limits[0] != position[0] != bounds[0]\n" - # f"{limits[0]} != {origin[0]} != {bounds[0]}" - # ) - # - # elif axis == "y": - # # initial y-position is position[1] - # if limits[0] != origin[1] != bounds[0]: - # raise ValueError( - # f"limits[0] != position[1] != bounds[0]\n" - # f"{limits[0]} != {origin[1]} != {bounds[0]}" - # ) - - self.parent = parent - - # world object for this will be a group - # basic mesh for the fill area of the selector - # line for each edge of the selector + group = pygfx.Group() if axis == "x": @@ -152,89 +157,69 @@ def __init__( # the fill of the selection self.fill = mesh - self.fill.world.position = (*origin, -2) + # no x, y offsets for linear region selector + # everything is done by setting the mesh data + # and line positions + self.fill.world.position = (0, 0, -2) group.add(self.fill) self._resizable = resizable if axis == "x": - # position data for the left edge line - left_line_data = np.array( - [ - [origin[0], (-size / 2) + origin[1], 0.5], - [origin[0], (size / 2) + origin[1], 0.5], - ] - ).astype(np.float32) - - left_line = pygfx.Line( - pygfx.Geometry(positions=left_line_data), - pygfx.LineMaterial( - thickness=edge_thickness, color=edge_color, pick_write=True - ), - ) - - # position data for the right edge line - right_line_data = np.array( - [ - [bounds[1], (-size / 2) + origin[1], 0.5], - [bounds[1], (size / 2) + origin[1], 0.5], - ] - ).astype(np.float32) - - right_line = pygfx.Line( - pygfx.Geometry(positions=right_line_data), - pygfx.LineMaterial( - thickness=edge_thickness, color=edge_color, pick_write=True - ), + # just some data to initialize the edge lines + init_line_data = np.array([[0, -size / 2, 0], [0, size / 2, 0]]).astype( + np.float32 ) - self.edges: Tuple[pygfx.Line, pygfx.Line] = (left_line, right_line) - elif axis == "y": - # position data for the left edge line - bottom_line_data = np.array( - [ - [(-size / 2) + origin[0], origin[1], 0.5], - [(size / 2) + origin[0], origin[1], 0.5], - ] - ).astype(np.float32) - - bottom_line = pygfx.Line( - pygfx.Geometry(positions=bottom_line_data), - pygfx.LineMaterial( - thickness=edge_thickness, color=edge_color, pick_write=True - ), - ) - - # position data for the right edge line - top_line_data = np.array( + # just some line data to initialize y axis edge lines + init_line_data = np.array( [ - [(-size / 2) + origin[0], bounds[1], 0.5], - [(size / 2) + origin[0], bounds[1], 0.5], + [-size / 2, 0, 0], + [size / 2, 0, 0], ] ).astype(np.float32) - top_line = pygfx.Line( - pygfx.Geometry(positions=top_line_data), - pygfx.LineMaterial( - thickness=edge_thickness, color=edge_color, pick_write=True - ), - ) - - self.edges: Tuple[pygfx.Line, pygfx.Line] = (bottom_line, top_line) - else: raise ValueError("axis argument must be one of 'x' or 'y'") + line0 = pygfx.Line( + pygfx.Geometry( + positions=init_line_data.copy() + ), # copy so the line buffer is isolated + pygfx.LineMaterial( + thickness=edge_thickness, color=edge_color, pick_write=True + ), + ) + line1 = pygfx.Line( + pygfx.Geometry( + positions=init_line_data.copy() + ), # copy so the line buffer is isolated + pygfx.LineMaterial( + thickness=edge_thickness, color=edge_color, pick_write=True + ), + ) + + self.edges: Tuple[pygfx.Line, pygfx.Line] = (line0, line1) + # add the edge lines for edge in self.edges: - edge.world.z = -1 + edge.world.z = -0.5 group.add(edge) + # TODO: if parent offset changes, we should set the selector offset too + # TODO: add check if parent is `None`, will throw error otherwise + if axis == "x": + offset = (parent.offset[0], center, 0) + elif axis == "y": + offset = (center, parent.offset[1], 0) + # set the initial bounds of the selector - self.selection = LinearRegionSelectionFeature( - self, bounds, axis=axis, limits=self._limits + # compensate for any offset from the parent graphic + # selection feature only works in world space, not data space + self._selection = LinearRegionSelectionFeature( + selection, axis=axis, limits=self._limits ) self._handled_widgets = list() @@ -248,17 +233,22 @@ def __init__( hover_responsive=self.edges, arrow_keys_modifier=arrow_keys_modifier, axis=axis, + parent=parent, name=name, + offset=offset, ) self._set_world_object(group) + self.selection = selection + def get_selected_data( self, graphic: Graphic = None - ) -> Union[np.ndarray, List[np.ndarray], None]: + ) -> Union[np.ndarray, List[np.ndarray]]: """ Get the ``Graphic`` data bounded by the current selection. - Returns a view of the full data array. + Returns a view of the data array. + If the ``Graphic`` is a collection, such as a ``LineStack``, it returns a list of views of the full array. Can be performed on the ``parent`` Graphic or on another graphic by passing to the ``graphic`` arg. @@ -269,15 +259,16 @@ def get_selected_data( Parameters ---------- - graphic: Graphic, optional + graphic: Graphic, optional, default ``None`` if provided, returns the data selection from this graphic instead of the graphic set as ``parent`` Returns ------- - np.ndarray, List[np.ndarray], or None + np.ndarray or List[np.ndarray] view or list of views of the full array, returns ``None`` if selection is empty """ + source = self._get_source(graphic) ixs = self.get_selected_indices(source) @@ -290,40 +281,47 @@ def get_selected_data( for i, g in enumerate(source.graphics): if ixs[i].size == 0: - data_selections.append(None) + data_selections.append( + np.array([], dtype=np.float32).reshape(0, 3) + ) else: - s = slice(ixs[i][0], ixs[i][-1]) - data_selections.append(g.data.buffer.data[s]) + s = slice( + ixs[i][0], ixs[i][-1] + 1 + ) # add 1 because these are direct indices + # slices n_datapoints dim + data_selections.append(g.data[s]) - return source[:].data[s] - # just for one Line graphic + return source.data[s] else: if ixs.size == 0: - return None + # empty selection + return np.array([], dtype=np.float32).reshape(0, 3) - s = slice(ixs[0], ixs[-1]) - return source.data.buffer.data[s] + s = slice( + ixs[0], ixs[-1] + 1 + ) # add 1 to end because these are direct indices + # slices n_datapoints dim + # slice with min, max is faster than using all the indices + return source.data[s] + + if "Image" in source.__class__.__name__: + s = slice(ixs[0], ixs[-1] + 1) - if ( - "Heatmap" in source.__class__.__name__ - or "Image" in source.__class__.__name__ - ): - s = slice(ixs[0], ixs[-1]) if self.axis == "x": - return source.data()[:, s] + # slice columns + return source.data[:, s] + elif self.axis == "y": - return source.data()[s] + # slice rows + return source.data[s] def get_selected_indices( self, graphic: Graphic = None ) -> Union[np.ndarray, List[np.ndarray]]: """ Returns the indices of the ``Graphic`` data bounded by the current selection. - This is useful because the ``bounds`` min and max are not necessarily the same - as the Line Geometry positions x-vals or y-vals. For example, if if you used a - np.linspace(0, 100, 1000) for xvals in your line, then you will have 1,000 - x-positions. If the selection ``bounds`` are set to ``(0, 10)``, the returned - indices would be ``(0, 100)``. + + These are the data indices along the selector's "axis" which correspond to the data under the selector. Parameters ---------- @@ -333,51 +331,45 @@ def get_selected_indices( Returns ------- Union[np.ndarray, List[np.ndarray]] - data indices of the selection, list of np.ndarray if graphic is LineCollection + data indices of the selection, list of np.ndarray if graphic is a collection """ + # we get the indices from the source graphic source = self._get_source(graphic) - # if the graphic position is not at (0, 0) then the bounds must be offset - offset = getattr(source, f"position_{self.selection.axis}") - offset_bounds = tuple(v - offset for v in self.selection()) - - # need them to be int to use as indices - offset_bounds = tuple(map(int, offset_bounds)) - - if self.selection.axis == "x": + # get the offset of the source graphic + if self.axis == "x": dim = 0 - else: + elif self.axis == "y": dim = 1 - if "Line" in source.__class__.__name__: - # now we need to map from graphic space to data space - # we can have more than 1 datapoint between two integer locations in the world space + # selector (min, max) data values along axis + bounds = self.selection + + if ( + "Line" in source.__class__.__name__ + or "Scatter" in source.__class__.__name__ + ): + # gets indices corresponding to n_datapoints dim + # data is [n_datapoints, xyz], so we return + # indices that can be used to slice `n_datapoints` if isinstance(source, GraphicCollection): ixs = list() for g in source.graphics: - # map for each graphic in the collection - g_ixs = np.where( - (g.data()[:, dim] >= offset_bounds[0]) - & (g.data()[:, dim] <= offset_bounds[1]) - )[0] + # indices for each graphic in the collection + data = g.data[:, dim] + g_ixs = np.where((data >= bounds[0]) & (data <= bounds[1]))[0] ixs.append(g_ixs) else: # map this only this graphic - ixs = np.where( - (source.data()[:, dim] >= offset_bounds[0]) - & (source.data()[:, dim] <= offset_bounds[1]) - )[0] + data = source.data[:, dim] + ixs = np.where((data >= bounds[0]) & (data <= bounds[1]))[0] return ixs - if ( - "Heatmap" in source.__class__.__name__ - or "Image" in source.__class__.__name__ - ): + if "Image" in source.__class__.__name__: # indices map directly to grid geometry for image data buffer - ixs = np.arange(*self.selection(), dtype=int) - return ixs + return np.arange(*bounds, dtype=int) def make_ipywidget_slider(self, kind: str = "IntRangeSlider", **kwargs): """ @@ -410,9 +402,9 @@ def make_ipywidget_slider(self, kind: str = "IntRangeSlider", **kwargs): cls = getattr(ipywidgets, kind) - value = self.selection() + value = self.selection if "Int" in kind: - value = tuple(map(int, self.selection())) + value = tuple(map(int, self.selection)) slider = cls( min=self.limits[0], @@ -457,7 +449,7 @@ def add_ipywidget_handler(self, widget, step: Union[int, float] = None): def _setup_ipywidget_slider(self, widget): # setup an ipywidget slider with bidirectional callbacks to this LinearSelector - value = self.selection() + value = self.selection if isinstance(widget, ipywidgets.IntSlider): value = tuple(map(int, value)) @@ -468,7 +460,7 @@ def _setup_ipywidget_slider(self, widget): widget.observe(self._ipywidget_callback, "value") # user changes linear selection -> widget changes - self.selection.add_event_handler(self._update_ipywidgets) + self.add_event_handler(self._update_ipywidgets, "selection") self._plot_area.renderer.add_event_handler(self._set_slider_layout, "resize") @@ -502,43 +494,39 @@ def _set_slider_layout(self, *args): widget.layout = ipywidgets.Layout(width=f"{w}px") def _move_graphic(self, delta: np.ndarray): - # add delta to current bounds to get new positions - if self.selection.axis == "x": - # min and max of current bounds, i.e. the edges - xmin, xmax = self.selection() + # add delta to current min, max to get new positions + if self.axis == "x": + # add x value + new_min, new_max = self.selection + delta[0] - # new left bound position - bound0_new = xmin + delta[0] - - # new right bound position - bound1_new = xmax + delta[0] - else: - # min and max of current bounds, i.e. the edges - ymin, ymax = self.selection() + elif self.axis == "y": + # add y value + new_min, new_max = self.selection + delta[1] - # new bottom bound position - bound0_new = ymin + delta[1] - - # new top bound position - bound1_new = ymax + delta[1] - - # move entire selector if source was fill + # move entire selector if event source was fill if self._move_info.source == self.fill: - # set the new bounds - self.selection = (bound0_new, bound1_new) + # prevent weird shrinkage of selector if one edge is already at the limit + if self.selection[0] == self.limits[0] and new_min < self.limits[0]: + # self._move_end(None) # TODO: cancel further movement to prevent weird asynchronization with pointer + return + if self.selection[1] == self.limits[1] and new_max > self.limits[1]: + # self._move_end(None) + return + + # move entire selector + self._selection.set_value(self, (new_min, new_max)) return - # if selector is not resizable do nothing + # if selector is not resizable return if not self._resizable: return - # if resizable, move edges + # if event source was an edge and selector is resizable, + # move the edge that caused the event if self._move_info.source == self.edges[0]: # change only left or bottom bound - self.selection = (bound0_new, self.selection()[1]) + self._selection.set_value(self, (new_min, self._selection.value[1])) elif self._move_info.source == self.edges[1]: # change only right or top bound - self.selection = (self.selection()[0], bound1_new) - else: - return + self._selection.set_value(self, (self.selection[0], new_max)) diff --git a/fastplotlib/graphics/selectors/_sync.py b/fastplotlib/graphics/selectors/_sync.py deleted file mode 100644 index ce903aab8..000000000 --- a/fastplotlib/graphics/selectors/_sync.py +++ /dev/null @@ -1,90 +0,0 @@ -from . import LinearSelector -from typing import * - - -class Synchronizer: - def __init__( - self, *selectors: LinearSelector, key_bind: Union[str, None] = "Shift" - ): - """ - Synchronize the movement of `Selectors`. Selectors will move in sync only when the selected `"key_bind"` is - used during the mouse movement event. Valid key binds are: ``"Control"``, ``"Shift"`` and ``"Alt"``. - If ``key_bind`` is ``None`` then the selectors will always be synchronized. - - Parameters - ---------- - selectors - selectors to synchronize - - key_bind: str, default ``"Shift"`` - one of ``"Control"``, ``"Shift"`` and ``"Alt"`` or ``None`` - """ - self._selectors = list() - self.key_bind = key_bind - - for s in selectors: - self.add(s) - - self.block_event = False - - self.enabled: bool = True - - @property - def selectors(self): - """Selectors managed by the Synchronizer""" - return self._selectors - - def add(self, selector): - """add a selector""" - selector.selection.add_event_handler(self._handle_event) - self._selectors.append(selector) - - def remove(self, selector): - """remove a selector""" - selector.selection.remove_event_handler(self._handle_event) - self._selectors.remove(selector) - - def clear(self): - for i in range(len(self.selectors)): - self.remove(self.selectors[0]) - - def _handle_event(self, ev): - if self.block_event: - # because infinite recursion - return - - if not self.enabled: - return - - self.block_event = True - - source = ev.pick_info["graphic"] - delta = ev.pick_info["delta"] - pygfx_ev = ev.pick_info["pygfx_event"] - - # only moves when modifier is used - if pygfx_ev is None: - self.block_event = False - return - - if self.key_bind is not None: - if self.key_bind not in pygfx_ev.modifiers: - self.block_event = False - return - - if delta is not None: - self._move_selectors(source, delta) - - self.block_event = False - - def _move_selectors(self, source, delta): - for s in self.selectors: - # must use == and not is to compare Graphics because they are weakref proxies! - if s == source: - # if it's the source, since it has already moved - continue - - s._move_graphic(delta) - - def __del__(self): - self.clear() diff --git a/fastplotlib/graphics/text.py b/fastplotlib/graphics/text.py index 49b4ac4be..fcee6129b 100644 --- a/fastplotlib/graphics/text.py +++ b/fastplotlib/graphics/text.py @@ -1,22 +1,35 @@ -from typing import * import pygfx import numpy as np from ._base import Graphic +from ._features import ( + TextData, + FontSize, + TextFaceColor, + TextOutlineColor, + TextOutlineThickness, +) class TextGraphic(Graphic): + _features = { + "text", + "font_size", + "face_color", + "outline_color", + "outline_thickness", + } + def __init__( self, text: str, - position: Tuple[int] = (0, 0, 0), - size: int = 14, - face_color: Union[str, np.ndarray] = "w", - outline_color: Union[str, np.ndarray] = "w", - outline_thickness=0, + font_size: float | int = 14, + face_color: str | np.ndarray | list[float] | tuple[float] = "w", + outline_color: str | np.ndarray | list[float] | tuple[float] = "w", + outline_thickness: float = 0.0, screen_space: bool = True, + offset: tuple[float] = (0, 0, 0), anchor: str = "middle-center", - *args, **kwargs, ): """ @@ -25,13 +38,10 @@ def __init__( Parameters ---------- text: str - display text - - position: int tuple, default (0, 0, 0) - int tuple indicating location of text in scene + text to display - size: int, default 10 - text size + font_size: float | int, default 10 + font size face_color: str or array, default "w" str or RGBA array to set the color of the text @@ -39,14 +49,14 @@ def __init__( outline_color: str or array, default "w" str or RGBA array to set the outline color of the text - outline_thickness: int, default 0 - text outline thickness + outline_thickness: float, default 0 + relative outline thickness, value between 0.0 - 0.5 screen_space: bool = True - whether the text is rendered in screen space, in contrast to world space + if True, text size is in screen space, if False the text size is in data space - name: str, optional - name of graphic, passed to Graphic + offset: (float, float, float), default (0, 0, 0) + places the text at this location anchor: str, default "middle-center" position of the origin of the text @@ -54,94 +64,80 @@ def __init__( * Vertical values: "top", "middle", "baseline", "bottom" * Horizontal values: "left", "center", "right" + + **kwargs + passed to Graphic + """ - super().__init__(*args, **kwargs) - self._text = text + super().__init__(**kwargs) + + self._text = TextData(text) + self._font_size = FontSize(font_size) + self._face_color = TextFaceColor(face_color) + self._outline_color = TextOutlineColor(outline_color) + self._outline_thickness = TextOutlineThickness(outline_thickness) world_object = pygfx.Text( pygfx.TextGeometry( - text=str(text), - font_size=size, + text=self.text, + font_size=self.font_size, screen_space=screen_space, anchor=anchor, ), pygfx.TextMaterial( - color=face_color, - outline_color=outline_color, - outline_thickness=outline_thickness, + color=self.face_color, + outline_color=self.outline_color, + outline_thickness=self.outline_thickness, pick_write=True, ), ) self._set_world_object(world_object) - self.world_object.position = position + self.offset = offset @property - def text(self): - """Returns the text of this graphic.""" - return self._text + def text(self) -> str: + """the text displayed""" + return self._text.value @text.setter def text(self, text: str): - """Set the text of this graphic.""" - if not isinstance(text, str): - raise ValueError("Text must be of type str.") - - self._text = text - self.world_object.geometry.set_text(self._text) + self._text.set_value(self, text) @property - def text_size(self): - """Returns the text size of this graphic.""" - return self.world_object.geometry.font_size + def font_size(self) -> float | int: + """ "text font size""" + return self._font_size.value - @text_size.setter - def text_size(self, size: Union[int, float]): - """Set the text size of this graphic.""" - if not (isinstance(size, int) or isinstance(size, float)): - raise ValueError("Text size must be of type int or float") - - self.world_object.geometry.font_size = size + @font_size.setter + def font_size(self, size: float | int): + self._font_size.set_value(self, size) @property - def face_color(self): - """Returns the face color of this graphic.""" - return self.world_object.material.color + def face_color(self) -> pygfx.Color: + """text face color""" + return self._face_color.value @face_color.setter - def face_color(self, color: Union[str, np.ndarray]): - """Set the face color of this graphic.""" - if not (isinstance(color, str) or isinstance(color, np.ndarray)): - raise ValueError("Face color must be of type str or np.ndarray") - - color = pygfx.Color(color) - - self.world_object.material.color = color + def face_color(self, color: str | np.ndarray | list[float] | tuple[float]): + self._face_color.set_value(self, color) @property - def outline_size(self): - """Returns the outline size of this graphic.""" - return self.world_object.material.outline_thickness + def outline_thickness(self) -> float: + """text outline thickness""" + return self._outline_thickness.value - @outline_size.setter - def outline_size(self, size: Union[int, float]): - """Set the outline size of this text graphic.""" - if not (isinstance(size, int) or isinstance(size, float)): - raise ValueError("Outline size must be of type int or float") - - self.world_object.material.outline_thickness = size + @outline_thickness.setter + def outline_thickness(self, thickness: float): + self._outline_thickness.set_value(self, thickness) @property - def outline_color(self): - """Returns the outline color of this graphic.""" - return self.world_object.material.outline_color + def outline_color(self) -> pygfx.Color: + """text outline color""" + return self._outline_color.value @outline_color.setter - def outline_color(self, color: Union[str, np.ndarray]): - """Set the outline color of this graphic""" - if not (isinstance(color, str) or isinstance(color, np.ndarray)): - raise ValueError("Outline color must be of type str or np.ndarray") - - self.world_object.material.outline_color = color + def outline_color(self, color: str | np.ndarray | list[float] | tuple[float]): + self._outline_color.set_value(self, color) diff --git a/fastplotlib/layouts/_figure.py b/fastplotlib/layouts/_figure.py index 1c7439613..2c157db8f 100644 --- a/fastplotlib/layouts/_figure.py +++ b/fastplotlib/layouts/_figure.py @@ -128,7 +128,6 @@ def __init__( # if controller instances have been specified for each subplot if controllers is not None: - # one controller for all subplots if isinstance(controllers, pygfx.Controller): controllers = [controllers] * len(self) diff --git a/fastplotlib/layouts/_graphic_methods_mixin.py b/fastplotlib/layouts/_graphic_methods_mixin.py index 9f82cfed5..387549ade 100644 --- a/fastplotlib/layouts/_graphic_methods_mixin.py +++ b/fastplotlib/layouts/_graphic_methods_mixin.py @@ -28,18 +28,17 @@ def _create_graphic(self, graphic_class, *args, **kwargs) -> Graphic: # only return a proxy to the real graphic return weakref.proxy(graphic) - def add_heatmap( + def add_image( self, data: Any, vmin: int = None, vmax: int = None, cmap: str = "plasma", - filter: str = "nearest", - chunk_size: int = 8192, + interpolation: str = "nearest", + cmap_interpolation: str = "linear", isolated_buffer: bool = True, - *args, **kwargs - ) -> HeatmapGraphic: + ) -> ImageGraphic: """ Create an Image Graphic @@ -48,7 +47,6 @@ def add_heatmap( ---------- data: array-like array-like, usually numpy.ndarray, must support ``memoryview()`` - Tensorflow Tensors also work **probably**, but not thoroughly tested | shape must be ``[x_dim, y_dim]`` vmin: int, optional @@ -60,107 +58,20 @@ def add_heatmap( cmap: str, optional, default "plasma" colormap to use to display the data - filter: str, optional, default "nearest" + interpolation: str, optional, default "nearest" interpolation filter, one of "nearest" or "linear" - chunk_size: int, default 8192, max 8192 - chunk size for each tile used to make up the heatmap texture - - isolated_buffer: bool, default True - If True, initialize a buffer with the same shape as the input data and then - set the data, useful if the data arrays are ready-only such as memmaps. - If False, the input array is itself used as the buffer. - - args: - additional arguments passed to Graphic - - kwargs: - additional keyword arguments passed to Graphic - - Features - -------- - - **data**: :class:`.HeatmapDataFeature` - Manages the data buffer displayed in the HeatmapGraphic - - **cmap**: :class:`.HeatmapCmapFeature` - Manages the colormap - - **present**: :class:`.PresentFeature` - Control the presence of the Graphic in the scene - - - """ - return self._create_graphic( - HeatmapGraphic, - data, - vmin, - vmax, - cmap, - filter, - chunk_size, - isolated_buffer, - *args, - **kwargs - ) - - def add_image( - self, - data: Any, - vmin: int = None, - vmax: int = None, - cmap: str = "plasma", - filter: str = "nearest", - isolated_buffer: bool = True, - *args, - **kwargs - ) -> ImageGraphic: - """ - - Create an Image Graphic - - Parameters - ---------- - data: array-like - array-like, usually numpy.ndarray, must support ``memoryview()`` - Tensorflow Tensors also work **probably**, but not thoroughly tested - | shape must be ``[x_dim, y_dim]`` or ``[x_dim, y_dim, rgb]`` - - 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, default "plasma" - colormap to use to display the image data, ignored if data is RGB - - filter: str, optional, default "nearest" - interpolation filter, one of "nearest" or "linear" + cmap_interpolation: str, optional, default "linear" + colormap interpolation method, one of "nearest" or "linear" isolated_buffer: bool, default True If True, initialize a buffer with the same shape as the input data and then set the data, useful if the data arrays are ready-only such as memmaps. If False, the input array is itself used as the buffer. - args: - additional arguments passed to Graphic - kwargs: additional keyword arguments passed to Graphic - Features - -------- - - **data**: :class:`.ImageDataFeature` - Manages the data buffer displayed in the ImageGraphic - - **cmap**: :class:`.ImageCmapFeature` - Manages the colormap - - **present**: :class:`.PresentFeature` - Control the presence of the Graphic in the scene - """ return self._create_graphic( @@ -169,24 +80,27 @@ def add_image( vmin, vmax, cmap, - filter, + interpolation, + cmap_interpolation, isolated_buffer, - *args, **kwargs ) def add_line_collection( self, - data: List[numpy.ndarray], - z_offset: Union[Iterable[float], float] = None, - thickness: Union[float, Iterable[float]] = 2.0, - colors: Union[str, Iterable[str], numpy.ndarray, Iterable[numpy.ndarray]] = "w", + data: Union[numpy.ndarray, List[numpy.ndarray]], + thickness: Union[float, Sequence[float]] = 2.0, + colors: Union[str, Sequence[str], numpy.ndarray, Sequence[numpy.ndarray]] = "w", + uniform_colors: bool = False, alpha: float = 1.0, - cmap: Union[Iterable[str], str] = None, - cmap_values: Union[numpy.ndarray, List] = None, + cmap: Union[Sequence[str], str] = None, + cmap_transform: Union[numpy.ndarray, List] = None, name: str = None, - metadata: Union[Iterable[Any], numpy.ndarray] = None, - *args, + names: list[str] = None, + metadata: Any = None, + metadatas: Union[Sequence[Any], numpy.ndarray] = None, + isolated_buffer: bool = True, + kwargs_lines: list[dict] = None, **kwargs ) -> LineCollection: """ @@ -195,13 +109,11 @@ def add_line_collection( Parameters ---------- - data: list of array-like or array - List of line data to plot, each element must be a 1D, 2D, or 3D numpy array - if elements are 2D, interpreted as [y_vals, n_lines] + data: list of array-like + List or array-like of multiple line data to plot - z_offset: Iterable of float or float, optional - | if ``float``, single offset will be used for all lines - | if ``list`` of ``float``, each value will apply to the individual lines + | if ``list`` each item in the list must be a 1D, 2D, or 3D numpy array + | if array-like, must be of shape [n_lines, n_points_line, y | xy | xyz] thickness: float or Iterable of float, default 2.0 | if ``float``, single thickness will be used for all lines @@ -223,43 +135,45 @@ def add_line_collection( .. note:: ``cmap`` overrides any arguments passed to ``colors`` - cmap_values: 1D array-like or Iterable of numerical values, optional + cmap_transform: 1D array-like of numerical values, optional if provided, these values are used to map the colors from the cmap name: str, optional - name of the line collection - - metadata: Iterable or array - metadata associated with this collection, this is for the user to manage. - ``len(metadata)`` must be same as ``len(data)`` + name of the line collection as a whole - args - passed to GraphicCollection + names: list[str], optional + names of the individual lines in the collection, ``len(names)`` must equal ``len(data)`` - kwargs - passed to GraphicCollection + metadata: Any + meatadata associated with the collection as a whole - Features - -------- + metadatas: Iterable or array + metadata for each individual line associated with this collection, this is for the user to manage. + ``len(metadata)`` must be same as ``len(data)`` - Collections support the same features as the underlying graphic. You just have to slice the selection. + kwargs_lines: list[dict], optional + list of kwargs passed to the individual lines, ``len(kwargs_lines)`` must equal ``len(data)`` - See :class:`LineGraphic` details on the features. + kwargs_collection + kwargs for the collection, passed to GraphicCollection """ return self._create_graphic( LineCollection, data, - z_offset, thickness, colors, + uniform_colors, alpha, cmap, - cmap_values, + cmap_transform, name, + names, metadata, - *args, + metadatas, + isolated_buffer, + kwargs_lines, **kwargs ) @@ -268,12 +182,11 @@ def add_line( data: Any, thickness: float = 2.0, colors: Union[str, numpy.ndarray, Iterable] = "w", + uniform_color: bool = False, alpha: float = 1.0, cmap: str = None, - cmap_values: Union[numpy.ndarray, Iterable] = None, - z_position: float = None, - collection_index: int = None, - *args, + cmap_transform: Union[numpy.ndarray, Iterable] = None, + isolated_buffer: bool = True, **kwargs ) -> LineGraphic: """ @@ -292,43 +205,23 @@ def add_line( 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, this - overrides any argument passed to "colors" - - cmap_values: 1D array-like or Iterable of numerical values, optional - if provided, these values are used to map the colors from the cmap + uniform_color: bool, default ``False`` + if True, uses a uniform buffer for the line color, + basically saves GPU VRAM when the entire line has a single color alpha: float, optional, default 1.0 alpha value for the colors - z_position: float, optional - z-axis position for placing the graphic + cmap: str, optional + apply a colormap to the line instead of assigning colors manually, this + overrides any argument passed to "colors" - args - passed to Graphic + cmap_transform: 1D array-like of numerical values, optional + if provided, these values are used to map the colors from the cmap - kwargs + **kwargs passed to Graphic - Features - -------- - - **data**: :class:`.ImageDataFeature` - Manages the line [x, y, z] positions data buffer, allows regular and fancy indexing. - - **colors**: :class:`.ColorFeature` - Manages the color buffer, allows regular and fancy indexing. - - **cmap**: :class:`.CmapFeature` - Manages the cmap, wraps :class:`.ColorFeature` to add additional functionality relevant to cmaps. - - **thickness**: :class:`.ThicknessFeature` - Manages the thickness feature of the lines. - - **present**: :class:`.PresentFeature` - Control the presence of the Graphic in the scene, set to ``True`` or ``False`` - """ return self._create_graphic( @@ -336,29 +229,30 @@ def add_line( data, thickness, colors, + uniform_color, alpha, cmap, - cmap_values, - z_position, - collection_index, - *args, + cmap_transform, + isolated_buffer, **kwargs ) def add_line_stack( self, data: List[numpy.ndarray], - z_offset: Union[Iterable[float], float] = None, thickness: Union[float, Iterable[float]] = 2.0, colors: Union[str, Iterable[str], numpy.ndarray, Iterable[numpy.ndarray]] = "w", alpha: float = 1.0, cmap: Union[Iterable[str], str] = None, - cmap_values: Union[numpy.ndarray, List] = None, + cmap_transform: Union[numpy.ndarray, List] = None, name: str = None, - metadata: Union[Iterable[Any], numpy.ndarray] = None, + names: list[str] = None, + metadata: Any = None, + metadatas: Union[Sequence[Any], numpy.ndarray] = None, + isolated_buffer: bool = True, separation: float = 10.0, separation_axis: str = "y", - *args, + kwargs_lines: list[dict] = None, **kwargs ) -> LineStack: """ @@ -367,13 +261,11 @@ def add_line_stack( Parameters ---------- - data: list of array-like or array - List of line data to plot, each element must be a 1D, 2D, or 3D numpy array - if elements are 2D, interpreted as [y_vals, n_lines] + data: list of array-like + List or array-like of multiple line data to plot - z_offset: Iterable of float or float, optional - | if ``float``, single offset will be used for all lines - | if ``list`` of ``float``, each value will apply to the individual lines + | if ``list`` each item in the list must be a 1D, 2D, or 3D numpy array + | if array-like, must be of shape [n_lines, n_points_line, y | xy | xyz] thickness: float or Iterable of float, default 2.0 | if ``float``, single thickness will be used for all lines @@ -385,6 +277,9 @@ def add_line_stack( | if ``list`` of ``str``, represents color for each individual line, example ["w", "b", "r",...] | if ``RGBA array`` of shape [data_size, 4], represents a single RGBA array for each line + alpha: float, optional + alpha value for colors, if colors is a ``str`` + cmap: Iterable of str or str, optional | if ``str``, single cmap will be used for all lines | if ``list`` of ``str``, each cmap will apply to the individual lines @@ -392,11 +287,20 @@ def add_line_stack( .. note:: ``cmap`` overrides any arguments passed to ``colors`` - cmap_values: 1D array-like or Iterable of numerical values, optional + cmap_transform: 1D array-like of numerical values, optional if provided, these values are used to map the colors from the cmap - metadata: Iterable or array - metadata associated with this collection, this is for the user to manage. + name: str, optional + name of the line collection as a whole + + names: list[str], optional + names of the individual lines in the collection, ``len(names)`` must equal ``len(data)`` + + metadata: Any + metadata associated with the collection as a whole + + metadatas: Iterable or array + metadata for each individual line associated with this collection, this is for the user to manage. ``len(metadata)`` must be same as ``len(data)`` separation: float, default 10 @@ -405,48 +309,45 @@ def add_line_stack( separation_axis: str, default "y" axis in which the line graphics in the stack should be separated - name: str, optional - name of the line stack - - kwargs - passed to LineCollection - Features - -------- + kwargs_lines: list[dict], optional + list of kwargs passed to the individual lines, ``len(kwargs_lines)`` must equal ``len(data)`` - Collections support the same features as the underlying graphic. You just have to slice the selection. - - See :class:`LineGraphic` details on the features. + kwargs_collection + kwargs for the collection, passed to GraphicCollection """ return self._create_graphic( LineStack, data, - z_offset, thickness, colors, alpha, cmap, - cmap_values, + cmap_transform, name, + names, metadata, + metadatas, + isolated_buffer, separation, separation_axis, - *args, + kwargs_lines, **kwargs ) def add_scatter( self, - data: numpy.ndarray, - sizes: Union[float, numpy.ndarray, Iterable[float]] = 1, - colors: Union[str, numpy.ndarray, Iterable[str]] = "w", + data: Any, + colors: str | numpy.ndarray | tuple[float] | list[float] | list[str] = "w", + uniform_color: bool = False, alpha: float = 1.0, cmap: str = None, - cmap_values: Union[numpy.ndarray, List] = None, - z_position: float = 0.0, - *args, + cmap_transform: numpy.ndarray = None, + isolated_buffer: bool = True, + sizes: Union[float, numpy.ndarray, Iterable[float]] = 1, + uniform_size: bool = False, **kwargs ) -> ScatterGraphic: """ @@ -458,73 +359,64 @@ def add_scatter( data: array-like Scatter data to plot, 2D must be of shape [n_points, 2], 3D must be of shape [n_points, 3] - sizes: float or iterable of float, optional, default 1.0 - size of the scatter points - colors: str, array, or iterable, default "w" specify colors as a single human readable string, a single RGBA array, or an iterable of strings or RGBA arrays + uniform_color: bool, default False + if True, uses a uniform buffer for the scatter point colors, + basically saves GPU VRAM when the entire line has a single color + + alpha: float, optional, default 1.0 + alpha value for the colors + cmap: str, optional apply a colormap to the scatter instead of assigning colors manually, this overrides any argument passed to "colors" - cmap_values: 1D array-like or list of numerical values, optional + cmap_transform: 1D array-like or list of numerical values, optional if provided, these values are used to map the colors from the cmap - alpha: float, optional, default 1.0 - alpha value for the colors + isolated_buffer: bool, default True + whether the buffers should be isolated from the user input array. + Generally always ``True``, ``False`` is for rare advanced use. - z_position: float, optional - z-axis position for placing the graphic + sizes: float or iterable of float, optional, default 1.0 + size of the scatter points - args - passed to Graphic + uniform_size: bool, default False + if True, uses a uniform buffer for the scatter point sizes, + basically saves GPU VRAM when all scatter points are the same size kwargs passed to Graphic - Features - -------- - - **data**: :class:`.ImageDataFeature` - Manages the line [x, y, z] positions data buffer, allows regular and fancy indexing. - - **colors**: :class:`.ColorFeature` - Manages the color buffer, allows regular and fancy indexing. - - **cmap**: :class:`.CmapFeature` - Manages the cmap, wraps :class:`.ColorFeature` to add additional functionality relevant to cmaps. - - **present**: :class:`.PresentFeature` - Control the presence of the Graphic in the scene, set to ``True`` or ``False`` - """ return self._create_graphic( ScatterGraphic, data, - sizes, colors, + uniform_color, alpha, cmap, - cmap_values, - z_position, - *args, + cmap_transform, + isolated_buffer, + sizes, + uniform_size, **kwargs ) def add_text( self, text: str, - position: Tuple[int] = (0, 0, 0), - size: int = 14, - face_color: Union[str, numpy.ndarray] = "w", - outline_color: Union[str, numpy.ndarray] = "w", - outline_thickness=0, + font_size: float | int = 14, + face_color: str | numpy.ndarray | list[float] | tuple[float] = "w", + outline_color: str | numpy.ndarray | list[float] | tuple[float] = "w", + outline_thickness: float = 0.0, screen_space: bool = True, + offset: tuple[float] = (0, 0, 0), anchor: str = "middle-center", - *args, **kwargs ) -> TextGraphic: """ @@ -534,13 +426,10 @@ def add_text( Parameters ---------- text: str - display text + text to display - position: int tuple, default (0, 0, 0) - int tuple indicating location of text in scene - - size: int, default 10 - text size + font_size: float | int, default 10 + font size face_color: str or array, default "w" str or RGBA array to set the color of the text @@ -548,14 +437,14 @@ def add_text( outline_color: str or array, default "w" str or RGBA array to set the outline color of the text - outline_thickness: int, default 0 - text outline thickness + outline_thickness: float, default 0 + relative outline thickness, value between 0.0 - 0.5 screen_space: bool = True - whether the text is rendered in screen space, in contrast to world space + if True, text size is in screen space, if False the text size is in data space - name: str, optional - name of graphic, passed to Graphic + offset: (float, float, float), default (0, 0, 0) + places the text at this location anchor: str, default "middle-center" position of the origin of the text @@ -564,17 +453,20 @@ def add_text( * Vertical values: "top", "middle", "baseline", "bottom" * Horizontal values: "left", "center", "right" + **kwargs + passed to Graphic + + """ return self._create_graphic( TextGraphic, text, - position, - size, + font_size, face_color, outline_color, outline_thickness, screen_space, + offset, anchor, - *args, **kwargs ) diff --git a/fastplotlib/layouts/_plot_area.py b/fastplotlib/layouts/_plot_area.py index 6ff07a748..d8e0adebc 100644 --- a/fastplotlib/layouts/_plot_area.py +++ b/fastplotlib/layouts/_plot_area.py @@ -12,6 +12,7 @@ from ._utils import create_controller from ..graphics._base import Graphic +from ..graphics._collection_base import GraphicCollection from ..graphics.selectors._base_selector import BaseSelector from ..legends import Legend @@ -469,14 +470,14 @@ def add_graphic(self, graphic: Graphic, center: bool = True): if self.camera.fov == 0: # for orthographic positions stack objects along the z-axis # for perspective projections we assume the user wants full 3D control - graphic.position_z = len(self) + graphic.offset = (*graphic.offset[:-1], len(self)) def insert_graphic( self, graphic: Graphic, center: bool = True, index: int = 0, - z_position: int = None, + auto_offset: int = None, ): """ Insert graphic into scene at given position ``index`` in stored graphics. @@ -493,8 +494,8 @@ def insert_graphic( index: int, default 0 Index to insert graphic. - z_position: int, default None - z axis position to place Graphic. If ``None``, uses value of `index` argument + auto_offset: bool, default True + If True and using an orthographic projection, sets z-axis offset of graphic to `index` """ if index > len(self._graphics): @@ -511,10 +512,8 @@ def insert_graphic( if self.camera.fov == 0: # for orthographic positions stack objects along the z-axis # for perspective projections we assume the user wants full 3D control - if z_position is None: - graphic.position_z = index - else: - graphic.position_z = z_position + if auto_offset: + graphic.offset = (*graphic.offset[:-1], index) def _add_or_insert_graphic( self, diff --git a/fastplotlib/layouts/_subplot.py b/fastplotlib/layouts/_subplot.py index a541c9d78..059307e6b 100644 --- a/fastplotlib/layouts/_subplot.py +++ b/fastplotlib/layouts/_subplot.py @@ -149,7 +149,7 @@ def set_title(self, text: str): if self._title_graphic is not None: self._title_graphic.text = text else: - tg = TextGraphic(text=text, size=18) + tg = TextGraphic(text=text, font_size=18) self._title_graphic = tg self.docks["top"].size = 35 diff --git a/fastplotlib/utils/functions.py b/fastplotlib/utils/functions.py index 561863b0c..73752ba5e 100644 --- a/fastplotlib/utils/functions.py +++ b/fastplotlib/utils/functions.py @@ -91,8 +91,8 @@ def make_colors(n_colors: int, cmap: str, alpha: float = 1.0) -> np.ndarray: max_colors = cmap.shape[0] if n_colors > cmap.shape[0]: raise ValueError( - f"You have requested <{n_colors}> but only <{max_colors} existing for the " - f"chosen cmap: <{cmap}>" + f"You have requested <{n_colors}> colors but only <{max_colors}> exist for the " + f"chosen cmap: <{name}>" ) return cmap[:n_colors] @@ -239,7 +239,7 @@ def normalize_min_max(a): def parse_cmap_values( n_colors: int, cmap_name: str, - cmap_values: np.ndarray | list[int | float] = None, + transform: np.ndarray | list[int | float] = None, ) -> np.ndarray: """ @@ -251,28 +251,25 @@ def parse_cmap_values( cmap_name: str colormap name - cmap_values: np.ndarray | List[int | float], optional - cmap values + transform: np.ndarray | List[int | float], optional + cmap transform Returns ------- """ - if cmap_values is None: - # use the cmap values linearly just along the collection indices - # for example, if len(data) = 10 and the cmap is "jet", then it will - # linearly go from blue to red from data[0] to data[-1] + if transform is None: colors = make_colors(n_colors, cmap_name) return colors else: - if not isinstance(cmap_values, np.ndarray): - cmap_values = np.array(cmap_values) + if not isinstance(transform, np.ndarray): + transform = np.array(transform) - # use the values within cmap_values to set the color of the corresponding data - # each individual data[i] has its color based on the "relative cmap_value intensity" - if len(cmap_values) != n_colors: + # use the of the cmap_transform to set the color of the corresponding data + # each individual data[i] has its color based on the transform values + if len(transform) != n_colors: raise ValueError( - f"len(cmap_values) != len(data): {len(cmap_values)} != {n_colors}" + f"len(cmap_values) != len(data): {len(transform)} != {n_colors}" ) colormap = get_cmap(cmap_name) @@ -280,23 +277,23 @@ def parse_cmap_values( n_colors = colormap.shape[0] - 1 if cmap_name in QUALITATIVE_CMAPS: - # check that cmap_values are and within the number of colors `n_colors` + # check that cmap_transform are and within the number of colors `n_colors` # do not scale, use directly - if not np.issubdtype(cmap_values.dtype, np.integer): + if not np.issubdtype(transform.dtype, np.integer): raise TypeError( - f" cmap_values should be used with qualitative colormaps, the dtype you " - f"have passed is {cmap_values.dtype}" + f" `cmap_transform` values should be used with qualitative colormaps, " + f"the dtype you have passed is {transform.dtype}" ) - if max(cmap_values) > n_colors: + if max(transform) > n_colors: raise IndexError( f"You have chosen the qualitative colormap <'{cmap_name}'> which only has " - f"<{n_colors}> colors, which is lower than the max value of your `cmap_values`." + f"<{n_colors}> colors, which is lower than the max value of your `cmap_transform`." f"Choose a cmap with more colors, or a non-quantitative colormap." ) - norm_cmap_values = cmap_values + norm_cmap_values = transform else: # scale between 0 - n_colors so we can just index the colormap as a LUT - norm_cmap_values = (normalize_min_max(cmap_values) * n_colors).astype(int) + norm_cmap_values = (normalize_min_max(transform) * n_colors).astype(int) # use colormap as LUT to map the cmap_values to the colormap index colors = np.vstack([colormap[val] for val in norm_cmap_values]) diff --git a/fastplotlib/utils/gui.py b/fastplotlib/utils/gui.py index b59c7799b..1941674ee 100644 --- a/fastplotlib/utils/gui.py +++ b/fastplotlib/utils/gui.py @@ -44,7 +44,6 @@ def _notebook_print_banner(): - from ipywidgets import Image from IPython.display import display diff --git a/fastplotlib/widgets/histogram_lut.py b/fastplotlib/widgets/histogram_lut.py index 971bc1a28..a3edffcbd 100644 --- a/fastplotlib/widgets/histogram_lut.py +++ b/fastplotlib/widgets/histogram_lut.py @@ -52,29 +52,30 @@ def __init__( origin = (hist_scaled.max() / 2, 0) self._linear_region_selector = LinearRegionSelector( - bounds=bounds, + selection=bounds, limits=limits, size=size, - origin=origin, + center=origin[0], axis="y", edge_thickness=8, + parent=self._histogram_line, ) # there will be a small difference with the histogram edges so this makes them both line up exactly self._linear_region_selector.selection = ( - image_graphic.cmap.vmin, - image_graphic.cmap.vmax, + self._image_graphic.vmin, + self._image_graphic.vmax, ) - self._vmin = self.image_graphic.cmap.vmin - self._vmax = self.image_graphic.cmap.vmax + self._vmin = self.image_graphic.vmin + self._vmax = self.image_graphic.vmax vmin_str, vmax_str = self._get_vmin_vmax_str() self._text_vmin = TextGraphic( text=vmin_str, - size=16, - position=(0, 0), + font_size=16, + offset=(0, 0, 0), anchor="top-left", outline_color="black", outline_thickness=1, @@ -84,8 +85,8 @@ def __init__( self._text_vmax = TextGraphic( text=vmax_str, - size=16, - position=(0, 0), + font_size=16, + offset=(0, 0, 0), anchor="bottom-left", outline_color="black", outline_thickness=1, @@ -105,17 +106,15 @@ def __init__( self.world_object.local.scale_x *= -1 - self._text_vmin.position_x = -120 - self._text_vmin.position_y = self._linear_region_selector.selection()[0] + self._text_vmin.offset = (-120, self._linear_region_selector.selection[0], 0) - self._text_vmax.position_x = -120 - self._text_vmax.position_y = self._linear_region_selector.selection()[1] + self._text_vmax.offset = (-120, self._linear_region_selector.selection[1], 0) - self._linear_region_selector.selection.add_event_handler( - self._linear_region_handler + self._linear_region_selector.add_event_handler( + self._linear_region_handler, "selection" ) - self.image_graphic.cmap.add_event_handler(self._image_cmap_handler) + self.image_graphic.add_event_handler(self._image_cmap_handler, "vmin", "vmax") def _get_vmin_vmax_str(self) -> tuple[str, str]: if self.vmin < 0.001 or self.vmin > 99_999: @@ -198,16 +197,13 @@ def _calculate_histogram(self, data): def _linear_region_handler(self, ev): # must use world coordinate values directly from selection() # otherwise the linear region bounds jump to the closest bin edges - vmin, vmax = self._linear_region_selector.selection() + selected_ixs = self._linear_region_selector.selection + vmin, vmax = selected_ixs[0], selected_ixs[1] vmin, vmax = vmin / self._scale_factor, vmax / self._scale_factor self.vmin, self.vmax = vmin, vmax def _image_cmap_handler(self, ev): - self.vmin, self.vmax = ev.pick_info["vmin"], ev.pick_info["vmax"] - - def _block_events(self, b: bool): - self.image_graphic.cmap.block_events(b) - self._linear_region_selector.selection.block_events(b) + setattr(self, ev.type, ev.info["value"]) @property def vmin(self) -> float: @@ -215,22 +211,24 @@ def vmin(self) -> float: @vmin.setter def vmin(self, value: float): - self._block_events(True) + self.image_graphic.block_events = True + self._linear_region_selector.block_events = True # must use world coordinate values directly from selection() # otherwise the linear region bounds jump to the closest bin edges self._linear_region_selector.selection = ( value * self._scale_factor, - self._linear_region_selector.selection()[1], + self._linear_region_selector.selection[1], ) - self.image_graphic.cmap.vmin = value + self.image_graphic.vmin = value - self._block_events(False) + self.image_graphic.block_events = False + self._linear_region_selector.block_events = False self._vmin = value vmin_str, vmax_str = self._get_vmin_vmax_str() - self._text_vmin.position_y = self._linear_region_selector.selection()[0] + self._text_vmin.offset = (-120, self._linear_region_selector.selection[0], 0) self._text_vmin.text = vmin_str @property @@ -239,22 +237,25 @@ def vmax(self) -> float: @vmax.setter def vmax(self, value: float): - self._block_events(True) + self.image_graphic.block_events = True + self._linear_region_selector.block_events = True # must use world coordinate values directly from selection() # otherwise the linear region bounds jump to the closest bin edges self._linear_region_selector.selection = ( - self._linear_region_selector.selection()[0], + self._linear_region_selector.selection[0], value * self._scale_factor, ) - self.image_graphic.cmap.vmax = value - self._block_events(False) + self.image_graphic.vmax = value + + self.image_graphic.block_events = False + self._linear_region_selector.block_events = False self._vmax = value vmin_str, vmax_str = self._get_vmin_vmax_str() - self._text_vmax.position_y = self._linear_region_selector.selection()[1] + self._text_vmax.offset = (-120, self._linear_region_selector.selection[1], 0) self._text_vmax.text = vmax_str def set_data(self, data, reset_vmin_vmax: bool = True): @@ -262,12 +263,12 @@ def set_data(self, data, reset_vmin_vmax: bool = True): line_data = np.column_stack([hist_scaled, edges_flanked]) - self._histogram_line.data = line_data + # set x and y vals + self._histogram_line.data[:, :2] = line_data bounds = (edges[0], edges[-1]) limits = (edges_flanked[0], edges_flanked[-11]) origin = (hist_scaled.max() / 2, 0) - # self.linear_region.fill.world.position = (*origin, -2) if reset_vmin_vmax: # reset according to the new data @@ -275,9 +276,11 @@ def set_data(self, data, reset_vmin_vmax: bool = True): self._linear_region_selector.selection = bounds else: # don't change the current selection - self._block_events(True) + self.image_graphic.block_events = True + self._linear_region_selector.block_events = True self._linear_region_selector.limits = limits - self._block_events(False) + self.image_graphic.block_events = False + self._linear_region_selector.block_events = False self._data = weakref.proxy(data) @@ -297,14 +300,14 @@ def image_graphic(self, graphic): if self._image_graphic is not None: # cleanup events from current image graphic - self._image_graphic.cmap.remove_event_handler(self._image_cmap_handler) + self._image_graphic.remove_event_handler(self._image_cmap_handler) self._image_graphic = graphic - self.image_graphic.cmap.add_event_handler(self._image_cmap_handler) + self.image_graphic.add_event_handler(self._image_cmap_handler) def disconnect_image_graphic(self): - self._image_graphic.cmap.remove_event_handler(self._image_cmap_handler) + self._image_graphic.remove_event_handler(self._image_cmap_handler) del self._image_graphic # self._image_graphic = None diff --git a/fastplotlib/widgets/image.py b/fastplotlib/widgets/image.py index 2a4dc31b4..df9b46b55 100644 --- a/fastplotlib/widgets/image.py +++ b/fastplotlib/widgets/image.py @@ -366,7 +366,6 @@ def __init__( if isinstance(data, list): # verify that it's a list of np.ndarray if all([_is_arraylike(d) for d in data]): - # Grid computations if figure_shape is None: figure_shape = calculate_figure_shape(len(data)) @@ -755,7 +754,7 @@ def reset_vmin_vmax(self): Reset the vmin and vmax w.r.t. the full data """ for ig in self.managed_graphics: - ig.cmap.reset_vmin_vmax() + ig.reset_vmin_vmax() def reset_vmin_vmax_frame(self): """ @@ -773,7 +772,7 @@ def reset_vmin_vmax_frame(self): hlut = subplot.docks["right"]["histogram_lut"] # set the data using the current image graphic data - hlut.set_data(subplot["image_widget_managed"].data()) + hlut.set_data(subplot["image_widget_managed"].data.value) def set_data( self, diff --git a/scripts/generate_add_graphic_methods.py b/scripts/generate_add_graphic_methods.py index 2a480d884..3f45d9007 100644 --- a/scripts/generate_add_graphic_methods.py +++ b/scripts/generate_add_graphic_methods.py @@ -69,7 +69,7 @@ def generate_add_graphics_methods(): f.write(f" {class_name.__init__.__doc__}\n") f.write(' """\n') f.write( - f" return self._create_graphic({class_name.__name__}, {s}*args, **kwargs)\n\n" + f" return self._create_graphic({class_name.__name__}, {s} **kwargs)\n\n" ) f.close() diff --git a/setup.py b/setup.py index b50a6a9bf..3ba77201d 100644 --- a/setup.py +++ b/setup.py @@ -18,7 +18,6 @@ "ipywidgets>=8.0.0,<9", "sphinx-copybutton", "sphinx-design", - "nbsphinx", "pandoc", "jupyterlab", "sidecar", diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/events.py b/tests/events.py new file mode 100644 index 000000000..ea160dec3 --- /dev/null +++ b/tests/events.py @@ -0,0 +1,91 @@ +from functools import partial +import pytest +import numpy as np +from numpy import testing as npt +import pygfx + +import fastplotlib as fpl +from fastplotlib.graphics._features import FeatureEvent + + +def make_positions_data() -> np.ndarray: + xs = np.linspace(0, 10 * np.pi, 10) + ys = np.sin(xs) + return np.column_stack([xs, ys]) + + +def make_line_graphic() -> fpl.LineGraphic: + return fpl.LineGraphic(make_positions_data()) + + +def make_scatter_graphic() -> fpl.ScatterGraphic: + return fpl.ScatterGraphic(make_positions_data()) + + +event_instance: FeatureEvent = None + + +def event_handler(event): + global event_instance + event_instance = event + + +decorated_event_instance: FeatureEvent = None + + +@pytest.mark.parametrize("graphic", [make_line_graphic(), make_scatter_graphic()]) +def test_positions_data_event(graphic: fpl.LineGraphic | fpl.ScatterGraphic): + global decorated_event_instance + global event_instance + + value = np.cos(np.linspace(0, 10 * np.pi, 10))[3:8] + + info = {"key": (slice(3, 8, None), 1), "value": value} + + expected = FeatureEvent(type="data", info=info) + + def validate(graphic, handler, expected_feature_event, event_to_test): + assert expected_feature_event.type == event_to_test.type + assert expected_feature_event.info["key"] == event_to_test.info["key"] + + npt.assert_almost_equal( + expected_feature_event.info["value"], event_to_test.info["value"] + ) + + # should only have one event handler + assert graphic._event_handlers["data"] == {handler} + + # make sure wrappers are correct + wrapper_map = tuple(graphic._event_handler_wrappers["data"])[0] + assert wrapper_map[0] is handler + assert isinstance(wrapper_map[1], partial) + assert wrapper_map[1].func == graphic._handle_event + assert wrapper_map[1].args[0] is handler + + # test remove handler + graphic.remove_event_handler(handler, "data") + assert len(graphic._event_handlers["click"]) == 0 + assert len(graphic._event_handler_wrappers["click"]) == 0 + assert len(graphic.world_object._event_handlers["click"]) == 0 + + # reset data + graphic.data[:, :-1] = make_positions_data() + event_to_test = None + + # test decorated function + @graphic.add_event_handler("data") + def decorated_handler(event): + global decorated_event_instance + decorated_event_instance = event + + # test decorated + graphic.data[3:8, 1] = value + validate(graphic, decorated_handler, expected, decorated_event_instance) + + # test regular + graphic.add_event_handler(event_handler, "data") + graphic.data[3:8, 1] = value + + validate(graphic, event_handler, expected, event_instance) + + event_instance = None diff --git a/tests/test_colors_buffer_manager.py b/tests/test_colors_buffer_manager.py new file mode 100644 index 000000000..252c6e5c3 --- /dev/null +++ b/tests/test_colors_buffer_manager.py @@ -0,0 +1,250 @@ +import numpy as np +from numpy import testing as npt +import pytest + +import pygfx + +import fastplotlib as fpl +from fastplotlib.graphics._features import VertexColors, FeatureEvent +from .utils import ( + generate_slice_indices, + assert_pending_uploads, + generate_color_inputs, + generate_positions_spiral_data, +) + + +def make_colors_buffer() -> VertexColors: + colors = VertexColors(colors="w", n_colors=10) + return colors + + +EVENT_RETURN_VALUE: FeatureEvent = None + + +def event_handler(ev): + global EVENT_RETURN_VALUE + EVENT_RETURN_VALUE = ev + + +@pytest.mark.parametrize( + "color_input", + [ + *generate_color_inputs("r"), + *generate_color_inputs("g"), + *generate_color_inputs("b"), + ], +) +def test_create_buffer(color_input): + colors = VertexColors(colors=color_input, n_colors=10) + truth = np.repeat([pygfx.Color(color_input)], 10, axis=0) + npt.assert_almost_equal(colors[:], truth) + + +@pytest.mark.parametrize("test_graphic", [False, "line", "scatter"]) +def test_int(test_graphic): + # setting single points + if test_graphic: + fig = fpl.Figure() + + data = generate_positions_spiral_data("xyz") + if test_graphic == "line": + graphic = fig[0, 0].add_line(data=data) + + elif test_graphic == "scatter": + graphic = fig[0, 0].add_scatter(data=data) + + colors = graphic.colors + global EVENT_RETURN_VALUE + graphic.add_event_handler(event_handler, "colors") + else: + colors = make_colors_buffer() + + # TODO: placeholder until I make a testing figure where we draw frames only on call + colors.buffer._gfx_pending_uploads.clear() + + colors[3] = "r" + npt.assert_almost_equal(colors[3], [1.0, 0.0, 0.0, 1.0]) + assert colors.buffer._gfx_pending_uploads[-1] == (3, 1) + + if test_graphic: + # test event + assert isinstance(EVENT_RETURN_VALUE, FeatureEvent) + assert EVENT_RETURN_VALUE.graphic == graphic + assert EVENT_RETURN_VALUE.target is graphic.world_object + assert EVENT_RETURN_VALUE.info["key"] == 3 + npt.assert_almost_equal( + EVENT_RETURN_VALUE.info["value"], np.array([[1, 0, 0, 1]]) + ) + assert EVENT_RETURN_VALUE.info["user_value"] == "r" + + colors[6] = [0.0, 1.0, 1.0, 1.0] + npt.assert_almost_equal(colors[6], [0.0, 1.0, 1.0, 1.0]) + + colors[7] = (0.0, 1.0, 1.0, 1.0) + npt.assert_almost_equal(colors[6], [0.0, 1.0, 1.0, 1.0]) + + colors[8] = np.array([1, 0, 1, 1]) + npt.assert_almost_equal(colors[8], [1.0, 0.0, 1.0, 1.0]) + + colors[2] = [1, 0, 1, 0.5] + npt.assert_almost_equal(colors[2], [1.0, 0.0, 1.0, 0.5]) + + +@pytest.mark.parametrize("test_graphic", [False, "line", "scatter"]) +@pytest.mark.parametrize( + "slice_method", [generate_slice_indices(i) for i in range(0, 16)] +) +def test_tuple(test_graphic, slice_method): + # setting entire array manually + if test_graphic: + fig = fpl.Figure() + + data = generate_positions_spiral_data("xyz") + if test_graphic == "line": + graphic = fig[0, 0].add_line(data=data) + + elif test_graphic == "scatter": + graphic = fig[0, 0].add_scatter(data=data) + + colors = graphic.colors + global EVENT_RETURN_VALUE + graphic.add_event_handler(event_handler, "colors") + else: + colors = make_colors_buffer() + + s = slice_method["slice"] + indices = slice_method["indices"] + others = slice_method["others"] + + # set all RGBA vals + colors[s, :] = 0.5 + truth = np.repeat([[0.5, 0.5, 0.5, 0.5]], repeats=len(indices), axis=0) + npt.assert_almost_equal(colors[indices], truth) + + if test_graphic: + # test event + assert isinstance(EVENT_RETURN_VALUE, FeatureEvent) + assert EVENT_RETURN_VALUE.graphic == graphic + assert EVENT_RETURN_VALUE.target is graphic.world_object + assert EVENT_RETURN_VALUE.info["key"] == (s, slice(None)) + npt.assert_almost_equal(EVENT_RETURN_VALUE.info["value"], truth) + assert EVENT_RETURN_VALUE.info["user_value"] == 0.5 + + # check others are not modified + others_truth = np.repeat([[1.0, 1.0, 1.0, 1.0]], repeats=len(others), axis=0) + npt.assert_almost_equal(colors[others], others_truth) + + # reset + if test_graphic: + # test setter + graphic.colors = "w" + else: + colors[:] = [1, 1, 1, 1] + truth = np.repeat([[1.0, 1.0, 1.0, 1.0]], 10, axis=0) + npt.assert_almost_equal(colors[:], truth) + + if test_graphic: + # test event + assert isinstance(EVENT_RETURN_VALUE, FeatureEvent) + assert EVENT_RETURN_VALUE.graphic == graphic + assert EVENT_RETURN_VALUE.target is graphic.world_object + assert EVENT_RETURN_VALUE.info["key"] == slice(None) + npt.assert_almost_equal(EVENT_RETURN_VALUE.info["value"], truth) + assert EVENT_RETURN_VALUE.info["user_value"] == "w" + + # set just R values + colors[s, 0] = 0.5 + truth = np.repeat([[0.5, 1.0, 1.0, 1.0]], repeats=len(indices), axis=0) + # check others not modified + npt.assert_almost_equal(colors[indices], truth) + npt.assert_almost_equal(colors[others], others_truth) + + # reset + colors[:] = (1, 1, 1, 1) + npt.assert_almost_equal(colors[:], np.repeat([[1.0, 1.0, 1.0, 1.0]], 10, axis=0)) + + # set green and blue + colors[s, 1:-1] = 0.7 + truth = np.repeat([[1.0, 0.7, 0.7, 1.0]], repeats=len(indices), axis=0) + npt.assert_almost_equal(colors[indices], truth) + npt.assert_almost_equal(colors[others], others_truth) + + # reset + colors[:] = (1, 1, 1, 1) + npt.assert_almost_equal(colors[:], np.repeat([[1.0, 1.0, 1.0, 1.0]], 10, axis=0)) + + # set only alpha + colors[s, -1] = 0.2 + truth = np.repeat([[1.0, 1.0, 1.0, 0.2]], repeats=len(indices), axis=0) + npt.assert_almost_equal(colors[indices], truth) + npt.assert_almost_equal(colors[others], others_truth) + + +@pytest.mark.parametrize("color_input", generate_color_inputs("red")) +# skip testing with int since that results in shape [1, 4] with np.repeat, int tested in independent unit test +@pytest.mark.parametrize( + "slice_method", [generate_slice_indices(i) for i in range(1, 16)] +) +@pytest.mark.parametrize("test_graphic", [False, "line", "scatter"]) +def test_slice(color_input, slice_method: dict, test_graphic: bool): + # slicing only first dim + if test_graphic: + fig = fpl.Figure() + + data = generate_positions_spiral_data("xyz") + if test_graphic == "line": + graphic = fig[0, 0].add_line(data=data) + + elif test_graphic == "scatter": + graphic = fig[0, 0].add_scatter(data=data) + + colors = graphic.colors + + global EVENT_RETURN_VALUE + graphic.add_event_handler(event_handler, "colors") + else: + colors = make_colors_buffer() + + # TODO: placeholder until I make a testing figure where we draw frames only on call + colors.buffer._gfx_pending_uploads.clear() + + s = slice_method["slice"] + indices = slice_method["indices"] + offset = slice_method["offset"] + size = slice_method["size"] + others = slice_method["others"] + + colors[s] = color_input + truth = np.repeat([pygfx.Color(color_input)], repeats=len(indices), axis=0) + # check that correct indices are modified + npt.assert_almost_equal(colors[s], truth) + npt.assert_almost_equal(colors[indices], truth) + + # check event + if test_graphic: + global EVENT_RETURN_VALUE + + assert isinstance(EVENT_RETURN_VALUE, FeatureEvent) + assert EVENT_RETURN_VALUE.graphic == graphic + assert EVENT_RETURN_VALUE.target is graphic.world_object + if isinstance(s, slice): + assert EVENT_RETURN_VALUE.info["key"] == s + else: + npt.assert_almost_equal(EVENT_RETURN_VALUE.info["key"], s) + npt.assert_almost_equal(EVENT_RETURN_VALUE.info["value"], truth) + if isinstance(color_input, str): + assert EVENT_RETURN_VALUE.info["user_value"] == color_input + else: + npt.assert_almost_equal(EVENT_RETURN_VALUE.info["user_value"], color_input) + + # make sure correct offset and size marked for pending upload + assert_pending_uploads(colors.buffer, offset, size) + + # check that others are not touched + others_truth = np.repeat([[1.0, 1.0, 1.0, 1.0]], repeats=len(others), axis=0) + npt.assert_almost_equal(colors[others], others_truth) + + # reset + colors[:] = (1, 1, 1, 1) + npt.assert_almost_equal(colors[:], np.repeat([[1.0, 1.0, 1.0, 1.0]], 10, axis=0)) diff --git a/tests/test_common_features.py b/tests/test_common_features.py new file mode 100644 index 000000000..332ac71ae --- /dev/null +++ b/tests/test_common_features.py @@ -0,0 +1,282 @@ +import numpy +import numpy as np +from numpy import testing as npt +import pytest + +import fastplotlib as fpl +from fastplotlib.graphics._features import FeatureEvent, Name, Offset, Rotation, Visible + + +def make_graphic(kind: str, **kwargs): + match kind: + case "image": + return fpl.ImageGraphic(np.random.rand(10, 10), **kwargs) + case "line": + return fpl.LineGraphic(np.random.rand(10), **kwargs) + case "scatter": + return fpl.ScatterGraphic( + np.column_stack([np.random.rand(10), np.random.rand(10)]), **kwargs + ) + case "text": + return fpl.TextGraphic("bah", **kwargs) + + +graphic_kinds = [ + "image", + "line", + "scatter", + "text", +] + + +RETURN_EVENT_VALUE: FeatureEvent = None +DECORATED_EVENT_VALUE: FeatureEvent = None + + +def return_event(ev: FeatureEvent): + global RETURN_EVENT_VALUE + RETURN_EVENT_VALUE = ev + + +@pytest.mark.parametrize("graphic", [make_graphic(k) for k in graphic_kinds]) +def test_name(graphic): + assert graphic.name is None + + graphic.add_event_handler(return_event, "name") + + graphic.name = "new_name" + + assert graphic.name == "new_name" + + global RETURN_EVENT_VALUE + + assert RETURN_EVENT_VALUE.type == "name" + assert RETURN_EVENT_VALUE.graphic is graphic + assert RETURN_EVENT_VALUE.target is graphic.world_object + assert RETURN_EVENT_VALUE.info["value"] == "new_name" + + # check removing event handler + RETURN_EVENT_VALUE = None + graphic.remove_event_handler(return_event, "name") + assert len(graphic._event_handlers["name"]) == 0 + + graphic.name = "new_name2" + + assert RETURN_EVENT_VALUE is None + assert graphic.name == "new_name2" + + # check adding event with decorator + global DECORATED_EVENT_VALUE + DECORATED_EVENT_VALUE = None + + @graphic.add_event_handler("name") + def decorated_handler(ev): + global DECORATED_EVENT_VALUE + DECORATED_EVENT_VALUE = ev + + graphic.name = "test_dec" + assert graphic.name == "test_dec" + + assert DECORATED_EVENT_VALUE.type == "name" + assert DECORATED_EVENT_VALUE.graphic is graphic + assert DECORATED_EVENT_VALUE.target is graphic.world_object + assert DECORATED_EVENT_VALUE.info["value"] == "test_dec" + + +@pytest.mark.parametrize( + "graphic", [make_graphic(k, name="init_name") for k in graphic_kinds] +) +def test_name_init(graphic): + assert graphic.name == "init_name" + + graphic.name = "new_name" + + assert graphic.name == "new_name" + + +@pytest.mark.parametrize("graphic", [make_graphic(k) for k in graphic_kinds]) +def test_offset(graphic): + npt.assert_almost_equal(graphic.offset, (0.0, 0.0, 0.0)) + npt.assert_almost_equal(graphic.world_object.world.position, (0.0, 0.0, 0.0)) + + graphic.add_event_handler(return_event, "offset") + + graphic.offset = (1.0, 2.0, 3.0) + + npt.assert_almost_equal(graphic.offset, (1.0, 2.0, 3.0)) + npt.assert_almost_equal(graphic.world_object.world.position, (1.0, 2.0, 3.0)) + + global RETURN_EVENT_VALUE + + assert RETURN_EVENT_VALUE.type == "offset" + assert RETURN_EVENT_VALUE.graphic is graphic + assert RETURN_EVENT_VALUE.target is graphic.world_object + npt.assert_almost_equal(RETURN_EVENT_VALUE.info["value"], (1.0, 2.0, 3.0)) + + # check removing event handler + RETURN_EVENT_VALUE = None + graphic.remove_event_handler(return_event, "offset") + assert len(graphic._event_handlers["offset"]) == 0 + + graphic.offset = (4, 5, 6) + + assert RETURN_EVENT_VALUE is None + npt.assert_almost_equal(graphic.offset, (4.0, 5.0, 6.0)) + + # check adding event with decorator + global DECORATED_EVENT_VALUE + DECORATED_EVENT_VALUE = None + + @graphic.add_event_handler("offset") + def decorated_handler(ev): + global DECORATED_EVENT_VALUE + DECORATED_EVENT_VALUE = ev + + graphic.offset = (7, 8, 9) + npt.assert_almost_equal(graphic.offset, (7.0, 8.0, 9.0)) + + assert DECORATED_EVENT_VALUE.type == "offset" + assert DECORATED_EVENT_VALUE.graphic is graphic + assert DECORATED_EVENT_VALUE.target is graphic.world_object + assert DECORATED_EVENT_VALUE.info["value"] == (7.0, 8.0, 9.0) + + +@pytest.mark.parametrize( + "graphic", [make_graphic(k, offset=(3.0, 4.0, 5.0)) for k in graphic_kinds] +) +def test_offset_init(graphic): + npt.assert_almost_equal(graphic.offset, (3.0, 4.0, 5.0)) + npt.assert_almost_equal(graphic.world_object.world.position, (3.0, 4.0, 5.0)) + + graphic.offset = (6.0, 7.0, 8.0) + + npt.assert_almost_equal(graphic.offset, (6.0, 7.0, 8.0)) + npt.assert_almost_equal(graphic.world_object.world.position, (6.0, 7.0, 8.0)) + + +@pytest.mark.parametrize("graphic", [make_graphic(k) for k in graphic_kinds]) +def test_rotation(graphic): + npt.assert_almost_equal(graphic.rotation, (0, 0, 0, 1)) + npt.assert_almost_equal(graphic.world_object.world.rotation, (0, 0, 0, 1)) + + graphic.add_event_handler(return_event, "rotation") + + graphic.rotation = (0.0, 0.0, 0.30001427, 0.95393471) + + npt.assert_almost_equal(graphic.rotation, (0.0, 0.0, 0.30001427, 0.95393471)) + npt.assert_almost_equal( + graphic.world_object.world.rotation, (0.0, 0.0, 0.30001427, 0.95393471) + ) + + global RETURN_EVENT_VALUE + + assert RETURN_EVENT_VALUE.type == "rotation" + assert RETURN_EVENT_VALUE.graphic is graphic + assert RETURN_EVENT_VALUE.target is graphic.world_object + npt.assert_almost_equal( + RETURN_EVENT_VALUE.info["value"], (0.0, 0.0, 0.30001427, 0.95393471) + ) + + # check removing event handler + RETURN_EVENT_VALUE = None + graphic.remove_event_handler(return_event, "rotation") + assert len(graphic._event_handlers["rotation"]) == 0 + + graphic.rotation = (0, 0, 0, 1) + + assert RETURN_EVENT_VALUE is None + npt.assert_almost_equal(graphic.rotation, (0, 0, 0, 1)) + + # check adding event with decorator + global DECORATED_EVENT_VALUE + DECORATED_EVENT_VALUE = None + + @graphic.add_event_handler("rotation") + def decorated_handler(ev): + global DECORATED_EVENT_VALUE + DECORATED_EVENT_VALUE = ev + + graphic.rotation = (0, 0, 0.6, 0.8) + npt.assert_almost_equal(graphic.rotation, (0, 0, 0.6, 0.8)) + + assert DECORATED_EVENT_VALUE.type == "rotation" + assert DECORATED_EVENT_VALUE.graphic is graphic + assert DECORATED_EVENT_VALUE.target is graphic.world_object + assert DECORATED_EVENT_VALUE.info["value"] == (0, 0, 0.6, 0.8) + + +@pytest.mark.parametrize( + "graphic", + [ + make_graphic(k, rotation=(0.0, 0.0, 0.30001427, 0.95393471)) + for k in graphic_kinds + ], +) +def test_rotation(graphic): + npt.assert_almost_equal(graphic.rotation, (0.0, 0.0, 0.30001427, 0.95393471)) + npt.assert_almost_equal( + graphic.world_object.world.rotation, (0.0, 0.0, 0.30001427, 0.95393471) + ) + + graphic.rotation = (0, 0.0, 0.6, 0.8) + + npt.assert_almost_equal(graphic.rotation, (0, 0.0, 0.6, 0.8)) + npt.assert_almost_equal(graphic.world_object.world.rotation, (0, 0.0, 0.6, 0.8)) + + +@pytest.mark.parametrize("graphic", [make_graphic(k) for k in graphic_kinds]) +def test_visible(graphic): + assert graphic.visible is True + assert graphic.world_object.visible is True + + graphic.add_event_handler(return_event, "rotation") + + graphic.visible = False + assert graphic.visible is False + assert graphic.world_object.visible is False + + global RETURN_EVENT_VALUE + + assert RETURN_EVENT_VALUE.type == "visible" + assert RETURN_EVENT_VALUE.graphic is graphic + assert RETURN_EVENT_VALUE.target is graphic.world_object + assert RETURN_EVENT_VALUE.info["value"] is False + + # check removing event handler + RETURN_EVENT_VALUE = None + graphic.remove_event_handler(return_event, "visible") + assert len(graphic._event_handlers["visible"]) == 0 + + graphic.visible = True + + assert RETURN_EVENT_VALUE is None + assert graphic.visible is True + + # check adding event with decorator + global DECORATED_EVENT_VALUE + DECORATED_EVENT_VALUE = None + + @graphic.add_event_handler("visible") + def decorated_handler(ev): + global DECORATED_EVENT_VALUE + DECORATED_EVENT_VALUE = ev + + graphic.visible = False + assert graphic.visible is False + + assert DECORATED_EVENT_VALUE.type == "visible" + assert DECORATED_EVENT_VALUE.graphic is graphic + assert DECORATED_EVENT_VALUE.target is graphic.world_object + assert DECORATED_EVENT_VALUE.info["value"] is False + + +@pytest.mark.parametrize( + "graphic", [make_graphic(k, visible=False) for k in graphic_kinds] +) +def test_visible(graphic): + assert graphic.visible is False + assert graphic.world_object.visible is False + + graphic.visible = True + assert graphic.visible is True + assert graphic.world_object.visible is True diff --git a/tests/test_figure.py b/tests/test_figure.py index 27b74c0b6..757b1eeae 100644 --- a/tests/test_figure.py +++ b/tests/test_figure.py @@ -6,21 +6,18 @@ def test_cameras_controller_properties(): - cameras = [ - ["2d", "3d", "3d"], - ["3d", "3d", "3d"] - ] + cameras = [["2d", "3d", "3d"], ["3d", "3d", "3d"]] controller_types = [ ["panzoom", "panzoom", "fly"], - ["orbit", "trackball", "panzoom"] + ["orbit", "trackball", "panzoom"], ] fig = fpl.Figure( shape=(2, 3), cameras=cameras, controller_types=controller_types, - canvas="offscreen" + canvas="offscreen", ) print(fig.canvas) @@ -34,13 +31,17 @@ def test_cameras_controller_properties(): for c1, c2 in zip(subplot_controllers, fig.controllers.ravel()): assert c1 is c2 - for camera_type, subplot_camera in zip(np.asarray(cameras).ravel(), fig.cameras.ravel()): + for camera_type, subplot_camera in zip( + np.asarray(cameras).ravel(), fig.cameras.ravel() + ): if camera_type == "2d": assert subplot_camera.fov == 0 else: assert subplot_camera.fov == 50 - for controller_type, subplot_controller in zip(np.asarray(controller_types).ravel(), fig.controllers.ravel()): + for controller_type, subplot_controller in zip( + np.asarray(controller_types).ravel(), fig.controllers.ravel() + ): match controller_type: case "panzoom": assert isinstance(subplot_controller, pygfx.PanZoomController) @@ -67,11 +68,7 @@ def test_cameras_controller_properties(): def test_controller_ids_int(): - ids = [ - [0, 1, 1], - [0, 2, 3], - [4, 1, 2] - ] + ids = [[0, 1, 1], [0, 2, 3], [4, 1, 2]] fig = fpl.Figure(shape=(3, 3), controller_ids=ids, canvas="offscreen") @@ -81,19 +78,13 @@ def test_controller_ids_int(): def test_controller_ids_int_change_controllers(): - ids = [ - [0, 1, 1], - [0, 2, 3], - [4, 1, 2] - ] + ids = [[0, 1, 1], [0, 2, 3], [4, 1, 2]] - cameras = [ - ["2d", "3d", "3d"], - ["2d", "3d", "2d"], - ["3d", "3d", "3d"] - ] + cameras = [["2d", "3d", "3d"], ["2d", "3d", "2d"], ["3d", "3d", "3d"]] - fig = fpl.Figure(shape=(3, 3), cameras=cameras, controller_ids=ids, canvas="offscreen") + fig = fpl.Figure( + shape=(3, 3), cameras=cameras, controller_ids=ids, canvas="offscreen" + ) assert isinstance(fig[0, 1].controller, pygfx.FlyController) @@ -101,30 +92,46 @@ def test_controller_ids_int_change_controllers(): fig[0, 1].controller = "panzoom" assert isinstance(fig[0, 1].controller, pygfx.PanZoomController) assert fig[0, 1].controller is fig[0, 2].controller is fig[2, 1].controller - assert set(fig[0, 1].controller.cameras) == {fig[0, 1].camera, fig[0, 2].camera, fig[2, 1].camera} + assert set(fig[0, 1].controller.cameras) == { + fig[0, 1].camera, + fig[0, 2].camera, + fig[2, 1].camera, + } # change to orbit fig[0, 1].controller = "orbit" assert isinstance(fig[0, 1].controller, pygfx.OrbitController) assert fig[0, 1].controller is fig[0, 2].controller is fig[2, 1].controller - assert set(fig[0, 1].controller.cameras) == {fig[0, 1].camera, fig[0, 2].camera, fig[2, 1].camera} + assert set(fig[0, 1].controller.cameras) == { + fig[0, 1].camera, + fig[0, 2].camera, + fig[2, 1].camera, + } def test_controller_ids_str(): - names = [ - ["a", "b", "c"], - ["d", "e", "f"] - ] + names = [["a", "b", "c"], ["d", "e", "f"]] - controller_ids = [ - ["a", "f"], - ["b", "d", "e"] - ] + controller_ids = [["a", "f"], ["b", "d", "e"]] - fig = fpl.Figure(shape=(2, 3), controller_ids=controller_ids, names=names, canvas="offscreen") + fig = fpl.Figure( + shape=(2, 3), controller_ids=controller_ids, names=names, canvas="offscreen" + ) - assert fig[0, 0].controller is fig[1, 2].controller is fig["a"].controller is fig["f"].controller - assert fig[0, 1].controller is fig[1, 0].controller is fig[1, 1].controller is fig["b"].controller is fig["d"].controller is fig["e"].controller + assert ( + fig[0, 0].controller + is fig[1, 2].controller + is fig["a"].controller + is fig["f"].controller + ) + assert ( + fig[0, 1].controller + is fig[1, 0].controller + is fig[1, 1].controller + is fig["b"].controller + is fig["d"].controller + is fig["e"].controller + ) # make sure subplot c is unique exclude_c = [fig[n].controller for n in ["a", "b", "d", "e", "f"]] @@ -137,22 +144,23 @@ def test_set_controllers_from_existing_controllers(): assert fig.controllers[:-1].size == 6 with pytest.raises(ValueError): - fig3 = fpl.Figure(shape=fig.shape, controllers=fig.controllers[:-1], canvas="offscreen") + fig3 = fpl.Figure( + shape=fig.shape, controllers=fig.controllers[:-1], canvas="offscreen" + ) for fig1_subplot, fig2_subplot in zip(fig, fig2): assert fig1_subplot.controller is fig2_subplot.controller - cameras = [ - [pygfx.PerspectiveCamera(), "3d"], - ["3d", "2d"] - ] + cameras = [[pygfx.PerspectiveCamera(), "3d"], ["3d", "2d"]] controllers = [ [pygfx.FlyController(cameras[0][0]), pygfx.TrackballController()], - [pygfx.OrbitController(), pygfx.PanZoomController()] + [pygfx.OrbitController(), pygfx.PanZoomController()], ] - fig = fpl.Figure(shape=(2, 2), cameras=cameras, controllers=controllers, canvas="offscreen") + fig = fpl.Figure( + shape=(2, 2), cameras=cameras, controllers=controllers, canvas="offscreen" + ) assert fig[0, 0].controller is controllers[0][0] assert fig[0, 1].controller is controllers[0][1] diff --git a/tests/test_image_graphic.py b/tests/test_image_graphic.py new file mode 100644 index 000000000..9f89e8aa8 --- /dev/null +++ b/tests/test_image_graphic.py @@ -0,0 +1,205 @@ +import numpy as np +from numpy import testing as npt +import imageio.v3 as iio + +import fastplotlib as fpl +from fastplotlib.graphics._features import FeatureEvent +from fastplotlib.utils import make_colors + +GRAY_IMAGE = iio.imread("imageio:camera.png") +RGB_IMAGE = iio.imread("imageio:astronaut.png") + + +COFFEE_IMAGE = iio.imread("imageio:coffee.png") + +# image cmap, vmin, vmax, interpolations +# new screenshot tests too for these when in graphics + + +EVENT_RETURN_VALUE: FeatureEvent = None + + +def event_handler(ev): + global EVENT_RETURN_VALUE + EVENT_RETURN_VALUE = ev + + +def check_event(graphic, feature, value): + global EVENT_RETURN_VALUE + assert isinstance(EVENT_RETURN_VALUE, FeatureEvent) + assert EVENT_RETURN_VALUE.type == feature + assert EVENT_RETURN_VALUE.graphic == graphic + assert EVENT_RETURN_VALUE.target == graphic.world_object + if isinstance(EVENT_RETURN_VALUE.info["value"], float): + # floating point error + npt.assert_almost_equal(EVENT_RETURN_VALUE.info["value"], value) + else: + assert EVENT_RETURN_VALUE.info["value"] == value + + +def check_set_slice( + data: np.ndarray, + image_graphic: fpl.ImageGraphic, + row_slice: slice, + col_slice: slice, +): + image_graphic.data[row_slice, col_slice] = 1 + data_values = image_graphic.data.value + npt.assert_almost_equal(data_values[row_slice, col_slice], 1) + + # make sure other vals unchanged + npt.assert_almost_equal(data_values[: row_slice.start], data[: row_slice.start]) + npt.assert_almost_equal(data_values[row_slice.stop :], data[row_slice.stop :]) + npt.assert_almost_equal( + data_values[:, : col_slice.start], data[:, : col_slice.start] + ) + npt.assert_almost_equal(data_values[:, col_slice.stop :], data[:, col_slice.stop :]) + + global EVENT_RETURN_VALUE + assert isinstance(EVENT_RETURN_VALUE, FeatureEvent) + assert EVENT_RETURN_VALUE.type == "data" + assert EVENT_RETURN_VALUE.graphic == image_graphic + assert EVENT_RETURN_VALUE.target == image_graphic.world_object + assert EVENT_RETURN_VALUE.info["key"] == (row_slice, col_slice) + npt.assert_almost_equal(EVENT_RETURN_VALUE.info["value"], 1) + + +def test_gray(): + fig = fpl.Figure() + ig = fig[0, 0].add_image(GRAY_IMAGE) + assert isinstance(ig, fpl.ImageGraphic) + + ig.add_event_handler( + event_handler, + "data", + "cmap", + "vmin", + "vmax", + "interpolation", + "cmap_interpolation", + ) + + npt.assert_almost_equal(ig.data.value, GRAY_IMAGE) + + ig.cmap = "viridis" + assert ig.cmap == "viridis" + check_event(graphic=ig, feature="cmap", value="viridis") + + new_colors = make_colors(256, "viridis") + for child in ig.world_object.children: + npt.assert_almost_equal(child.material.map.data, new_colors) + + ig.cmap = "jet" + assert ig.cmap == "jet" + + new_colors = make_colors(256, "jet") + for child in ig.world_object.children: + npt.assert_almost_equal(child.material.map.data, new_colors) + + assert ig.interpolation == "nearest" + for child in ig.world_object.children: + assert child.material.interpolation == "nearest" + + ig.interpolation = "linear" + assert ig.interpolation == "linear" + for child in ig.world_object.children: + assert child.material.interpolation == "linear" + check_event(graphic=ig, feature="interpolation", value="linear") + + assert ig.cmap_interpolation == "linear" + for child in ig.world_object.children: + assert child.material.map_interpolation == "linear" + + ig.cmap_interpolation = "nearest" + assert ig.cmap_interpolation == "nearest" + for child in ig.world_object.children: + assert child.material.map_interpolation == "nearest" + check_event(graphic=ig, feature="cmap_interpolation", value="nearest") + + npt.assert_almost_equal(ig.vmin, GRAY_IMAGE.min()) + npt.assert_almost_equal(ig.vmax, GRAY_IMAGE.max()) + + ig.vmin = 50 + assert ig.vmin == 50 + for child in ig.world_object.children: + assert child.material.clim == (50, ig.vmax) + check_event(graphic=ig, feature="vmin", value=50) + + ig.vmax = 100 + assert ig.vmax == 100 + for child in ig.world_object.children: + assert child.material.clim == (ig.vmin, 100) + check_event(graphic=ig, feature="vmax", value=100) + + # test reset + ig.reset_vmin_vmax() + npt.assert_almost_equal(ig.vmin, GRAY_IMAGE.min()) + npt.assert_almost_equal(ig.vmax, GRAY_IMAGE.max()) + + check_set_slice(GRAY_IMAGE, ig, slice(100, 200), slice(200, 300)) + + # test setting all values + ig.data = 1 + npt.assert_almost_equal(ig.data.value, 1) + + +def test_rgb(): + fig = fpl.Figure() + ig = fig[0, 0].add_image(RGB_IMAGE) + assert isinstance(ig, fpl.ImageGraphic) + + ig.add_event_handler(event_handler, "data") + + npt.assert_almost_equal(ig.data.value, RGB_IMAGE) + + assert ig.interpolation == "nearest" + for child in ig.world_object.children: + assert child.material.interpolation == "nearest" + + ig.interpolation = "linear" + assert ig.interpolation == "linear" + for child in ig.world_object.children: + assert child.material.interpolation == "linear" + + npt.assert_almost_equal(ig.vmin, RGB_IMAGE.min()) + npt.assert_almost_equal(ig.vmax, RGB_IMAGE.max()) + + ig.vmin = 50 + assert ig.vmin == 50 + for child in ig.world_object.children: + assert child.material.clim == (50, ig.vmax) + + ig.vmax = 100 + assert ig.vmax == 100 + for child in ig.world_object.children: + assert child.material.clim == (ig.vmin, 100) + + # test reset + ig.reset_vmin_vmax() + npt.assert_almost_equal(ig.vmin, RGB_IMAGE.min()) + npt.assert_almost_equal(ig.vmax, RGB_IMAGE.max()) + + check_set_slice(RGB_IMAGE, ig, slice(100, 200), slice(200, 300)) + + +def test_rgba(): + rgba = np.zeros(shape=(*COFFEE_IMAGE.shape[:2], 4), dtype=np.float32) + + fig = fpl.Figure() + ig = fig[0, 0].add_image(rgba) + assert isinstance(ig, fpl.ImageGraphic) + + npt.assert_almost_equal(ig.data.value, rgba) + + # fancy indexing + # set the blue values of some pixels with an alpha > 1 + ig.data[COFFEE_IMAGE[:, :, -1] > 200] = np.array([0.0, 0.0, 1.0, 0.6]).astype( + np.float32 + ) + + rgba[COFFEE_IMAGE[:, :, -1] > 200] = np.array([0.0, 0.0, 1.0, 0.6]).astype( + np.float32 + ) + + # check that fancy indexing works + npt.assert_almost_equal(ig.data.value, rgba) diff --git a/tests/test_positions_data_buffer_manager.py b/tests/test_positions_data_buffer_manager.py new file mode 100644 index 000000000..de9d179d8 --- /dev/null +++ b/tests/test_positions_data_buffer_manager.py @@ -0,0 +1,208 @@ +import numpy as np +from numpy import testing as npt +import pytest + +import fastplotlib as fpl +from fastplotlib.graphics._features import VertexPositions, FeatureEvent +from .utils import ( + generate_slice_indices, + assert_pending_uploads, + generate_positions_spiral_data, +) + + +EVENT_RETURN_VALUE: FeatureEvent = None + + +def event_handler(ev): + global EVENT_RETURN_VALUE + EVENT_RETURN_VALUE = ev + + +@pytest.mark.parametrize( + "data", [generate_positions_spiral_data(v) for v in ["y", "xy", "xyz"]] +) +def test_create_buffer(data): + points_data = VertexPositions(data) + + if data.ndim == 1: + # only y-vals specified + npt.assert_almost_equal(points_data[:, 1], generate_positions_spiral_data("y")) + # x-vals are auto generated just using arange + npt.assert_almost_equal(points_data[:, 0], np.arange(data.size)) + + elif data.shape[1] == 2: + # test 2D + npt.assert_almost_equal( + points_data[:, :-1], generate_positions_spiral_data("xy") + ) + npt.assert_almost_equal(points_data[:, -1], 0.0) + + elif data.shape[1] == 3: + # test 3D spiral + npt.assert_almost_equal(points_data[:], generate_positions_spiral_data("xyz")) + + +@pytest.mark.parametrize("test_graphic", [False, "line", "scatter"]) +def test_int(test_graphic): + # test setting single points + + data = generate_positions_spiral_data("xyz") + if test_graphic: + fig = fpl.Figure() + + if test_graphic == "line": + graphic = fig[0, 0].add_line(data=data) + + elif test_graphic == "scatter": + graphic = fig[0, 0].add_scatter(data=data) + + points = graphic.data + global EVENT_RETURN_VALUE + graphic.add_event_handler(event_handler, "data") + else: + points = VertexPositions(data) + + # set all x, y, z points, create a kink in the spiral + points[2] = 1.0 + npt.assert_almost_equal(points[2], 1.0) + # make sure other points are not affected + indices = list(range(10)) + indices.pop(2) + npt.assert_almost_equal(points[indices], data[indices]) + + # check event + if test_graphic: + assert isinstance(EVENT_RETURN_VALUE, FeatureEvent) + assert EVENT_RETURN_VALUE.graphic == graphic + assert EVENT_RETURN_VALUE.target is graphic.world_object + assert EVENT_RETURN_VALUE.info["key"] == 2 + npt.assert_almost_equal(EVENT_RETURN_VALUE.info["value"], 1.0) + + # reset + if test_graphic: + graphic.data = data + else: + points[:] = data + npt.assert_almost_equal(points[:], data) + + # check event + if test_graphic: + assert isinstance(EVENT_RETURN_VALUE, FeatureEvent) + assert EVENT_RETURN_VALUE.graphic == graphic + assert EVENT_RETURN_VALUE.target is graphic.world_object + assert EVENT_RETURN_VALUE.info["key"] == slice(None) + npt.assert_almost_equal(EVENT_RETURN_VALUE.info["value"], data) + + # just set y value + points[3, 1] = 1.0 + npt.assert_almost_equal(points[3, 1], 1.0) + # make sure others not modified + npt.assert_almost_equal(points[3, 0], data[3, 0]) + npt.assert_almost_equal(points[3, 2], data[3, 2]) + indices = list(range(10)) + indices.pop(3) + npt.assert_almost_equal(points[indices], data[indices]) + + +@pytest.mark.parametrize("test_graphic", [False, "line", "scatter"]) +@pytest.mark.parametrize( + "slice_method", [generate_slice_indices(i) for i in range(1, 16)] +) # int tested separately +@pytest.mark.parametrize("test_axis", ["y", "xy", "xyz"]) +def test_slice(test_graphic, slice_method: dict, test_axis: str): + data = generate_positions_spiral_data("xyz") + + if test_graphic: + fig = fpl.Figure() + + if test_graphic == "line": + graphic = fig[0, 0].add_line(data=data) + + elif test_graphic == "scatter": + graphic = fig[0, 0].add_scatter(data=data) + + points = graphic.data + global EVENT_RETURN_VALUE + graphic.add_event_handler(event_handler, "data") + else: + points = VertexPositions(data) + + s = slice_method["slice"] + indices = slice_method["indices"] + offset = slice_method["offset"] + size = slice_method["size"] + others = slice_method["others"] + + # TODO: placeholder until I make a testing figure where we draw frames only on call + points.buffer._gfx_pending_uploads.clear() + + match test_axis: + case "y": + points[s, 1] = -data[s, 1] + npt.assert_almost_equal(points[s, 1], -data[s, 1]) + npt.assert_almost_equal(points[indices, 1], -data[indices, 1]) + # make sure other points are not modified + npt.assert_almost_equal( + points[others, 1], data[others, 1] + ) # other points in same dimension + npt.assert_almost_equal( + points[:, 2:], data[:, 2:] + ) # dimensions that are not sliced + + # check event + if test_graphic: + assert isinstance(EVENT_RETURN_VALUE, FeatureEvent) + assert EVENT_RETURN_VALUE.graphic == graphic + assert EVENT_RETURN_VALUE.target is graphic.world_object + if isinstance(s, slice): + assert EVENT_RETURN_VALUE.info["key"] == (s, 1) + else: + npt.assert_almost_equal(EVENT_RETURN_VALUE.info["key"][0], s) + assert EVENT_RETURN_VALUE.info["key"][1] == 1 + npt.assert_almost_equal(EVENT_RETURN_VALUE.info["value"], -data[s, 1]) + + case "xy": + points[s, :-1] = -data[s, :-1] + npt.assert_almost_equal(points[s, :-1], -data[s, :-1]) + npt.assert_almost_equal(points[indices, :-1], -data[s, :-1]) + # make sure other points are not modified + npt.assert_almost_equal( + points[others, :-1], data[others, :-1] + ) # other points in the same dimensions + npt.assert_almost_equal( + points[:, -1], data[:, -1] + ) # dimensions that are not touched + + # check event + if test_graphic: + assert isinstance(EVENT_RETURN_VALUE, FeatureEvent) + assert EVENT_RETURN_VALUE.graphic == graphic + assert EVENT_RETURN_VALUE.target is graphic.world_object + if isinstance(s, slice): + assert EVENT_RETURN_VALUE.info["key"] == (s, slice(None, -1, None)) + else: + npt.assert_almost_equal(EVENT_RETURN_VALUE.info["key"][0], s) + assert EVENT_RETURN_VALUE.info["key"][1] == slice(None, -1, None) + npt.assert_almost_equal(EVENT_RETURN_VALUE.info["value"], -data[s, :-1]) + + case "xyz": + points[s] = -data[s] + npt.assert_almost_equal(points[s], -data[s]) + npt.assert_almost_equal(points[indices], -data[s]) + # make sure other points are not modified + npt.assert_almost_equal(points[others], data[others]) + + # check event + if test_graphic: + assert isinstance(EVENT_RETURN_VALUE, FeatureEvent) + assert EVENT_RETURN_VALUE.graphic == graphic + assert EVENT_RETURN_VALUE.target is graphic.world_object + if isinstance(s, slice): + assert EVENT_RETURN_VALUE.info["key"] == s + else: + npt.assert_almost_equal(EVENT_RETURN_VALUE.info["key"], s) + npt.assert_almost_equal(EVENT_RETURN_VALUE.info["value"], -data[s]) + + # make sure correct offset and size marked for pending upload + assert_pending_uploads(points.buffer, offset, size) diff --git a/tests/test_positions_graphics.py b/tests/test_positions_graphics.py new file mode 100644 index 000000000..d9c3a4871 --- /dev/null +++ b/tests/test_positions_graphics.py @@ -0,0 +1,446 @@ +import numpy as np +from numpy import testing as npt +import pytest + +import pygfx + +import fastplotlib as fpl +from fastplotlib.graphics._features import ( + VertexPositions, + VertexColors, + VertexCmap, + UniformColor, + UniformSize, + PointsSizesFeature, + Thickness, + FeatureEvent, +) + +from .utils import ( + generate_positions_spiral_data, + generate_color_inputs, + MULTI_COLORS_TRUTH, + generate_slice_indices, + assert_pending_uploads, +) + + +TRUTH_CMAPS = { + "jet": np.array( + [ + [0.0, 0.0, 0.5, 1.0], + [0.0, 0.0, 0.99910873, 1.0], + [0.0, 0.37843138, 1.0, 1.0], + [0.0, 0.8333333, 1.0, 1.0], + [0.30044276, 1.0, 0.66729915, 1.0], + [0.65464896, 1.0, 0.31309298, 1.0], + [1.0, 0.90123457, 0.0, 1.0], + [1.0, 0.4945534, 0.0, 1.0], + [1.0, 0.08787218, 0.0, 1.0], + [0.5, 0.0, 0.0, 1.0], + ], + dtype=np.float32, + ), + "viridis": np.array( + [ + [0.267004, 0.004874, 0.329415, 1.0], + [0.281412, 0.155834, 0.469201, 1.0], + [0.244972, 0.287675, 0.53726, 1.0], + [0.190631, 0.407061, 0.556089, 1.0], + [0.147607, 0.511733, 0.557049, 1.0], + [0.119483, 0.614817, 0.537692, 1.0], + [0.20803, 0.718701, 0.472873, 1.0], + [0.421908, 0.805774, 0.35191, 1.0], + [0.699415, 0.867117, 0.175971, 1.0], + [0.993248, 0.906157, 0.143936, 1.0], + ], + dtype=np.float32, + ), +} + + +EVENT_RETURN_VALUE: FeatureEvent = None + + +def event_handler(ev): + global EVENT_RETURN_VALUE + EVENT_RETURN_VALUE = ev + + +def test_sizes_slice(): + pass + + +@pytest.mark.parametrize("graphic_type", ["line", "scatter"]) +@pytest.mark.parametrize("colors", [None, *generate_color_inputs("b")]) +@pytest.mark.parametrize("uniform_color", [True, False]) +@pytest.mark.parametrize("alpha", [1.0, 0.5, 0.0]) +def test_uniform_color(graphic_type, colors, uniform_color, alpha): + fig = fpl.Figure() + + kwargs = dict() + for kwarg in ["colors", "uniform_color", "alpha"]: + if locals()[kwarg] is not None: + # add to dict of arguments that will be passed + kwargs[kwarg] = locals()[kwarg] + + data = generate_positions_spiral_data("xy") + + if graphic_type == "line": + graphic = fig[0, 0].add_line(data=data, **kwargs) + elif graphic_type == "scatter": + graphic = fig[0, 0].add_scatter(data=data, **kwargs) + + if uniform_color: + assert isinstance(graphic._colors, UniformColor) + assert isinstance(graphic.colors, pygfx.Color) + if colors is None: + # default white + assert graphic.colors == pygfx.Color([1, 1, 1, alpha]) + else: + # should be blue + assert graphic.colors == pygfx.Color([0, 0, 1, alpha]) + + # check pygfx material + npt.assert_almost_equal( + graphic.world_object.material.color, np.asarray(graphic.colors) + ) + else: + assert isinstance(graphic._colors, VertexColors) + assert isinstance(graphic.colors, VertexColors) + if colors is None: + # default white + npt.assert_almost_equal( + graphic.colors.value, + np.repeat([[1, 1, 1, alpha]], repeats=len(graphic.data), axis=0), + ) + else: + # blue + npt.assert_almost_equal( + graphic.colors.value, + np.repeat([[0, 0, 1, alpha]], repeats=len(graphic.data), axis=0), + ) + + # check geometry + npt.assert_almost_equal( + graphic.world_object.geometry.colors.data, graphic.colors.value + ) + + +@pytest.mark.parametrize("graphic_type", ["line", "scatter"]) +@pytest.mark.parametrize( + "data", [generate_positions_spiral_data(v) for v in ["y", "xy", "xyz"]] +) +def test_positions_graphics_data( + graphic_type, + data, +): + # tests with different ways of passing positions data, x, xy and xyz + fig = fpl.Figure() + + if graphic_type == "line": + graphic = fig[0, 0].add_line(data=data) + + elif graphic_type == "scatter": + graphic = fig[0, 0].add_scatter(data=data) + + assert isinstance(graphic._data, VertexPositions) + assert isinstance(graphic.data, VertexPositions) + + # n_datapoints must match + assert len(graphic.data.value) == len(data) + + # make sure data is correct + match data.shape[-1]: + case 1: # only y-vals given + npt.assert_almost_equal(graphic.data[:, 1], data) # y vals must match + npt.assert_almost_equal( + graphic.data[:, 0], np.arange(data.size) + ) # VertexData makes x-vals with arange + npt.assert_almost_equal(graphic.data[:, -1], 0) # z-vals must be zeros + case 2: # xy vals given + npt.assert_almost_equal(graphic.data[:, :-1], data) # x and y must match + npt.assert_almost_equal(graphic.data[:, -1], 0) # z-vals must be zero + case 3: # xyz vals given + npt.assert_almost_equal(graphic.data[:], data[:]) # everything must match + + +@pytest.mark.parametrize("graphic_type", ["line", "scatter"]) +@pytest.mark.parametrize("colors", [None, *generate_color_inputs("r")]) +@pytest.mark.parametrize("uniform_color", [None, False]) +@pytest.mark.parametrize("alpha", [None, 0.5, 0.0]) +def test_positions_graphic_vertex_colors( + graphic_type, + colors, + uniform_color, + alpha, +): + # test different ways of passing vertex colors + fig = fpl.Figure() + + kwargs = dict() + for kwarg in ["colors", "uniform_color", "alpha"]: + if locals()[kwarg] is not None: + # add to dict of arguments that will be passed + kwargs[kwarg] = locals()[kwarg] + + data = generate_positions_spiral_data("xy") + + if graphic_type == "line": + graphic = fig[0, 0].add_line(data=data, **kwargs) + elif graphic_type == "scatter": + graphic = fig[0, 0].add_scatter(data=data, **kwargs) + + if alpha is None: # default arg + alpha = 1 + + # color per vertex + # uniform colors is default False, or set to False + assert isinstance(graphic._colors, VertexColors) + assert isinstance(graphic.colors, VertexColors) + assert len(graphic.colors) == len(graphic.data) + + if colors is None: + # default + npt.assert_almost_equal( + graphic.colors.value, + np.repeat([[1, 1, 1, alpha]], repeats=len(graphic.data), axis=0), + ) + else: + if len(colors) != len(graphic.data): + # should be single red, regardless of input variant (i.e. str, array, RGBA tuple, etc. + npt.assert_almost_equal( + graphic.colors.value, + np.repeat([[1, 0, 0, alpha]], repeats=len(graphic.data), axis=0), + ) + else: + # multi colors + # use the truth for multi colors test that is pre-set + npt.assert_almost_equal(graphic.colors.value, MULTI_COLORS_TRUTH) + + +@pytest.mark.parametrize("graphic_type", ["line", "scatter"]) +@pytest.mark.parametrize("colors", [None, *generate_color_inputs("r")]) +@pytest.mark.parametrize("uniform_color", [None, False]) +@pytest.mark.parametrize("cmap", ["jet"]) +@pytest.mark.parametrize( + "cmap_transform", [None, [3, 5, 2, 1, 0, 6, 9, 7, 4, 8], np.arange(9, -1, -1)] +) +@pytest.mark.parametrize("alpha", [None, 0.5, 0.0]) +def test_cmap( + graphic_type, + colors, + uniform_color, + cmap, + cmap_transform, + alpha, +): + # test different ways of passing cmap args + fig = fpl.Figure() + + kwargs = dict() + for kwarg in ["cmap", "cmap_transform", "colors", "uniform_color", "alpha"]: + if locals()[kwarg] is not None: + # add to dict of arguments that will be passed + kwargs[kwarg] = locals()[kwarg] + + data = generate_positions_spiral_data("xy") + + if graphic_type == "line": + graphic = fig[0, 0].add_line(data=data, **kwargs) + elif graphic_type == "scatter": + graphic = fig[0, 0].add_scatter(data=data, **kwargs) + + if alpha is None: + alpha = 1.0 + + truth = TRUTH_CMAPS[cmap].copy() + truth[:, -1] = alpha + + # permute if transform is provided + if cmap_transform is not None: + truth = truth[cmap_transform] + npt.assert_almost_equal(graphic.cmap.transform, cmap_transform) + + assert isinstance(graphic._cmap, VertexCmap) + + assert graphic.cmap.name == cmap + + # make sure buffer is identical + # cmap overrides colors argument + assert graphic.colors.buffer is graphic.cmap.buffer + + npt.assert_almost_equal(graphic.cmap.value, truth) + npt.assert_almost_equal(graphic.colors.value, truth) + + # test changing cmap but not transform + graphic.cmap = "viridis" + truth = TRUTH_CMAPS["viridis"].copy() + truth[:, -1] = alpha + + if cmap_transform is not None: + truth = truth[cmap_transform] + + assert graphic.cmap.name == "viridis" + npt.assert_almost_equal(graphic.cmap.value, truth) + npt.assert_almost_equal(graphic.colors.value, truth) + + # test changing transform + cmap_transform = np.random.rand(10) + + # cmap transform is internally normalized between 0 - 1 + cmap_transform_norm = cmap_transform.copy() + cmap_transform_norm -= cmap_transform.min() + cmap_transform_norm /= cmap_transform_norm.max() + cmap_transform_norm *= 255 + + truth = fpl.utils.get_cmap("viridis", alpha=alpha) + truth = np.vstack([truth[val] for val in cmap_transform_norm.astype(int)]) + + graphic.cmap.transform = cmap_transform + npt.assert_almost_equal(graphic.cmap.transform, cmap_transform) + + npt.assert_almost_equal(graphic.cmap.value, truth) + npt.assert_almost_equal(graphic.colors.value, truth) + + +@pytest.mark.parametrize("graphic_type", ["line", "scatter"]) +@pytest.mark.parametrize("cmap", ["jet"]) +@pytest.mark.parametrize( + "colors", [None, *generate_color_inputs("multi")] +) # cmap arg overrides colors +@pytest.mark.parametrize( + "uniform_color", [True] # none of these will work with a uniform buffer +) +def test_incompatible_cmap_color_args(graphic_type, cmap, colors, uniform_color): + # test incompatible cmap args + fig = fpl.Figure() + + kwargs = dict() + for kwarg in ["cmap", "colors", "uniform_color"]: + if locals()[kwarg] is not None: + # add to dict of arguments that will be passed + kwargs[kwarg] = locals()[kwarg] + + data = generate_positions_spiral_data("xy") + + if graphic_type == "line": + with pytest.raises(TypeError): + graphic = fig[0, 0].add_line(data=data, **kwargs) + elif graphic_type == "scatter": + with pytest.raises(TypeError): + graphic = fig[0, 0].add_scatter(data=data, **kwargs) + + +@pytest.mark.parametrize("graphic_type", ["line", "scatter"]) +@pytest.mark.parametrize("colors", [*generate_color_inputs("multi")]) +@pytest.mark.parametrize( + "uniform_color", [True] # none of these will work with a uniform buffer +) +def test_incompatible_color_args(graphic_type, colors, uniform_color): + # test incompatible color args + fig = fpl.Figure() + + kwargs = dict() + for kwarg in ["colors", "uniform_color"]: + if locals()[kwarg] is not None: + # add to dict of arguments that will be passed + kwargs[kwarg] = locals()[kwarg] + + data = generate_positions_spiral_data("xy") + + if graphic_type == "line": + with pytest.raises(TypeError): + graphic = fig[0, 0].add_line(data=data, **kwargs) + elif graphic_type == "scatter": + with pytest.raises(TypeError): + graphic = fig[0, 0].add_scatter(data=data, **kwargs) + + +@pytest.mark.parametrize("sizes", [None, 5.0, np.linspace(3, 8, 10, dtype=np.float32)]) +@pytest.mark.parametrize("uniform_size", [None, False]) +def test_sizes(sizes, uniform_size): + # test scatter sizes + fig = fpl.Figure() + + kwargs = dict() + for kwarg in ["sizes"]: + if locals()[kwarg] is not None: + # add to dict of arguments that will be passed + kwargs[kwarg] = locals()[kwarg] + + data = generate_positions_spiral_data("xy") + + graphic = fig[0, 0].add_scatter(data=data, **kwargs) + + assert isinstance(graphic.sizes, PointsSizesFeature) + assert isinstance(graphic._sizes, PointsSizesFeature) + assert len(data) == len(graphic.sizes) + + if sizes is None: + sizes = 1 # default sizes + + npt.assert_almost_equal(graphic.sizes.value, sizes) + npt.assert_almost_equal( + graphic.world_object.geometry.sizes.data, graphic.sizes.value + ) + + +@pytest.mark.parametrize("sizes", [None, 5.0]) +@pytest.mark.parametrize("uniform_size", [True]) +def test_uniform_size(sizes, uniform_size): + fig = fpl.Figure() + + kwargs = dict() + for kwarg in ["sizes", "uniform_size"]: + if locals()[kwarg] is not None: + # add to dict of arguments that will be passed + kwargs[kwarg] = locals()[kwarg] + + data = generate_positions_spiral_data("xy") + + graphic = fig[0, 0].add_scatter(data=data, **kwargs) + + assert isinstance(graphic.sizes, (float, int)) + assert isinstance(graphic._sizes, UniformSize) + + if sizes is None: + sizes = 1 # default sizes + + npt.assert_almost_equal(graphic.sizes, sizes) + npt.assert_almost_equal(graphic.world_object.material.size, sizes) + + # test changing size + graphic.sizes = 10.0 + assert isinstance(graphic.sizes, float) + assert isinstance(graphic._sizes, UniformSize) + assert graphic.sizes == 10.0 + + +@pytest.mark.parametrize("thickness", [None, 0.5, 5.0]) +def test_thickness(thickness): + fig = fpl.Figure() + + kwargs = dict() + for kwarg in ["thickness"]: + if locals()[kwarg] is not None: + # add to dict of arguments that will be passed + kwargs[kwarg] = locals()[kwarg] + + data = generate_positions_spiral_data("xy") + + graphic = fig[0, 0].add_line(data=data, **kwargs) + + if thickness is None: + thickness = 2.0 # default thickness + + assert isinstance(graphic._thickness, Thickness) + + assert graphic.thickness == thickness + assert graphic.world_object.material.thickness == thickness + + if thickness == 0.5: + assert isinstance(graphic.world_object.material, pygfx.LineThinMaterial) + + else: + assert isinstance(graphic.world_object.material, pygfx.LineMaterial) diff --git a/tests/test_sizes_buffer_manager.py b/tests/test_sizes_buffer_manager.py new file mode 100644 index 000000000..0b34f9588 --- /dev/null +++ b/tests/test_sizes_buffer_manager.py @@ -0,0 +1,76 @@ +import numpy as np +from numpy import testing as npt +import pytest + +from fastplotlib.graphics._features import PointsSizesFeature +from .utils import generate_slice_indices, assert_pending_uploads + + +def generate_data(input_type: str) -> np.ndarray | float: + """ + Point sizes varying with a sine wave + + Parameters + ---------- + input_type: str + one of "sine", "cosine", or "float" + """ + if input_type == "float": + return 10.0 + xs = np.linspace(0, 10 * np.pi, 10) + + if input_type == "sine": + return np.abs(np.sin(xs)).astype(np.float32) + + if input_type == "cosine": + return np.abs(np.cos(xs)).astype(np.float32) + + +@pytest.mark.parametrize("data", [generate_data(v) for v in ["float", "sine"]]) +def test_create_buffer(data): + sizes = PointsSizesFeature(data, n_datapoints=10) + + if isinstance(data, float): + npt.assert_almost_equal(sizes[:], generate_data("float")) + + elif isinstance(data, np.ndarray): + npt.assert_almost_equal(sizes[:], generate_data("sine")) + + +@pytest.mark.parametrize( + "slice_method", [generate_slice_indices(i) for i in range(0, 16)] +) +@pytest.mark.parametrize("user_input", ["float", "cosine"]) +def test_slice(slice_method: dict, user_input: str): + data = generate_data("sine") + + s = slice_method["slice"] + indices = slice_method["indices"] + offset = slice_method["offset"] + size = slice_method["size"] + others = slice_method["others"] + + sizes = PointsSizesFeature(data, n_datapoints=10) + + # TODO: placeholder until I make a testing figure where we draw frames only on call + sizes.buffer._gfx_pending_uploads.clear() + + match user_input: + case "float": + sizes[s] = 20.0 + truth = np.full(len(indices), 20.0) + npt.assert_almost_equal(sizes[s], truth) + npt.assert_almost_equal(sizes[indices], truth) + # make sure other sizes not modified + npt.assert_almost_equal(sizes[others], data[others]) + + case "cosine": + cosine = generate_data("cosine") + sizes[s] = cosine[s] + npt.assert_almost_equal(sizes[s], cosine[s]) + npt.assert_almost_equal(sizes[indices], cosine[s]) + # make sure other sizes not modified + npt.assert_almost_equal(sizes[others], data[others]) + + # make sure correct offset and size marked for pending upload + assert_pending_uploads(sizes.buffer, offset, size) diff --git a/tests/test_text_graphic.py b/tests/test_text_graphic.py new file mode 100644 index 000000000..a13dfe690 --- /dev/null +++ b/tests/test_text_graphic.py @@ -0,0 +1,101 @@ +from numpy import testing as npt + +import fastplotlib as fpl +from fastplotlib.graphics._features import ( + FeatureEvent, + TextData, + FontSize, + TextFaceColor, + TextOutlineColor, + TextOutlineThickness, +) + +import pygfx + + +def test_create_graphic(): + fig = fpl.Figure() + data = "lorem ipsum" + text = fig[0, 0].add_text(data) + + assert isinstance(text, fpl.TextGraphic) + + assert isinstance(text._text, TextData) + assert text.text == data + + assert text.font_size == 14 + assert isinstance(text._font_size, FontSize) + assert text.world_object.geometry.font_size == 14 + + assert text.face_color == pygfx.Color("w") + assert isinstance(text._face_color, TextFaceColor) + assert text.world_object.material.color == pygfx.Color("w") + + assert text.outline_color == pygfx.Color("w") + assert isinstance(text._outline_color, TextOutlineColor) + assert text.world_object.material.outline_color == pygfx.Color("w") + + assert text.outline_thickness == 0 + assert isinstance(text._outline_thickness, TextOutlineThickness) + assert text.world_object.material.outline_thickness == 0 + + +EVENT_RETURN_VALUE: FeatureEvent = None + + +def event_handler(ev): + global EVENT_RETURN_VALUE + EVENT_RETURN_VALUE = ev + + +def check_event(graphic, feature, value): + global EVENT_RETURN_VALUE + assert isinstance(EVENT_RETURN_VALUE, FeatureEvent) + assert EVENT_RETURN_VALUE.type == feature + assert EVENT_RETURN_VALUE.graphic == graphic + assert EVENT_RETURN_VALUE.target == graphic.world_object + if isinstance(EVENT_RETURN_VALUE.info["value"], float): + # floating point error + npt.assert_almost_equal(EVENT_RETURN_VALUE.info["value"], value) + else: + assert EVENT_RETURN_VALUE.info["value"] == value + + +def test_text_changes_events(): + fig = fpl.Figure() + data = "lorem ipsum" + text = fig[0, 0].add_text(data) + + text.add_event_handler( + event_handler, + "text", + "font_size", + "face_color", + "outline_color", + "outline_thickness", + ) + + text.text = "bah" + assert text.text == "bah" + # TODO: seems like there isn't a way in pygfx to get the current text as a str? + check_event(graphic=text, feature="text", value="bah") + + text.font_size = 10.0 + assert text.font_size == 10.0 + assert text.world_object.geometry.font_size == 10 + check_event(text, "font_size", 10) + + text.face_color = "r" + assert text.face_color == pygfx.Color("r") + assert text.world_object.material.color == pygfx.Color("r") + check_event(text, "face_color", pygfx.Color("r")) + + text.outline_color = "b" + assert text.outline_color == pygfx.Color("b") + assert text.world_object.material.outline_color == pygfx.Color("b") + check_event(text, "outline_color", pygfx.Color("b")) + + text.outline_thickness = 0.3 + npt.assert_almost_equal(text.outline_thickness, 0.3) + npt.assert_almost_equal(text.world_object.material.outline_thickness, 0.3) + check_event(text, "outline_thickness", 0.3) diff --git a/tests/test_texture_array.py b/tests/test_texture_array.py new file mode 100644 index 000000000..5aecf49a5 --- /dev/null +++ b/tests/test_texture_array.py @@ -0,0 +1,230 @@ +import numpy as np +from numpy import testing as npt +import pytest + +import pygfx + +import fastplotlib as fpl +from fastplotlib.graphics._features import TextureArray, WGPU_MAX_TEXTURE_SIZE +from fastplotlib.graphics.image import _ImageTile + + +def make_data(n_rows: int, n_cols: int) -> np.ndarray: + """ + Makes a 2D array where the amplitude of the sine wave + is increasing along the y-direction (along rows), and + the wavelength is increasing along the x-axis (columns) + """ + xs = np.linspace(0, 1_000, n_cols) + + sine = np.sin(np.sqrt(xs)) + + return np.vstack([sine * i for i in range(n_rows)]).astype(np.float32) + + +def check_texture_array( + data: np.ndarray, + ta: TextureArray, + buffer_size: int, + buffer_shape: tuple[int, int], + row_indices_size: int, + col_indices_size: int, + row_indices_values: np.ndarray, + col_indices_values: np.ndarray, +): + + npt.assert_almost_equal(ta.value, data) + + assert ta.buffer.size == buffer_size + assert ta.buffer.shape == buffer_shape + + assert all([isinstance(texture, pygfx.Texture) for texture in ta.buffer.ravel()]) + + assert ta.row_indices.size == row_indices_size + assert ta.col_indices.size == col_indices_size + npt.assert_array_equal(ta.row_indices, row_indices_values) + npt.assert_array_equal(ta.col_indices, col_indices_values) + + # make sure chunking is correct + for texture, chunk_index, data_slice in ta: + assert ta.buffer[chunk_index] is texture + chunk_row, chunk_col = chunk_index + + data_row_start_index = chunk_row * WGPU_MAX_TEXTURE_SIZE + data_col_start_index = chunk_col * WGPU_MAX_TEXTURE_SIZE + + data_row_stop_index = min( + data.shape[0] - 1, data_row_start_index + WGPU_MAX_TEXTURE_SIZE + ) + data_col_stop_index = min( + data.shape[1] - 1, data_col_start_index + WGPU_MAX_TEXTURE_SIZE + ) + + row_slice = slice(data_row_start_index, data_row_stop_index) + col_slice = slice(data_col_start_index, data_col_stop_index) + + assert data_slice == (row_slice, col_slice) + + +def check_set_slice(data, ta, row_slice, col_slice): + ta[row_slice, col_slice] = 1 + npt.assert_almost_equal(ta[row_slice, col_slice], 1) + + # make sure other vals unchanged + npt.assert_almost_equal(ta[: row_slice.start], data[: row_slice.start]) + npt.assert_almost_equal(ta[row_slice.stop :], data[row_slice.stop :]) + npt.assert_almost_equal(ta[:, : col_slice.start], data[:, : col_slice.start]) + npt.assert_almost_equal(ta[:, col_slice.stop :], data[:, col_slice.stop :]) + + +def make_image_graphic(data) -> fpl.ImageGraphic: + fig = fpl.Figure() + return fig[0, 0].add_image(data) + + +def check_image_graphic(texture_array, graphic): + # make sure each ImageTile has the right texture + for (texture, chunk_index, data_slice), img in zip( + texture_array, graphic.world_object.children + ): + assert isinstance(img, _ImageTile) + assert img.geometry.grid is texture + assert img.world.x == data_slice[1].start + assert img.world.y == data_slice[0].start + + +@pytest.mark.parametrize("test_graphic", [False, True]) +def test_small_texture(test_graphic): + # tests TextureArray with dims that requires only 1 texture + data = make_data(1_000, 1_000) + + if test_graphic: + graphic = make_image_graphic(data) + ta = graphic.data + else: + ta = TextureArray(data) + + check_texture_array( + data=data, + ta=ta, + buffer_size=1, + buffer_shape=(1, 1), + row_indices_size=1, + col_indices_size=1, + row_indices_values=np.array([0]), + col_indices_values=np.array([0]), + ) + + if test_graphic: + check_image_graphic(ta, graphic) + + check_set_slice(data, ta, slice(50, 200), slice(600, 800)) + + +@pytest.mark.parametrize("test_graphic", [False, True]) +def test_texture_at_limit(test_graphic): + # tests TextureArray with data that is 8192 x 8192 + data = make_data(WGPU_MAX_TEXTURE_SIZE, WGPU_MAX_TEXTURE_SIZE) + + if test_graphic: + graphic = make_image_graphic(data) + ta = graphic.data + else: + ta = TextureArray(data) + + check_texture_array( + data, + ta=ta, + buffer_size=1, + buffer_shape=(1, 1), + row_indices_size=1, + col_indices_size=1, + row_indices_values=np.array([0]), + col_indices_values=np.array([0]), + ) + + if test_graphic: + check_image_graphic(ta, graphic) + + check_set_slice(data, ta, slice(5000, 8000), slice(2000, 3000)) + + +@pytest.mark.parametrize("test_graphic", [False, True]) +def test_wide(test_graphic): + data = make_data(10_000, 20_000) + + if test_graphic: + graphic = make_image_graphic(data) + ta = graphic.data + else: + ta = TextureArray(data) + + check_texture_array( + data, + ta=ta, + buffer_size=6, + buffer_shape=(2, 3), + row_indices_size=2, + col_indices_size=3, + row_indices_values=np.array([0, 8192]), + col_indices_values=np.array([0, 8192, 16384]), + ) + + if test_graphic: + check_image_graphic(ta, graphic) + + check_set_slice(data, ta, slice(6_000, 9_000), slice(12_000, 18_000)) + + +@pytest.mark.parametrize("test_graphic", [False, True]) +def test_tall(test_graphic): + data = make_data(20_000, 10_000) + + if test_graphic: + graphic = make_image_graphic(data) + ta = graphic.data + else: + ta = TextureArray(data) + + check_texture_array( + data, + ta=ta, + buffer_size=6, + buffer_shape=(3, 2), + row_indices_size=3, + col_indices_size=2, + row_indices_values=np.array([0, 8192, 16384]), + col_indices_values=np.array([0, 8192]), + ) + + if test_graphic: + check_image_graphic(ta, graphic) + + check_set_slice(data, ta, slice(12_000, 18_000), slice(6_000, 9_000)) + + +@pytest.mark.parametrize("test_graphic", [False, True]) +def test_square(test_graphic): + data = make_data(20_000, 20_000) + + if test_graphic: + graphic = make_image_graphic(data) + ta = graphic.data + else: + ta = TextureArray(data) + + check_texture_array( + data, + ta=ta, + buffer_size=9, + buffer_shape=(3, 3), + row_indices_size=3, + col_indices_size=3, + row_indices_values=np.array([0, 8192, 16384]), + col_indices_values=np.array([0, 8192, 16384]), + ) + + if test_graphic: + check_image_graphic(ta, graphic) + + check_set_slice(data, ta, slice(12_000, 18_000), slice(16_000, 19_000)) diff --git a/tests/utils.py b/tests/utils.py new file mode 100644 index 000000000..6a25968e1 --- /dev/null +++ b/tests/utils.py @@ -0,0 +1,185 @@ +import numpy as np + +import pygfx + + +def generate_slice_indices(kind: int): + n_elements = 10 + a = np.arange(n_elements) + + match kind: + case 0: + # simplest, just int + s = 2 + indices = [2] + + case 1: + # everything, [:] + s = slice(None, None, None) + indices = list(range(10)) + + case 2: + # positive continuous range, [1:5] + s = slice(1, 5, None) + indices = [1, 2, 3, 4] + + case 3: + # positive stepped range, [2:8:2] + s = slice(2, 8, 2) + indices = [2, 4, 6] + + case 4: + # negative continuous range, [-5:] + s = slice(-5, None, None) + indices = [5, 6, 7, 8, 9] + + case 5: + # negative backwards, [-5::-1] + s = slice(-5, None, -1) + indices = [5, 4, 3, 2, 1, 0] + + case 5: + # negative backwards stepped, [-5::-2] + s = slice(-5, None, -2) + indices = [5, 3, 1] + + case 6: + # negative stepped forward[-5::2] + s = slice(-5, None, 2) + indices = [5, 7, 9] + + case 7: + # both negative, [-8:-2] + s = slice(-8, -2, None) + indices = [2, 3, 4, 5, 6, 7] + + case 8: + # both negative and stepped, [-8:2:2] + s = slice(-8, -2, 2) + indices = [2, 4, 6] + + case 9: + # positive, negative, negative, [8:-9:-2] + s = slice(8, -9, -2) + indices = [8, 6, 4, 2] + + case 10: + # only stepped forward, [::2] + s = slice(None, None, 2) + indices = [0, 2, 4, 6, 8] + + case 11: + # only stepped backward, [::-3] + s = slice(None, None, -3) + indices = [9, 6, 3, 0] + + case 12: + # list indices + s = [2, 5, 9] + indices = [2, 5, 9] + + case 13: + # bool indices + s = a > 5 + indices = [6, 7, 8, 9] + + case 14: + # list indices with negatives + s = [1, 4, -2] + indices = [1, 4, 8] + + case 15: + # array indices + s = np.array([1, 4, -7, 9]) + indices = [1, 4, 3, 9] + + others = [i for i in a if i not in indices] + + offset, size = (min(indices), np.ptp(indices) + 1) + + return { + "slice": s, + "indices": indices, + "others": others, + "offset": offset, + "size": size, + } + + +def assert_pending_uploads(buffer: pygfx.Buffer, offset: int, size: int): + upload_offset, upload_size = buffer._gfx_pending_uploads[-1] + # sometimes when slicing with step, it will over-estimate offset + # but it overestimates to upload 1 extra point so it's fine + assert (upload_offset == offset) or (upload_offset == offset - 1) + + # sometimes when slicing with step, it will over-estimate size + # but it overestimates to upload 1 extra point so it's fine + assert (upload_size == size) or (upload_size == size + 1) + + +def generate_positions_spiral_data(inputs: str) -> np.ndarray: + """ + Generates a spiral/spring + + Only 10 points so a very pointy spiral but easier to spot changes :D + """ + xs = np.linspace(0, 10 * np.pi, 10) + ys = np.sin(xs) + zs = np.cos(xs) + + match inputs: + case "y": + data = ys + + case "xy": + data = np.column_stack([xs, ys]) + + case "xyz": + data = np.column_stack([xs, ys, zs]) + + return data.astype(np.float32) + + +def generate_color_inputs( + name: str, +) -> list[str, np.ndarray, list, tuple] | list[str, np.ndarray]: + if name == "multi": + s = [ + "r", + "g", + "b", + "cyan", + "magenta", + "green", + "yellow", + "white", + "purple", + "orange", + ] + array = np.vstack([pygfx.Color(c) for c in s]) + return [s, array] + + color = pygfx.Color(name) + + s = name + a = np.array(color) + l = list(color) + t = tuple(color) + + return [s, a, l, t] + + +MULTI_COLORS_TRUTH = np.array( + [ + [1.0, 0.0, 0.0, 1.0], + [0.0, 1.0, 0.0, 1.0], + [0.0, 0.0, 1.0, 1.0], + [0.0, 1.0, 1.0, 1.0], + [1.0, 0.0, 1.0, 1.0], + [0.0, 0.501960813999176, 0.0, 1.0], + [1.0, 1.0, 0.0, 1.0], + [1.0, 1.0, 1.0, 1.0], + [0.501960813999176, 0.0, 0.501960813999176, 1.0], + [1.0, 0.6470588445663452, 0.0, 1.0], + ] +) From 02a851532c91c82fb6b5117d4287fd598b2f4d41 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Mon, 17 Jun 2024 21:35:23 -0400 Subject: [PATCH 075/185] numpy v2 and wgpu v0.16.0 (#524) --- docs/source/user_guide/gpu.rst | 6 ++- .../notebooks/linear_region_selector.ipynb | 40 ------------------- examples/tests/testutils.py | 2 +- fastplotlib/graphics/line_collection.py | 4 +- fastplotlib/legends/legend.py | 4 +- fastplotlib/utils/gui.py | 4 +- fastplotlib/widgets/histogram_lut.py | 2 +- setup.py | 2 +- 8 files changed, 13 insertions(+), 51 deletions(-) diff --git a/docs/source/user_guide/gpu.rst b/docs/source/user_guide/gpu.rst index 3f4ff4bb4..f4ef89a0c 100644 --- a/docs/source/user_guide/gpu.rst +++ b/docs/source/user_guide/gpu.rst @@ -59,7 +59,7 @@ You can get more detailed info on each adapter like this:: import pprint for a in fpl.enumerate_adapters(): - pprint.pprint(a.request_adapter_info()) + pprint.pprint(a.info) General description of the fields: * vendor: GPU manufacturer @@ -265,7 +265,9 @@ You can select an adapter by passing one of the ``wgpu.GPUAdapter`` instances re to ``fpl.select_adapter()``:: # get info or summary of all adapters to pick an adapter - print([a.request_adapter_info() for a in fpl.enumerate_adapters()]) + import pprint + for a in fpl.enumerate_adapters(): + pprint.pprint(a.info) # example, pick adapter at index 2 chosen_gpu = fpl.enumerate_adapters()[2] diff --git a/examples/notebooks/linear_region_selector.ipynb b/examples/notebooks/linear_region_selector.ipynb index 57a72bdec..74b304a35 100644 --- a/examples/notebooks/linear_region_selector.ipynb +++ b/examples/notebooks/linear_region_selector.ipynb @@ -93,46 +93,6 @@ "fig.show(maintain_aspect=False)" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "2f29e913-c4f8-44a6-8692-eb14436849a5", - "metadata": {}, - "outputs": [], - "source": [ - "sine_graphic_x.data[:, 1].ptp()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1947a477-5dd2-4df9-aecd-6967c6ab45fe", - "metadata": {}, - "outputs": [], - "source": [ - "np.clip(-0.1, 0, 10)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "18e10277-6d5d-42fe-8715-1733efabefa0", - "metadata": {}, - "outputs": [], - "source": [ - "ls_y.selection" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8e9c42b9-60d2-4544-96c5-c8c6832b79e3", - "metadata": {}, - "outputs": [], - "source": [ - "ls_y.get_selected_indices()" - ] - }, { "cell_type": "code", "execution_count": null, diff --git a/examples/tests/testutils.py b/examples/tests/testutils.py index f62ae7602..1a95e249f 100644 --- a/examples/tests/testutils.py +++ b/examples/tests/testutils.py @@ -30,7 +30,7 @@ def get_wgpu_backend(): """ Query the configured wgpu backend driver. """ - code = "import wgpu.utils; info = wgpu.utils.get_default_device().adapter.request_adapter_info(); print(info['adapter_type'], info['backend_type'])" + code = "import wgpu.utils; info = wgpu.utils.get_default_device().adapter.info; print(info['adapter_type'], info['backend_type'])" result = subprocess.run( [ sys.executable, diff --git a/fastplotlib/graphics/line_collection.py b/fastplotlib/graphics/line_collection.py index 92aad56b2..4a55c1fb3 100644 --- a/fastplotlib/graphics/line_collection.py +++ b/fastplotlib/graphics/line_collection.py @@ -460,7 +460,7 @@ def _get_linear_selector_init_args(self, axis, padding): bounds = (xmin, value_25p) limits = (xmin, xmax) # size from orthogonal axis - size = bbox[:, 1].ptp() * 1.5 + size = np.ptp(bbox[:, 1]) * 1.5 # center on orthogonal axis center = bbox[:, 1].mean() @@ -472,7 +472,7 @@ def _get_linear_selector_init_args(self, axis, padding): bounds = (xmin, value_25p) limits = (xmin, xmax) - size = bbox[:, 0].ptp() * 1.5 + size = np.ptp(bbox[:, 0]) * 1.5 # center on orthogonal axis center = bbox[:, 0].mean() diff --git a/fastplotlib/legends/legend.py b/fastplotlib/legends/legend.py index b7e55f321..8ab3ddedb 100644 --- a/fastplotlib/legends/legend.py +++ b/fastplotlib/legends/legend.py @@ -244,7 +244,7 @@ def add_graphic(self, graphic: Graphic, label: str = None): # get width of widest LegendItem in previous column to add to x_pos offset for this column for item in prev_column_items: bbox = item.world_object.get_world_bounding_box() - width, height, depth = bbox.ptp(axis=0) + width, height, depth = np.ptp(bbox, axis=0) max_width = max(max_width, width) # x position offset for this new column @@ -278,7 +278,7 @@ def add_graphic(self, graphic: Graphic, label: str = None): def _reset_mesh_dims(self): bbox = self._legend_items_group.get_world_bounding_box() - width, height, _ = bbox.ptp(axis=0) + width, height, _ = np.ptp(bbox, axis=0) self._mesh.geometry.positions.data[mesh_masks.x_right] = width + 7 self._mesh.geometry.positions.data[mesh_masks.x_left] = -5 diff --git a/fastplotlib/utils/gui.py b/fastplotlib/utils/gui.py index 1941674ee..1f13c1406 100644 --- a/fastplotlib/utils/gui.py +++ b/fastplotlib/utils/gui.py @@ -60,9 +60,9 @@ def _notebook_print_banner(): # print logo and adapter info adapters = [a for a in wgpu.gpu.enumerate_adapters()] - adapters_info = [a.request_adapter_info() for a in adapters] + adapters_info = [a.info for a in adapters] - default_adapter_info = wgpu.gpu.request_adapter().request_adapter_info() + default_adapter_info = wgpu.gpu.request_adapter().info default_ix = adapters_info.index(default_adapter_info) if len(adapters) > 0: diff --git a/fastplotlib/widgets/histogram_lut.py b/fastplotlib/widgets/histogram_lut.py index a3edffcbd..02c21aa38 100644 --- a/fastplotlib/widgets/histogram_lut.py +++ b/fastplotlib/widgets/histogram_lut.py @@ -163,7 +163,7 @@ def _calculate_histogram(self, data): # used if data ptp <= 10 because event things get weird # with tiny world objects due to floating point error # so if ptp <= 10, scale up by a factor - self._scale_factor: int = max(1, 100 * int(10 / data_ss.ptp())) + self._scale_factor: int = max(1, 100 * int(10 / np.ptp(data_ss))) edges = edges * self._scale_factor diff --git a/setup.py b/setup.py index 3ba77201d..d75edf956 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ install_requires = [ "numpy>=1.23.0", - "wgpu>=0.15.1", + "wgpu>=0.16.0", "pygfx>=0.1.14", ] From 852ffdc839df7a3b948dfaac9128a65840058928 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Tue, 18 Jun 2024 10:39:21 -0400 Subject: [PATCH 076/185] update example w.r.t. gfeatures refactor (#528) --- examples/notebooks/quickstart.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/notebooks/quickstart.ipynb b/examples/notebooks/quickstart.ipynb index 5c5040418..09317110d 100644 --- a/examples/notebooks/quickstart.ipynb +++ b/examples/notebooks/quickstart.ipynb @@ -825,7 +825,7 @@ " print(event_data)\n", "\n", "# Will print event data when the color changes\n", - "cosine_graphic.colors.add_event_handler(callback_func)" + "cosine_graphic.add_event_handler(callback_func, \"colors\")" ] }, { From ae9ccc297a61334051a33b320b7c79aaa8271784 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Tue, 18 Jun 2024 10:39:45 -0400 Subject: [PATCH 077/185] always cast to float32, try to convert other array types too (#527) --- fastplotlib/graphics/_features/_base.py | 32 ++++++------------------- 1 file changed, 7 insertions(+), 25 deletions(-) diff --git a/fastplotlib/graphics/_features/_base.py b/fastplotlib/graphics/_features/_base.py index 1b24d3b78..a57f8a453 100644 --- a/fastplotlib/graphics/_features/_base.py +++ b/fastplotlib/graphics/_features/_base.py @@ -12,36 +12,18 @@ WGPU_MAX_TEXTURE_SIZE = 8192 -supported_dtypes = [ - np.uint8, - np.uint16, - np.uint32, - np.int8, - np.int16, - np.int32, - np.float16, - np.float32, -] - - def to_gpu_supported_dtype(array): """ - If ``array`` is a numpy array, converts it to a supported type. GPUs don't support 64 bit dtypes. + convert input array to float32 numpy array """ if isinstance(array, np.ndarray): - if array.dtype not in supported_dtypes: - if np.issubdtype(array.dtype, np.integer): - warn(f"converting {array.dtype} array to int32") - return array.astype(np.int32) - elif np.issubdtype(array.dtype, np.floating): - warn(f"converting {array.dtype} array to float32") - return array.astype(np.float32, copy=False) - else: - raise TypeError( - "Unsupported type, supported array types must be int or float dtypes" - ) + if not array.dtype == np.float32: + warn(f"casting {array.dtype} array to float32") + return array.astype(np.float32) + return array - return array + # try to make a numpy array from it, should not copy, tested with jax arrays + return np.asarray(array).astype(np.float32) class FeatureEvent(pygfx.Event): From a07636c0b334ef93f83820de313cf3b5392edd3f Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Tue, 18 Jun 2024 13:55:51 -0400 Subject: [PATCH 078/185] Get nearest graphics (#519) * bugfix * center works, need to figure out what's wrong with edge * allow passing graphic collection to subset * allow passing graphic collection to subset * move get_nearest_graphics to utils * move stuff * test for get nearest * black * more black --- fastplotlib/graphics/line_collection.py | 2 +- fastplotlib/utils/__init__.py | 1 + fastplotlib/utils/_plot_helpers.py | 53 +++++++++++++++++++++++++ tests/test_plot_helpers.py | 33 +++++++++++++++ 4 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 fastplotlib/utils/_plot_helpers.py create mode 100644 tests/test_plot_helpers.py diff --git a/fastplotlib/graphics/line_collection.py b/fastplotlib/graphics/line_collection.py index 4a55c1fb3..01faa9164 100644 --- a/fastplotlib/graphics/line_collection.py +++ b/fastplotlib/graphics/line_collection.py @@ -81,7 +81,7 @@ def cmap(self, args): if isinstance(args, str): name = args transform, alpha = None, 1.0 - if len(args) == 1: + elif len(args) == 1: name = args[0] transform, alpha = None, None diff --git a/fastplotlib/utils/__init__.py b/fastplotlib/utils/__init__.py index e1eef6447..3ae83fb6b 100644 --- a/fastplotlib/utils/__init__.py +++ b/fastplotlib/utils/__init__.py @@ -3,6 +3,7 @@ from .functions import * from .gpu import enumerate_adapters, select_adapter, print_wgpu_report +from ._plot_helpers import * @dataclass diff --git a/fastplotlib/utils/_plot_helpers.py b/fastplotlib/utils/_plot_helpers.py new file mode 100644 index 000000000..ac0ff2cda --- /dev/null +++ b/fastplotlib/utils/_plot_helpers.py @@ -0,0 +1,53 @@ +from typing import Sequence + +import numpy as np + +from ..graphics._base import Graphic +from ..graphics._collection_base import GraphicCollection + + +def get_nearest_graphics( + pos: tuple[float, float] | tuple[float, float, float], + graphics: Sequence[Graphic] | GraphicCollection, +) -> np.ndarray[Graphic]: + """ + Returns the nearest ``graphics`` to the passed position ``pos`` in world space. + Uses the distance between ``pos`` and the center of the bounding sphere for each graphic. + + Parameters + ---------- + pos: (x, y) | (x, y, z) + position in world space, z-axis is ignored when calculating L2 norms if ``pos`` is 2D + + graphics: Sequence, i.e. array, list, tuple, etc. of Graphic | GraphicCollection + the graphics from which to return a sorted array of graphics in order of closest + to furthest graphic + + Returns + ------- + tuple[Graphic] + nearest graphics to ``pos`` in order + + """ + + if isinstance(graphics, GraphicCollection): + graphics = graphics.graphics + + if not all(isinstance(g, Graphic) for g in graphics): + raise TypeError("all elements of `graphics` must be Graphic objects") + + pos = np.asarray(pos) + + if pos.shape != (2,) or not pos.shape != (3,): + raise TypeError + + # get centers + centers = np.empty(shape=(len(graphics), len(pos))) + for i in range(centers.shape[0]): + centers[i] = graphics[i].world_object.get_world_bounding_sphere()[: len(pos)] + + # l2 + distances = np.linalg.norm(centers[:, : len(pos)] - pos, ord=2, axis=1) + + sort_indices = np.argsort(distances) + return np.asarray(graphics)[sort_indices] diff --git a/tests/test_plot_helpers.py b/tests/test_plot_helpers.py new file mode 100644 index 000000000..b4abe55fc --- /dev/null +++ b/tests/test_plot_helpers.py @@ -0,0 +1,33 @@ +import numpy as np +import fastplotlib as fpl + + +def make_circle(center, radius: float, n_points: int = 75) -> np.ndarray: + theta = np.linspace(0, 2 * np.pi, n_points) + xs = radius * np.sin(theta) + ys = radius * np.cos(theta) + + return np.column_stack([xs, ys]) + center + + +def test_get_nearest_graphics(): + circles = list() + + centers = [[0, 0], [0, 20], [20, 0], [20, 20]] + + for center in centers: + circles.append(make_circle(center, 5, n_points=75)) + + fig = fpl.Figure() + + lines = fig[0, 0].add_line_collection(circles, cmap="jet", thickness=5) + + fig[0, 0].add_scatter(np.array([[0, 12, 0]])) + + # check distances + nearest = fpl.utils.get_nearest_graphics((0, 12), lines) + assert nearest[0] is lines[1] # closest + assert nearest[1] is lines[0] + assert nearest[2] is lines[3] + assert nearest[3] is lines[2] # furthest + assert nearest[-1] is lines[2] From e08d1976081db4dff95b9a63fea8bc31d278e0df Mon Sep 17 00:00:00 2001 From: Caitlin Lewis <69729525+clewis7@users.noreply.github.com> Date: Tue, 18 Jun 2024 16:32:17 -0400 Subject: [PATCH 079/185] disable cmap property for RGB images (#529) --- fastplotlib/graphics/image.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/fastplotlib/graphics/image.py b/fastplotlib/graphics/image.py index d6576c12d..5805804c7 100644 --- a/fastplotlib/graphics/image.py +++ b/fastplotlib/graphics/image.py @@ -132,7 +132,11 @@ def __init__( self._vmin = ImageVmin(vmin) self._vmax = ImageVmax(vmax) - self._cmap = ImageCmap(cmap) + # set cmap to None for RGB images + if self._data.value.ndim == 3: + self._cmap = None + else: + self._cmap = ImageCmap(cmap) self._interpolation = ImageInterpolation(interpolation) self._cmap_interpolation = ImageCmapInterpolation(cmap_interpolation) @@ -189,10 +193,14 @@ def data(self, data): @property def cmap(self) -> str: """colormap name""" + if self.data.value.ndim == 3: + raise AttributeError("RGB images do not have a colormap property") return self._cmap.value @cmap.setter def cmap(self, name: str): + if self.data.value.ndim == 3: + raise AttributeError("RGB images do not have a colormap property") self._cmap.set_value(self, name) @property From bb0bb273ce0be1cdc1b56a1d0702b39d7cdbccc7 Mon Sep 17 00:00:00 2001 From: Caitlin Lewis <69729525+clewis7@users.noreply.github.com> Date: Tue, 18 Jun 2024 16:48:20 -0400 Subject: [PATCH 080/185] sphinx gallery (#509) * start sphinx gallery * relevant file changes * progress * edit setup.py to include missing dependencies * attempt to fix rtd * remove large heatmap example that breaks rtd * fix linter, attempt to fix quickstart bug * fix gridplot examples and gallery structure * remove nbsphinx * remove nbsphinx and quickstart.ipynb from docs * remove init files from examples * update examples and conf.py * add animation examples, edit conf.py, fix scatter examples * add sklearn to docs, example needs it * Update setup.py * Update setup.py * remove from gallery, kills readthedocs runner * update canvas size so thumbnails render better * change scatter examples, remove sklearn from docs dependencies * add iris scatter examples, exclude from screenshot tests * add more animation examples, add to tests * add simple multigraphic gridplot example * add simple event, fix multigraphic gridplot * Update line3d_animation.py * add simple event example * minor changes * replace screenshots and fix small bug * fix scatter examples for CI build * fix screenshots * add sklearn as docs dependency, include iris scatter examples in gallery * Update examples/desktop/misc/simple_event.py Co-authored-by: Kushal Kolar * update screenshots --------- Co-authored-by: Kushal Kolar --- docs/source/conf.py | 51 +++++++- docs/source/index.rst | 6 + examples/README.rst | 2 + .../{gridplot/__init__.py => README.rst} | 0 examples/desktop/gridplot/README.rst | 2 + examples/desktop/gridplot/gridplot.py | 23 ++-- .../desktop/gridplot/gridplot_non_square.py | 23 ++-- .../desktop/gridplot/multigraphic_gridplot.py | 116 ++++++++++++++++++ examples/desktop/heatmap/README.rst | 2 + examples/desktop/heatmap/__init__.py | 0 examples/desktop/heatmap/heatmap.py | 14 ++- examples/desktop/heatmap/heatmap_cmap.py | 15 ++- examples/desktop/heatmap/heatmap_data.py | 14 ++- examples/desktop/heatmap/heatmap_square.py | 11 +- examples/desktop/heatmap/heatmap_vmin_vmax.py | 14 ++- examples/desktop/heatmap/heatmap_wide.py | 11 +- examples/desktop/image/README.rst | 2 + examples/desktop/image/__init__.py | 0 examples/desktop/image/image_cmap.py | 18 +-- examples/desktop/image/image_rgb.py | 20 +-- examples/desktop/image/image_rgbvminvmax.py | 20 +-- examples/desktop/image/image_simple.py | 17 +-- examples/desktop/image/image_vminvmax.py | 20 +-- examples/desktop/image/image_widget.py | 7 +- examples/desktop/line/README.rst | 2 + examples/desktop/line/__init__.py | 0 examples/desktop/line/line.py | 24 ++-- examples/desktop/line/line_cmap.py | 19 +-- examples/desktop/line/line_colorslice.py | 26 ++-- examples/desktop/line/line_dataslice.py | 24 ++-- examples/desktop/line_collection/README.rst | 2 + examples/desktop/line_collection/__init__.py | 0 .../line_collection/line_collection.py | 16 ++- .../line_collection_cmap_values.py | 13 +- ...line_collection_cmap_values_qualitative.py | 17 +-- .../line_collection/line_collection_colors.py | 14 ++- .../line_collection_slicing.py | 10 +- .../desktop/line_collection/line_stack.py | 14 ++- .../desktop/line_collection/line_stack_3d.py | 17 +-- examples/desktop/misc/README.rst | 2 + examples/desktop/misc/cycle_animation.py | 62 ++++++++++ examples/desktop/misc/em_wave_animation.py | 105 ++++++++++++++++ examples/desktop/misc/image_animation.py | 38 ++++++ examples/desktop/misc/line3d_animation.py | 59 +++++++++ examples/desktop/misc/line_animation.py | 53 ++++++++ examples/desktop/misc/multiplot_animation.py | 49 ++++++++ examples/desktop/misc/scatter_animation.py | 59 +++++++++ examples/desktop/misc/simple_event.py | 56 +++++++++ examples/desktop/scatter/README.rst | 2 + examples/desktop/scatter/__init__.py | 0 examples/desktop/scatter/scatter.py | 42 +++++-- examples/desktop/scatter/scatter_cmap.py | 51 +++++--- examples/desktop/scatter/scatter_cmap_iris.py | 42 +++++++ .../desktop/scatter/scatter_colorslice.py | 54 +++++--- .../scatter/scatter_colorslice_iris.py | 42 +++++++ examples/desktop/scatter/scatter_dataslice.py | 42 ++++--- .../desktop/scatter/scatter_dataslice_iris.py | 41 +++++++ examples/desktop/scatter/scatter_iris.py | 38 ++++++ examples/desktop/scatter/scatter_size.py | 21 ++-- examples/desktop/screenshots/gridplot.png | 4 +- .../screenshots/gridplot_non_square.png | 4 +- examples/desktop/screenshots/heatmap.png | 4 +- examples/desktop/screenshots/heatmap_cmap.png | 4 +- examples/desktop/screenshots/heatmap_data.png | 4 +- .../desktop/screenshots/heatmap_square.png | 3 - .../desktop/screenshots/heatmap_vmin_vmax.png | 4 +- examples/desktop/screenshots/heatmap_wide.png | 3 - examples/desktop/screenshots/image_cmap.png | 4 +- examples/desktop/screenshots/image_rgb.png | 4 +- .../desktop/screenshots/image_rgbvminvmax.png | 4 +- examples/desktop/screenshots/image_simple.png | 4 +- .../desktop/screenshots/image_vminvmax.png | 4 +- examples/desktop/screenshots/line.png | 4 +- examples/desktop/screenshots/line_cmap.png | 4 +- .../desktop/screenshots/line_collection.png | 4 +- .../line_collection_cmap_values.png | 4 +- ...ine_collection_cmap_values_qualitative.png | 4 +- .../screenshots/line_collection_colors.png | 4 +- .../screenshots/line_collection_slicing.png | 4 +- .../desktop/screenshots/line_colorslice.png | 4 +- .../desktop/screenshots/line_dataslice.png | 4 +- .../screenshots/line_present_scaling.png | 3 + examples/desktop/screenshots/line_stack.png | 4 +- .../screenshots/multigraphic_gridplot.png | 3 + examples/desktop/screenshots/scatter.png | 4 +- examples/desktop/screenshots/scatter_cmap.png | 4 +- .../desktop/screenshots/scatter_cmap_iris.png | 3 + .../screenshots/scatter_colorslice.png | 4 +- .../screenshots/scatter_colorslice_iris.png | 3 + .../screenshots/scatter_dataslice_iris.png | 3 + examples/desktop/screenshots/scatter_iris.png | 3 + .../desktop/screenshots/scatter_present.png | 3 + examples/desktop/screenshots/scatter_size.png | 4 +- .../nb-image-widget-movie-single-0-reset.png | 4 +- .../nb-image-widget-movie-single-0.png | 4 +- .../nb-image-widget-movie-single-279.png | 4 +- ...e-widget-movie-single-50-window-max-33.png | 4 +- ...-widget-movie-single-50-window-mean-13.png | 4 +- ...-widget-movie-single-50-window-mean-33.png | 4 +- ...ge-widget-movie-single-50-window-reset.png | 4 +- .../nb-image-widget-movie-single-50.png | 4 +- .../notebooks/screenshots/nb-lines-3d.png | 4 +- .../nb-lines-cmap-jet-values-cosine.png | 4 +- .../screenshots/nb-lines-cmap-jet-values.png | 4 +- .../screenshots/nb-lines-cmap-jet.png | 4 +- .../screenshots/nb-lines-cmap-tab-10.png | 4 +- .../nb-lines-cmap-viridis-values.png | 4 +- .../screenshots/nb-lines-cmap-viridis.png | 4 +- .../screenshots/nb-lines-cmap-white.png | 4 +- .../notebooks/screenshots/nb-lines-colors.png | 4 +- .../notebooks/screenshots/nb-lines-data.png | 4 +- .../screenshots/nb-lines-underlay.png | 4 +- examples/notebooks/screenshots/nb-lines.png | 4 +- examples/tests/test_examples.py | 2 +- examples/tests/testutils.py | 3 +- fastplotlib/layouts/_figure.py | 5 - setup.py | 4 + 117 files changed, 1330 insertions(+), 355 deletions(-) create mode 100644 examples/README.rst rename examples/desktop/{gridplot/__init__.py => README.rst} (100%) create mode 100644 examples/desktop/gridplot/README.rst create mode 100644 examples/desktop/gridplot/multigraphic_gridplot.py create mode 100644 examples/desktop/heatmap/README.rst delete mode 100644 examples/desktop/heatmap/__init__.py create mode 100644 examples/desktop/image/README.rst delete mode 100644 examples/desktop/image/__init__.py create mode 100644 examples/desktop/line/README.rst delete mode 100644 examples/desktop/line/__init__.py create mode 100644 examples/desktop/line_collection/README.rst delete mode 100644 examples/desktop/line_collection/__init__.py create mode 100644 examples/desktop/misc/README.rst create mode 100644 examples/desktop/misc/cycle_animation.py create mode 100644 examples/desktop/misc/em_wave_animation.py create mode 100644 examples/desktop/misc/image_animation.py create mode 100644 examples/desktop/misc/line3d_animation.py create mode 100644 examples/desktop/misc/line_animation.py create mode 100644 examples/desktop/misc/multiplot_animation.py create mode 100644 examples/desktop/misc/scatter_animation.py create mode 100644 examples/desktop/misc/simple_event.py create mode 100644 examples/desktop/scatter/README.rst delete mode 100644 examples/desktop/scatter/__init__.py create mode 100644 examples/desktop/scatter/scatter_cmap_iris.py create mode 100644 examples/desktop/scatter/scatter_colorslice_iris.py create mode 100644 examples/desktop/scatter/scatter_dataslice_iris.py create mode 100644 examples/desktop/scatter/scatter_iris.py delete mode 100644 examples/desktop/screenshots/heatmap_square.png delete mode 100644 examples/desktop/screenshots/heatmap_wide.png create mode 100644 examples/desktop/screenshots/line_present_scaling.png create mode 100644 examples/desktop/screenshots/multigraphic_gridplot.png create mode 100644 examples/desktop/screenshots/scatter_cmap_iris.png create mode 100644 examples/desktop/screenshots/scatter_colorslice_iris.png create mode 100644 examples/desktop/screenshots/scatter_dataslice_iris.png create mode 100644 examples/desktop/screenshots/scatter_iris.png create mode 100644 examples/desktop/screenshots/scatter_present.png diff --git a/docs/source/conf.py b/docs/source/conf.py index 38133c901..4d94ec7e7 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -2,7 +2,24 @@ # # For the full list of built-in configuration values, see the documentation: # https://www.sphinx-doc.org/en/master/usage/configuration.html + +import os + +# need to force offscreen rendering before importing fpl +# otherwise fpl tries to select glfw canvas +os.environ["WGPU_FORCE_OFFSCREEN"] = "1" + import fastplotlib +from pygfx.utils.gallery_scraper import find_examples_for_gallery +from pathlib import Path +import sys +from sphinx_gallery.sorting import ExplicitOrder +import imageio.v3 as iio + +ROOT_DIR = Path(__file__).parents[1].parents[0] # repo root +EXAMPLES_DIR = Path.joinpath(ROOT_DIR, "examples", "desktop") + +sys.path.insert(0, str(ROOT_DIR)) # -- Project information ----------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information @@ -23,8 +40,40 @@ "sphinx.ext.viewcode", "sphinx_copybutton", "sphinx_design", + "sphinx_gallery.gen_gallery" ] +sphinx_gallery_conf = { + "gallery_dirs": "_gallery", + "backreferences_dir": "_gallery/backreferences", + "doc_module": ("fastplotlib",), + "image_scrapers": ("pygfx",), + "remove_config_comments": True, + "subsection_order": ExplicitOrder( + [ + "../../examples/desktop/image", + "../../examples/desktop/gridplot", + "../../examples/desktop/line", + "../../examples/desktop/line_collection", + "../../examples/desktop/scatter", + "../../examples/desktop/heatmap", + "../../examples/desktop/misc" + ] + ), + "ignore_pattern": r'__init__\.py', + "nested_sections": False, + "thumbnail_size": (250, 250) +} + +extra_conf = find_examples_for_gallery(EXAMPLES_DIR) +sphinx_gallery_conf.update(extra_conf) + +# download imageio examples for the gallery +iio.imread("imageio:clock.png") +iio.imread("imageio:astronaut.png") +iio.imread("imageio:coffee.png") +iio.imread("imageio:hubble_deep_field.png") + autosummary_generate = True templates_path = ["_templates"] @@ -56,7 +105,7 @@ } html_theme_options = { - "source_repository": "https://github.com/kushalkolar/fastplotlib", + "source_repository": "https://github.com/fastplotlib/fastplotlib", "source_branch": "main", "source_directory": "docs/", } diff --git a/docs/source/index.rst b/docs/source/index.rst index e99e38c52..6385c2aee 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -20,6 +20,12 @@ Welcome to fastplotlib's documentation! Utils GPU +.. toctree:: + :caption: Gallery + :maxdepth: 1 + + Gallery <_gallery/index> + Summary ======= diff --git a/examples/README.rst b/examples/README.rst new file mode 100644 index 000000000..138ec748b --- /dev/null +++ b/examples/README.rst @@ -0,0 +1,2 @@ +Examples that use fastplotlib +============================= diff --git a/examples/desktop/gridplot/__init__.py b/examples/desktop/README.rst similarity index 100% rename from examples/desktop/gridplot/__init__.py rename to examples/desktop/README.rst diff --git a/examples/desktop/gridplot/README.rst b/examples/desktop/gridplot/README.rst new file mode 100644 index 000000000..486e708e7 --- /dev/null +++ b/examples/desktop/gridplot/README.rst @@ -0,0 +1,2 @@ +GridPlot Examples +================= diff --git a/examples/desktop/gridplot/gridplot.py b/examples/desktop/gridplot/gridplot.py index 2669dd49b..044adae80 100644 --- a/examples/desktop/gridplot/gridplot.py +++ b/examples/desktop/gridplot/gridplot.py @@ -1,34 +1,37 @@ """ GridPlot Simple -============ +=============== + Example showing simple 2x2 GridPlot with Standard images from imageio. """ # test_example = true +# sphinx_gallery_pygfx_docs = 'screenshot' import fastplotlib as fpl import imageio.v3 as iio - -fig = fpl.Figure(shape=(2, 2)) +figure = fpl.Figure(shape=(2, 2)) im = iio.imread("imageio:clock.png") im2 = iio.imread("imageio:astronaut.png") im3 = iio.imread("imageio:coffee.png") im4 = iio.imread("imageio:hubble_deep_field.png") -fig[0, 0].add_image(data=im) -fig[0, 1].add_image(data=im2) -fig[1, 0].add_image(data=im3) -fig[1, 1].add_image(data=im4) +figure[0, 0].add_image(data=im) +figure[0, 1].add_image(data=im2) +figure[1, 0].add_image(data=im3) +figure[1, 1].add_image(data=im4) -fig.show() +figure.show() -fig.canvas.set_logical_size(800, 800) +figure.canvas.set_logical_size(700, 560) -for subplot in fig: +for subplot in figure: subplot.auto_scale() +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) fpl.run() diff --git a/examples/desktop/gridplot/gridplot_non_square.py b/examples/desktop/gridplot/gridplot_non_square.py index ea93096dc..c8a68cc85 100644 --- a/examples/desktop/gridplot/gridplot_non_square.py +++ b/examples/desktop/gridplot/gridplot_non_square.py @@ -1,32 +1,35 @@ """ -GridPlot Simple -============ +GridPlot Non-Square Example +=========================== + Example showing simple 2x2 GridPlot with Standard images from imageio. """ # test_example = true +# sphinx_gallery_pygfx_docs = 'screenshot' import fastplotlib as fpl import imageio.v3 as iio - -fig = fpl.Figure(shape=(2, 2), controller_ids="sync") +figure = fpl.Figure(shape=(2, 2), controller_ids="sync") im = iio.imread("imageio:clock.png") im2 = iio.imread("imageio:astronaut.png") im3 = iio.imread("imageio:coffee.png") -fig[0, 0].add_image(data=im) -fig[0, 1].add_image(data=im2) -fig[1, 0].add_image(data=im3) +figure[0, 0].add_image(data=im) +figure[0, 1].add_image(data=im2) +figure[1, 0].add_image(data=im3) -fig.show() +figure.show() -fig.canvas.set_logical_size(800, 800) +figure.canvas.set_logical_size(700, 560) -for subplot in fig: +for subplot in figure: subplot.auto_scale() +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) fpl.run() diff --git a/examples/desktop/gridplot/multigraphic_gridplot.py b/examples/desktop/gridplot/multigraphic_gridplot.py new file mode 100644 index 000000000..3d8dbe4a8 --- /dev/null +++ b/examples/desktop/gridplot/multigraphic_gridplot.py @@ -0,0 +1,116 @@ +""" +Multi-Graphic GridPlot +====================== + +Example showing a Figure with multiple subplots and multiple graphic types. +""" + +# test_example = true +# sphinx_gallery_pygfx_docs = 'screenshot' + +import fastplotlib as fpl +import numpy as np +import imageio.v3 as iio +from itertools import product + +# define figure +figure = fpl.Figure(shape=(2, 2), names=[["image-overlay", "circles"], ["line-stack", "scatter"]]) + +img = iio.imread("imageio:coffee.png") + +# add image to subplot +figure["image-overlay"].add_image(data=img) + +# generate overlay + +# empty array for overlay, shape is [nrows, ncols, RGBA] +overlay = np.zeros(shape=(*img.shape[:2], 4), dtype=np.float32) + +# set the blue values of some pixels with an alpha > 1 +overlay[img[:, :, -1] > 200] = np.array([0.0, 0.0, 1.0, 0.6]).astype(np.float32) + +# add overlay to image +figure["image-overlay"].add_image(data=overlay) + +# generate some circles +def make_circle(center, radius: float, n_points: int = 75) -> np.ndarray: + theta = np.linspace(0, 2 * np.pi, n_points) + xs = radius * np.sin(theta) + ys = radius * np.cos(theta) + + return np.column_stack([xs, ys]) + center + + +spatial_dims = (50, 50) + +# this makes 16 circles, so we can create 16 cmap values, so it will use these values to set the +# color of the line based by using the cmap as a LUT with the corresponding cmap_transform +circles = list() +for center in product(range(0, spatial_dims[0], 15), range(0, spatial_dims[1], 15)): + circles.append(make_circle(center, 5, n_points=75)) + +# things like class labels, cluster labels, etc. +cmap_transform = [ + 0, 1, 1, 2, + 0, 0, 1, 1, + 2, 2, 8, 3, + 1, 9, 1, 5 +] + +# add an image to overlay the circles on +img2 = np.ones((60, 60)) + +figure["circles"].add_image(data=img2) + +# add the circles to the figure +figure["circles"].add_line_collection( + circles, + cmap="tab10", + cmap_transform=cmap_transform, + thickness=3, + alpha=0.5, + name="circles-graphic" +) + +# move the circles graphic so that it is centered over the image +figure["circles"]["circles-graphic"].offset = np.array([7, 7, 2]) + +# generate some sine data +# linspace, create 100 evenly spaced x values from -10 to 10 +xs = np.linspace(-10, 10, 100) +# sine wave +ys = np.sin(xs) +sine = np.dstack([xs, ys])[0] + +# make 10 identical waves +sine_waves = 10 * [sine] + +# add the line stack to the figure +figure["line-stack"].add_line_stack(data=sine_waves, cmap="Wistia", separation=1) + +figure["line-stack"].auto_scale(maintain_aspect=True) + +# generate some scatter data +# create a gaussian cloud of 500 points +n_points = 500 + +mean = [0, 0] # mean of the Gaussian distribution +covariance = [[1, 0], [0, 1]] # covariance matrix + +gaussian_cloud = np.random.multivariate_normal(mean, covariance, n_points) +gaussian_cloud2 = np.random.multivariate_normal(mean, covariance, n_points) + +# add the scatter graphics to the figure +figure["scatter"].add_scatter(data=gaussian_cloud, sizes=1, cmap="jet") +figure["scatter"].add_scatter(data=gaussian_cloud2, colors="r", sizes=1) + +figure.show() + +figure.canvas.set_logical_size(700, 560) + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.run() + diff --git a/examples/desktop/heatmap/README.rst b/examples/desktop/heatmap/README.rst new file mode 100644 index 000000000..64294f46f --- /dev/null +++ b/examples/desktop/heatmap/README.rst @@ -0,0 +1,2 @@ +Heatmap Examples +================ diff --git a/examples/desktop/heatmap/__init__.py b/examples/desktop/heatmap/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/examples/desktop/heatmap/heatmap.py b/examples/desktop/heatmap/heatmap.py index f3a1bf460..08b284749 100644 --- a/examples/desktop/heatmap/heatmap.py +++ b/examples/desktop/heatmap/heatmap.py @@ -5,12 +5,12 @@ """ # test_example = true +# sphinx_gallery_pygfx_docs = 'screenshot' import fastplotlib as fpl import numpy as np - -fig = fpl.Figure() +figure = fpl.Figure() xs = np.linspace(0, 1_000, 10_000, dtype=np.float32) @@ -19,14 +19,16 @@ data = np.vstack([sine * i for i in range(20_000)]) # plot the image data -img = fig[0, 0].add_image(data=data, name="heatmap") +img = figure[0, 0].add_image(data=data, name="heatmap") -fig.show() +figure.show() -fig.canvas.set_logical_size(1500, 1500) +figure.canvas.set_logical_size(700, 560) -fig[0, 0].auto_scale() +figure[0, 0].auto_scale() +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) fpl.run() diff --git a/examples/desktop/heatmap/heatmap_cmap.py b/examples/desktop/heatmap/heatmap_cmap.py index 39e697c93..f51981bed 100644 --- a/examples/desktop/heatmap/heatmap_cmap.py +++ b/examples/desktop/heatmap/heatmap_cmap.py @@ -4,13 +4,14 @@ Change the cmap of a heatmap """ + # test_example = false +# sphinx_gallery_pygfx_docs = 'hidden' import fastplotlib as fpl import numpy as np - -fig = fpl.Figure() +figure = fpl.Figure() xs = np.linspace(0, 1_000, 10_000, dtype=np.float32) @@ -19,16 +20,18 @@ data = np.vstack([sine * i for i in range(20_000)]) # plot the image data -img = fig[0, 0].add_image(data=data, name="heatmap") +img = figure[0, 0].add_image(data=data, name="heatmap") -fig.show() +figure.show() -fig.canvas.set_logical_size(1500, 1500) +figure.canvas.set_logical_size(700, 560) -fig[0, 0].auto_scale() +figure[0, 0].auto_scale() img.cmap = "viridis" +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) fpl.run() diff --git a/examples/desktop/heatmap/heatmap_data.py b/examples/desktop/heatmap/heatmap_data.py index 75ef3ce41..9334ea4d7 100644 --- a/examples/desktop/heatmap/heatmap_data.py +++ b/examples/desktop/heatmap/heatmap_data.py @@ -5,12 +5,12 @@ """ # test_example = false +# sphinx_gallery_pygfx_docs = 'hidden' import fastplotlib as fpl import numpy as np - -fig = fpl.Figure() +figure = fpl.Figure() xs = np.linspace(0, 1_000, 9_000, dtype=np.float32) @@ -19,18 +19,20 @@ data = np.vstack([sine * i for i in range(9_000)]) # plot the image data -img = fig[0, 0].add_image(data=data, name="heatmap") +img = figure[0, 0].add_image(data=data, name="heatmap") -fig.show() +figure.show() -fig.canvas.set_logical_size(1500, 1500) +figure.canvas.set_logical_size(700, 560) -fig[0, 0].auto_scale() +figure[0, 0].auto_scale() cosine = np.cos(np.sqrt(xs)[:3000]) # change first 2,000 rows and 3,000 columns img.data[:2_000, :3_000] = np.vstack([cosine * i * 4 for i in range(2_000)]) +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) fpl.run() diff --git a/examples/desktop/heatmap/heatmap_square.py b/examples/desktop/heatmap/heatmap_square.py index f776b74e1..51e71695a 100644 --- a/examples/desktop/heatmap/heatmap_square.py +++ b/examples/desktop/heatmap/heatmap_square.py @@ -5,12 +5,13 @@ """ # test_example = false +# sphinx_gallery_pygfx_docs = 'hidden' import fastplotlib as fpl import numpy as np -fig = fpl.Figure() +figure = fpl.Figure() xs = np.linspace(0, 1_000, 20_000, dtype=np.float32) @@ -19,14 +20,14 @@ data = np.vstack([sine * i for i in range(20_000)]) # plot the image data -img = fig[0, 0].add_image(data=data, name="heatmap") +img = figure[0, 0].add_image(data=data, name="heatmap") del data # data no longer needed after given to graphic -fig.show() +figure.show() -fig.canvas.set_logical_size(1500, 1500) +figure.canvas.set_logical_size(1500, 1500) -fig[0, 0].auto_scale() +figure[0, 0].auto_scale() if __name__ == "__main__": print(__doc__) diff --git a/examples/desktop/heatmap/heatmap_vmin_vmax.py b/examples/desktop/heatmap/heatmap_vmin_vmax.py index 75b6b7b68..45c960fd8 100644 --- a/examples/desktop/heatmap/heatmap_vmin_vmax.py +++ b/examples/desktop/heatmap/heatmap_vmin_vmax.py @@ -5,12 +5,12 @@ """ # test_example = false +# sphinx_gallery_pygfx_docs = 'hidden' import fastplotlib as fpl import numpy as np - -fig = fpl.Figure() +figure = fpl.Figure() xs = np.linspace(0, 1_000, 10_000, dtype=np.float32) @@ -19,17 +19,19 @@ data = np.vstack([sine * i for i in range(20_000)]) # plot the image data -img = fig[0, 0].add_image(data=data, name="heatmap") +img = figure[0, 0].add_image(data=data, name="heatmap") -fig.show() +figure.show() -fig.canvas.set_logical_size(1500, 1500) +figure.canvas.set_logical_size(700, 560) -fig[0, 0].auto_scale() +figure[0, 0].auto_scale() img.vmin = -5_000 img.vmax = 10_000 +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) fpl.run() diff --git a/examples/desktop/heatmap/heatmap_wide.py b/examples/desktop/heatmap/heatmap_wide.py index 251c25fa4..dccf531e2 100644 --- a/examples/desktop/heatmap/heatmap_wide.py +++ b/examples/desktop/heatmap/heatmap_wide.py @@ -5,12 +5,13 @@ """ # test_example = false +# sphinx_gallery_pygfx_docs = 'hidden' import fastplotlib as fpl import numpy as np -fig = fpl.Figure() +figure = fpl.Figure() xs = np.linspace(0, 1_000, 20_000, dtype=np.float32) @@ -19,13 +20,13 @@ data = np.vstack([sine * i for i in range(10_000)]) # plot the image data -img = fig[0, 0].add_image(data=data, name="heatmap") +img = figure[0, 0].add_image(data=data, name="heatmap") -fig.show() +figure.show() -fig.canvas.set_logical_size(1500, 1500) +figure.canvas.set_logical_size(1500, 1500) -fig[0, 0].auto_scale() +figure[0, 0].auto_scale() if __name__ == "__main__": print(__doc__) diff --git a/examples/desktop/image/README.rst b/examples/desktop/image/README.rst new file mode 100644 index 000000000..028e85ec5 --- /dev/null +++ b/examples/desktop/image/README.rst @@ -0,0 +1,2 @@ +Image Examples +============== diff --git a/examples/desktop/image/__init__.py b/examples/desktop/image/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/examples/desktop/image/image_cmap.py b/examples/desktop/image/image_cmap.py index bb8e9f9d8..c70af7346 100644 --- a/examples/desktop/image/image_cmap.py +++ b/examples/desktop/image/image_cmap.py @@ -1,29 +1,33 @@ """ -Simple Plot -============ +Image Colormap +============== + Example showing simple plot creation and subsequent cmap change with Standard image from imageio. """ # test_example = true +# sphinx_gallery_pygfx_docs = 'screenshot' import fastplotlib as fpl import imageio.v3 as iio im = iio.imread("imageio:camera.png") -fig = fpl.Figure() +figure = fpl.Figure() # plot the image data -image_graphic = fig[0, 0].add_image(data=im, name="random-image") +image_graphic = figure[0, 0].add_image(data=im, name="random-image") -fig.show() +figure.show() -fig.canvas.set_logical_size(800, 800) +figure.canvas.set_logical_size(700, 560) -fig[0, 0].auto_scale() +figure[0, 0].auto_scale() image_graphic.cmap = "viridis" +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) fpl.run() diff --git a/examples/desktop/image/image_rgb.py b/examples/desktop/image/image_rgb.py index ce7e151d0..951142fd7 100644 --- a/examples/desktop/image/image_rgb.py +++ b/examples/desktop/image/image_rgb.py @@ -1,29 +1,31 @@ """ -Simple Plot -============ +RGB Image +========= + Example showing the simple plot creation with 512 x 512 2D RGB image. """ # test_example = true +# sphinx_gallery_pygfx_docs = 'screenshot' import fastplotlib as fpl import imageio.v3 as iio - im = iio.imread("imageio:astronaut.png") -fig = fpl.Figure() +figure = fpl.Figure() # plot the image data -image_graphic = fig[0, 0].add_image(data=im, name="iio astronaut") - -fig.show() +image_graphic = figure[0, 0].add_image(data=im, name="iio astronaut") -fig.canvas.set_logical_size(800, 800) +figure.show() -fig[0, 0].auto_scale() +figure.canvas.set_logical_size(700, 560) +figure[0, 0].auto_scale() +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) fpl.run() diff --git a/examples/desktop/image/image_rgbvminvmax.py b/examples/desktop/image/image_rgbvminvmax.py index 56114e1e3..25d3904e8 100644 --- a/examples/desktop/image/image_rgbvminvmax.py +++ b/examples/desktop/image/image_rgbvminvmax.py @@ -1,32 +1,34 @@ """ -Simple Plot -============ +RGB Image Vmin/Vmax +=================== + Example showing the simple plot followed by changing the vmin/vmax with 512 x 512 2D RGB image. """ # test_example = true +# sphinx_gallery_pygfx_docs = 'screenshot' import fastplotlib as fpl import imageio.v3 as iio - im = iio.imread("imageio:astronaut.png") -fig = fpl.Figure() +figure = fpl.Figure() # plot the image data -image_graphic = fig[0, 0].add_image(data=im, name="iio astronaut") +image_graphic = figure[0, 0].add_image(data=im, name="iio astronaut") -fig.show() +figure.show() -fig.canvas.set_logical_size(800, 800) +figure.canvas.set_logical_size(700, 560) -fig[0, 0].auto_scale() +figure[0, 0].auto_scale() image_graphic.vmin = 0.5 image_graphic.vmax = 0.75 - +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) fpl.run() diff --git a/examples/desktop/image/image_simple.py b/examples/desktop/image/image_simple.py index a640974ed..dab5188a1 100644 --- a/examples/desktop/image/image_simple.py +++ b/examples/desktop/image/image_simple.py @@ -1,28 +1,31 @@ """ -Simple Plot +Simple Image ============ + Example showing the simple plot creation with Standard imageio image. """ # test_example = true +# sphinx_gallery_pygfx_docs = 'screenshot' import fastplotlib as fpl import imageio.v3 as iio - -fig = fpl.Figure() +figure = fpl.Figure() data = iio.imread("imageio:camera.png") # plot the image data -image_graphic = fig[0, 0].add_image(data=data, name="iio camera") +image_graphic = figure[0, 0].add_image(data=data, name="iio camera") -fig.show() +figure.show() -fig.canvas.set_logical_size(800, 800) +figure.canvas.set_logical_size(700, 560) -fig[0, 0].auto_scale() +figure[0, 0].auto_scale() +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) fpl.run() diff --git a/examples/desktop/image/image_vminvmax.py b/examples/desktop/image/image_vminvmax.py index d24d1f18c..d9e49b18e 100644 --- a/examples/desktop/image/image_vminvmax.py +++ b/examples/desktop/image/image_vminvmax.py @@ -1,32 +1,34 @@ """ -Simple Plot -============ +Image Vmin/Vmax +=============== + Example showing the simple plot creation followed by changing the vmin/vmax with Standard imageio image. """ # test_example = true +# sphinx_gallery_pygfx_docs = 'screenshot' import fastplotlib as fpl import imageio.v3 as iio - -fig = fpl.Figure() +figure = fpl.Figure() data = iio.imread("imageio:astronaut.png") # plot the image data -image_graphic = fig[0, 0].add_image(data=data, name="iio astronaut") +image_graphic = figure[0, 0].add_image(data=data, name="iio astronaut") -fig.show() +figure.show() -fig.canvas.set_logical_size(800, 800) +figure.canvas.set_logical_size(700, 560) -fig[0, 0].auto_scale() +figure[0, 0].auto_scale() image_graphic.vmin = 0.5 image_graphic.vmax = 0.75 - +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) fpl.run() diff --git a/examples/desktop/image/image_widget.py b/examples/desktop/image/image_widget.py index 80aafe0b1..de1d27de1 100644 --- a/examples/desktop/image/image_widget.py +++ b/examples/desktop/image/image_widget.py @@ -1,19 +1,22 @@ """ Image widget ============ + Example showing the image widget in action. When run in a notebook, or with the Qt GUI backend, sliders are also shown. """ +# sphinx_gallery_pygfx_docs = 'hidden' + import fastplotlib as fpl import imageio.v3 as iio # not a fastplotlib dependency, only used for examples - a = iio.imread("imageio:camera.png") iw = fpl.ImageWidget(data=a, cmap="viridis") iw.show() - +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) fpl.run() diff --git a/examples/desktop/line/README.rst b/examples/desktop/line/README.rst new file mode 100644 index 000000000..b9970c543 --- /dev/null +++ b/examples/desktop/line/README.rst @@ -0,0 +1,2 @@ +Line Examples +============= diff --git a/examples/desktop/line/__init__.py b/examples/desktop/line/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/examples/desktop/line/line.py b/examples/desktop/line/line.py index 56575a810..cd661da1e 100644 --- a/examples/desktop/line/line.py +++ b/examples/desktop/line/line.py @@ -1,16 +1,17 @@ """ -Line Plot -============ +Simple Line Plot +================ + Example showing cosine, sine, sinc lines. """ # test_example = true +# sphinx_gallery_pygfx_docs = 'screenshot' import fastplotlib as fpl import numpy as np - -fig = fpl.Figure() +figure = fpl.Figure() xs = np.linspace(-10, 10, 100) # sine wave @@ -26,22 +27,23 @@ ys = np.sinc(xs) * 3 + 8 sinc = np.dstack([xs, ys])[0] -sine_graphic = fig[0, 0].add_line(data=sine, thickness=5, colors="magenta") +sine_graphic = figure[0, 0].add_line(data=sine, thickness=5, colors="magenta") # you can also use colormaps for lines! -cosine_graphic = fig[0, 0].add_line(data=cosine, thickness=12, cmap="autumn") +cosine_graphic = figure[0, 0].add_line(data=cosine, thickness=12, cmap="autumn") # or a list of colors for each datapoint colors = ["r"] * 25 + ["purple"] * 25 + ["y"] * 25 + ["b"] * 25 -sinc_graphic = fig[0, 0].add_line(data=sinc, thickness=5, colors=colors) - -fig.show() +sinc_graphic = figure[0, 0].add_line(data=sinc, thickness=5, colors=colors) -fig.canvas.set_logical_size(800, 800) +figure.show() -fig[0, 0].auto_scale() +figure.canvas.set_logical_size(700, 560) +figure[0, 0].auto_scale() +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) fpl.run() diff --git a/examples/desktop/line/line_cmap.py b/examples/desktop/line/line_cmap.py index f18ceb201..5ffea6fef 100644 --- a/examples/desktop/line/line_cmap.py +++ b/examples/desktop/line/line_cmap.py @@ -1,16 +1,17 @@ """ -Line Plot -============ +Line Plot Colormap +================== + Example showing cosine, sine, sinc lines. """ # test_example = true +# sphinx_gallery_pygfx_docs = 'screenshot' import fastplotlib as fpl import numpy as np - -fig = fpl.Figure() +figure = fpl.Figure() xs = np.linspace(-10, 10, 100) # sine wave @@ -22,7 +23,7 @@ cosine = np.dstack([xs, ys])[0] # cmap_transform from an array, so the colors on the sine line will be based on the sine y-values -sine_graphic = fig[0, 0].add_line( +sine_graphic = figure[0, 0].add_line( data=sine, thickness=10, cmap="plasma", @@ -31,17 +32,19 @@ # qualitative colormaps, useful for cluster labels or other types of categorical labels labels = [0] * 25 + [5] * 10 + [1] * 35 + [2] * 30 -cosine_graphic = fig[0, 0].add_line( +cosine_graphic = figure[0, 0].add_line( data=cosine, thickness=10, cmap="tab10", cmap_transform=labels ) -fig.show() +figure.show() -fig.canvas.set_logical_size(800, 800) +figure.canvas.set_logical_size(700, 560) +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) fpl.run() diff --git a/examples/desktop/line/line_colorslice.py b/examples/desktop/line/line_colorslice.py index 28b877793..3d18d74b7 100644 --- a/examples/desktop/line/line_colorslice.py +++ b/examples/desktop/line/line_colorslice.py @@ -1,16 +1,17 @@ """ -Line Plot -========= +Line Plot Color Slicing +======================= + Example showing color slicing with cosine, sine, sinc lines. """ # test_example = true +# sphinx_gallery_pygfx_docs = 'screenshot' import fastplotlib as fpl import numpy as np - -fig = fpl.Figure() +figure = fpl.Figure() xs = np.linspace(-10, 10, 100) # sine wave @@ -26,14 +27,14 @@ ys = np.sinc(xs) * 3 sinc = np.column_stack([xs, ys]) -sine_graphic = fig[0, 0].add_line( +sine_graphic = figure[0, 0].add_line( data=sine, thickness=5, colors="magenta" ) # you can also use colormaps for lines! -cosine_graphic = fig[0, 0].add_line( +cosine_graphic = figure[0, 0].add_line( data=cosine, thickness=12, cmap="autumn", @@ -42,7 +43,7 @@ # or a list of colors for each datapoint colors = ["r"] * 25 + ["purple"] * 25 + ["y"] * 25 + ["b"] * 25 -sinc_graphic = fig[0, 0].add_line( +sinc_graphic = figure[0, 0].add_line( data=sinc, thickness=5, colors=colors, @@ -51,14 +52,14 @@ zeros = np.zeros(xs.size) zeros_data = np.column_stack([xs, zeros]) -zeros_graphic = fig[0, 0].add_line( +zeros_graphic = figure[0, 0].add_line( data=zeros_data, thickness=8, colors="w", offset=(0, 10, 0) ) -fig.show() +figure.show() # indexing of colors cosine_graphic.colors[:15] = "magenta" @@ -81,11 +82,12 @@ zeros_graphic.cmap[50:75] = "jet" zeros_graphic.cmap[75:] = "viridis" -fig.canvas.set_logical_size(800, 800) - -fig[0, 0].auto_scale() +figure.canvas.set_logical_size(700, 560) +figure[0, 0].auto_scale() +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) fpl.run() diff --git a/examples/desktop/line/line_dataslice.py b/examples/desktop/line/line_dataslice.py index c2c6b9d36..eac765c68 100644 --- a/examples/desktop/line/line_dataslice.py +++ b/examples/desktop/line/line_dataslice.py @@ -1,16 +1,17 @@ """ -Line Plot -============ +Line Plot Data Slicing +====================== + Example showing data slicing with cosine, sine, sinc lines. """ # test_example = true +# sphinx_gallery_pygfx_docs = 'screenshot' import fastplotlib as fpl import numpy as np - -fig = fpl.Figure() +figure = fpl.Figure() xs = np.linspace(-10, 10, 100) # sine wave @@ -26,16 +27,16 @@ ys = np.sinc(xs) * 3 + 8 sinc = np.dstack([xs, ys])[0] -sine_graphic = fig[0, 0].add_line(data=sine, thickness=5, colors="magenta") +sine_graphic = figure[0, 0].add_line(data=sine, thickness=5, colors="magenta") # you can also use colormaps for lines! -cosine_graphic = fig[0, 0].add_line(data=cosine, thickness=12, cmap="autumn") +cosine_graphic = figure[0, 0].add_line(data=cosine, thickness=12, cmap="autumn") # or a list of colors for each datapoint colors = ["r"] * 25 + ["purple"] * 25 + ["y"] * 25 + ["b"] * 25 -sinc_graphic = fig[0, 0].add_line(data=sinc, thickness=5, colors=colors) +sinc_graphic = figure[0, 0].add_line(data=sinc, thickness=5, colors=colors) -fig.show() +figure.show() cosine_graphic.data[10:50:5, :2] = sine[10:50:5] cosine_graphic.data[90:, 1] = 7 @@ -45,11 +46,12 @@ bool_key = [True, True, True, False, False] * 20 sinc_graphic.data[bool_key, 1] = 7 # y vals to 1 -fig.canvas.set_logical_size(800, 800) - -fig[0, 0].auto_scale() +figure.canvas.set_logical_size(700, 560) +figure[0, 0].auto_scale() +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) fpl.run() diff --git a/examples/desktop/line_collection/README.rst b/examples/desktop/line_collection/README.rst new file mode 100644 index 000000000..3dbe05f7f --- /dev/null +++ b/examples/desktop/line_collection/README.rst @@ -0,0 +1,2 @@ +LineCollection Examples +======================= diff --git a/examples/desktop/line_collection/__init__.py b/examples/desktop/line_collection/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/examples/desktop/line_collection/line_collection.py b/examples/desktop/line_collection/line_collection.py index db99e32ed..44b765319 100644 --- a/examples/desktop/line_collection/line_collection.py +++ b/examples/desktop/line_collection/line_collection.py @@ -1,10 +1,12 @@ """ -Line collection -=============== +Line Collection Simple +====================== + Example showing how to plot line collections """ # test_example = true +# sphinx_gallery_pygfx_docs = 'screenshot' from itertools import product import numpy as np @@ -27,14 +29,16 @@ def make_circle(center, radius: float, n_points: int = 75) -> np.ndarray: pos_xy = np.vstack(circles) -fig = fpl.Figure() +figure = fpl.Figure() -fig[0, 0].add_line_collection(circles, cmap="jet", thickness=5) +figure[0, 0].add_line_collection(circles, cmap="jet", thickness=5) -fig.show() +figure.show() -fig.canvas.set_logical_size(800, 800) +figure.canvas.set_logical_size(700, 560) +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) fpl.run() diff --git a/examples/desktop/line_collection/line_collection_cmap_values.py b/examples/desktop/line_collection/line_collection_cmap_values.py index 5ffc032e9..e94a161ad 100644 --- a/examples/desktop/line_collection/line_collection_cmap_values.py +++ b/examples/desktop/line_collection/line_collection_cmap_values.py @@ -1,16 +1,17 @@ """ Line collections quantitative cmap ================================== + Example showing a line collection with a quantitative cmap """ # test_example = true +# sphinx_gallery_pygfx_docs = 'screenshot' from itertools import product import numpy as np import fastplotlib as fpl - def make_circle(center, radius: float, n_points: int = 75) -> np.ndarray: theta = np.linspace(0, 2 * np.pi, n_points) xs = radius * np.sin(theta) @@ -33,16 +34,18 @@ def make_circle(center, radius: float, n_points: int = 75) -> np.ndarray: # highest values, lowest values, mid-high values, mid values cmap_values = [10] * 4 + [0] * 4 + [7] * 4 + [5] * 4 -fig = fpl.Figure() +figure = fpl.Figure() -fig[0, 0].add_line_collection( +figure[0, 0].add_line_collection( circles, cmap="bwr", cmap_transform=cmap_values, thickness=10 ) -fig.show() +figure.show() -fig.canvas.set_logical_size(800, 800) +figure.canvas.set_logical_size(700, 560) +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) fpl.run() diff --git a/examples/desktop/line_collection/line_collection_cmap_values_qualitative.py b/examples/desktop/line_collection/line_collection_cmap_values_qualitative.py index f96fd3aac..5f9ea0000 100644 --- a/examples/desktop/line_collection/line_collection_cmap_values_qualitative.py +++ b/examples/desktop/line_collection/line_collection_cmap_values_qualitative.py @@ -1,16 +1,17 @@ """ -Line collections qualitative cmaps -================================== +Line Collection Qualitative Colormap +==================================== + Example showing a line collection with a qualitative cmap """ # test_example = true +# sphinx_gallery_pygfx_docs = 'screenshot' from itertools import product import numpy as np import fastplotlib as fpl - def make_circle(center, radius: float, n_points: int = 75) -> np.ndarray: theta = np.linspace(0, 2 * np.pi, n_points) xs = radius * np.sin(theta) @@ -39,19 +40,21 @@ def make_circle(center, radius: float, n_points: int = 75) -> np.ndarray: 1, 1, 1, 5 ] -fig = fpl.Figure() +figure = fpl.Figure() -fig[0, 0].add_line_collection( +figure[0, 0].add_line_collection( circles, cmap="tab10", cmap_transform=cmap_values, thickness=10 ) -fig.show() +figure.show() -fig.canvas.set_logical_size(800, 800) +figure.canvas.set_logical_size(700, 560) +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) fpl.run() diff --git a/examples/desktop/line_collection/line_collection_colors.py b/examples/desktop/line_collection/line_collection_colors.py index 3ee561d8f..bf3e818cd 100644 --- a/examples/desktop/line_collection/line_collection_colors.py +++ b/examples/desktop/line_collection/line_collection_colors.py @@ -1,10 +1,12 @@ """ -Line collection colors +Line Collection Colors ====================== + Example showing one way ot setting colors for individual lines in a collection """ # test_example = true +# sphinx_gallery_pygfx_docs = 'screenshot' from itertools import product import numpy as np @@ -31,14 +33,16 @@ def make_circle(center, radius: float, n_points: int = 75) -> np.ndarray: # this will produce 16 circles so we will define 16 colors colors = ["blue"] * 4 + ["red"] * 4 + ["yellow"] * 4 + ["w"] * 4 -fig = fpl.Figure() +figure = fpl.Figure() -fig[0, 0].add_line_collection(circles, colors=colors, thickness=10) +figure[0, 0].add_line_collection(circles, colors=colors, thickness=10) -fig.show() +figure.show() -fig.canvas.set_logical_size(800, 800) +figure.canvas.set_logical_size(700, 560) +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) fpl.run() diff --git a/examples/desktop/line_collection/line_collection_slicing.py b/examples/desktop/line_collection/line_collection_slicing.py index 9eaebdd7e..a7525f7ba 100644 --- a/examples/desktop/line_collection/line_collection_slicing.py +++ b/examples/desktop/line_collection/line_collection_slicing.py @@ -1,10 +1,12 @@ """ Line collection slicing ======================= + Example showing how to slice a line collection """ # test_example = true +# sphinx_gallery_pygfx_docs = 'screenshot' import numpy as np import fastplotlib as fpl @@ -18,9 +20,9 @@ multi_data = np.stack([data] * 15) -fig = fpl.Figure() +figure = fpl.Figure() -lines = fig[0, 0].add_line_stack( +lines = figure[0, 0].add_line_stack( multi_data, thickness=[2, 10, 2, 5, 5, 5, 8, 8, 8, 9, 3, 3, 3, 4, 4], separation=1, @@ -59,9 +61,9 @@ lines[::2].colors[::5] = "magenta" # set every 5th point of every other line to magenta lines[3:6].colors[50:, -1] = 0.6 # set half the points alpha to 0.6 -fig.show(maintain_aspect=False) +figure.show(maintain_aspect=False) -fig.canvas.set_logical_size(900, 600) +figure.canvas.set_logical_size(700, 580) if __name__ == "__main__": print(__doc__) diff --git a/examples/desktop/line_collection/line_stack.py b/examples/desktop/line_collection/line_stack.py index 676e6e5c2..e7f7125e1 100644 --- a/examples/desktop/line_collection/line_stack.py +++ b/examples/desktop/line_collection/line_stack.py @@ -1,10 +1,12 @@ """ -Line stack +Line Stack ========== + Example showing how to plot a stack of lines """ # test_example = true +# sphinx_gallery_pygfx_docs = 'screenshot' import numpy as np import fastplotlib as fpl @@ -17,19 +19,21 @@ data = np.column_stack([xs, ys]) multi_data = np.stack([data] * 10) -fig = fpl.Figure() +figure = fpl.Figure() -line_stack = fig[0, 0].add_line_stack( +line_stack = figure[0, 0].add_line_stack( multi_data, # shape: (10, 100, 2), i.e. [n_lines, n_points, xy] cmap="jet", # applied along n_lines thickness=5, separation=1, # spacing between lines along the separation axis, default separation along "y" axis ) -fig.show(maintain_aspect=False) +figure.show(maintain_aspect=False) -fig.canvas.set_logical_size(900, 600) +figure.canvas.set_logical_size(700, 560) +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) fpl.run() diff --git a/examples/desktop/line_collection/line_stack_3d.py b/examples/desktop/line_collection/line_stack_3d.py index 41914e2d2..314a97ff2 100644 --- a/examples/desktop/line_collection/line_stack_3d.py +++ b/examples/desktop/line_collection/line_stack_3d.py @@ -1,10 +1,12 @@ """ Line stack 3D ============= + Example showing a 3D stack of lines with animations """ # test_example = false +# sphinx_gallery_pygfx_docs = 'animate' import numpy as np import fastplotlib as fpl @@ -19,9 +21,9 @@ multi_data = np.stack([data] * 10) # create figure to plot lines and use an orbit controller in 3D -fig = fpl.Figure(cameras="3d", controller_types="orbit") +figure = fpl.Figure(cameras="3d", controller_types="orbit") -line_stack = fig[0, 0].add_line_stack( +line_stack = figure[0, 0].add_line_stack( multi_data, # shape: (10, 100, 2), i.e. [n_lines, n_points, xy] cmap="jet", # applied along n_lines thickness=3, @@ -75,7 +77,7 @@ def animate_colors(subplot): colors_iteration += 1 -fig[0, 0].add_animations(animate_data, animate_colors) +figure[0, 0].add_animations(animate_data, animate_colors) # just a pre-saved camera state camera_state = { @@ -91,13 +93,14 @@ def animate_colors(subplot): "depth_range": None, } -fig.show(maintain_aspect=False) - -fig[0, 0].camera.set_state(camera_state) +figure.show(maintain_aspect=False) -fig.canvas.set_logical_size(500, 500) +figure[0, 0].camera.set_state(camera_state) +figure.canvas.set_logical_size(700, 560) +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) fpl.run() diff --git a/examples/desktop/misc/README.rst b/examples/desktop/misc/README.rst new file mode 100644 index 000000000..cc51fd686 --- /dev/null +++ b/examples/desktop/misc/README.rst @@ -0,0 +1,2 @@ +Other Examples +============== diff --git a/examples/desktop/misc/cycle_animation.py b/examples/desktop/misc/cycle_animation.py new file mode 100644 index 000000000..bb402a1f7 --- /dev/null +++ b/examples/desktop/misc/cycle_animation.py @@ -0,0 +1,62 @@ +""" +Scatter Animation Colors +======================== + +Example showing animation with a scatter plot. +""" + +# test_example = false +# sphinx_gallery_pygfx_docs = 'animate' + +import fastplotlib as fpl +import numpy as np + +# create a random distribution of 10,000 xyz coordinates +n_points = 10_000 + +# dimensions always have to be [n_points, xyz] +dims = (n_points, 3) + +clouds_offset = 15 + +# create some random clouds +normal = np.random.normal(size=dims, scale=5) +# stack the data into a single array +cloud = np.vstack( + [ + normal - clouds_offset, + normal, + normal + clouds_offset, + ] +) + +# color each of them separately +colors = ["yellow"] * n_points + ["cyan"] * n_points + ["magenta"] * n_points + +# create plot +figure = fpl.Figure() +subplot_scatter = figure[0, 0] +# use an alpha value since this will be a lot of points +scatter_graphic = subplot_scatter.add_scatter(data=cloud, sizes=3, colors=colors, alpha=0.6) + + +i = 0.05 +def cycle_colors(subplot): + global i + # cycle the red values + scatter_graphic.colors[n_points * 2:, 0] = np.abs(np.sin(i)) + scatter_graphic.colors[n_points * 2:, 1] = np.abs(np.sin(i + (np.pi / 4))) + scatter_graphic.colors[n_points * 2:, 2] = np.abs(np.cos(i)) + i += 0.05 + +subplot_scatter.add_animations(cycle_colors) + +figure.show() + +subplot_scatter.canvas.set_logical_size(700, 560) + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.run() \ No newline at end of file diff --git a/examples/desktop/misc/em_wave_animation.py b/examples/desktop/misc/em_wave_animation.py new file mode 100644 index 000000000..50ab27ed6 --- /dev/null +++ b/examples/desktop/misc/em_wave_animation.py @@ -0,0 +1,105 @@ +""" +Electromagnetic Wave Animation +============================== + +Example showing animation of an electromagnetic wave. +""" + +# test_example = false +# sphinx_gallery_pygfx_docs = 'animate' + +import fastplotlib as fpl +import numpy as np + +figure = fpl.Figure( + cameras="3d", + controller_types="orbit", + size=(700, 400) +) + +start, stop = 0, 4 * np.pi + +# let's define the x, y and z axes for each with direction of wave propogation along the z-axis +# electric field in the xz plane travelling along +zs = np.linspace(start, stop, 200) +e_ys = np.zeros(200) +e_xs = np.sin(zs) +electric = np.column_stack([e_xs, e_ys, zs]) + +# magnetic field in the yz plane +zs = np.linspace(start, stop, 200) +m_ys = np.sin(zs) +m_xs = np.zeros(200) +magnetic = np.column_stack([m_xs, m_ys, zs]) + +# add the lines +figure[0, 0].add_line(electric, colors="blue", thickness=2, name="e") +figure[0, 0].add_line(magnetic, colors="red", thickness=2, name="m") + +# draw vector line at every 10th position +electric_vectors = [np.array([[0, 0, z], [x, 0, z]]) for (x, z) in zip(e_xs[::10], zs[::10])] +magnetic_vectors = [np.array([[0, 0, z], [0, y, z]]) for (y, z) in zip(m_ys[::10], zs[::10])] + +# add as a line collection +figure[0, 0].add_line_collection(electric_vectors, colors="blue", thickness=1.5, name="e-vec") +figure[0, 0].add_line_collection(magnetic_vectors, colors="red", thickness=1.5, name="m-vec") +# note that the z_offset in `add_line_collection` is not data-related +# it is the z-offset for where to place the *graphic*, by default with Orthographic cameras (i.e. 2D views) +# it will increment by 1 for each line in the collection, we want to disable this so set z_position=0 + +# axes are a WIP, just draw a white line along z for now +z_axis = np.array([[0, 0, 0], [0, 0, stop]]) +figure[0, 0].add_line(z_axis, colors="w", thickness=1) + +# just a pre-saved camera state +state = { + 'position': np.array([-8.0 , 6.0, -2.0]), + 'rotation': np.array([0.09, 0.9 , 0.2, -0.5]), + 'scale': np.array([1., 1., 1.]), + 'reference_up': np.array([0., 1., 0.]), + 'fov': 50.0, + 'width': 12, + 'height': 12, + 'zoom': 1.35, + 'maintain_aspect': True, + 'depth_range': None +} + + +figure[0, 0].camera.set_state(state) + +figure.show() + +figure[0, 0].camera.zoom = 1.5 + +increment = np.pi * 4 / 100 + +figure.canvas.set_logical_size(700, 560) + +# moves the wave one step along the z-axis +def tick(subplot): + global increment, start, stop, zs + new_zs = np.linspace(start, stop, 200) + new_data = np.sin(new_zs) + + # just change the x-axis vals for the electric field + subplot["e"].data[:, 0] = new_data + # and y-axis vals for magnetic field + subplot["m"].data[:, 1] = new_data + + # update the vector lines + for i, (value, z) in enumerate(zip(new_data[::10], zs[::10])): + subplot["e-vec"].graphics[i].data = np.array([[0, 0, z], [value, 0, z]]) + subplot["m-vec"].graphics[i].data = np.array([[0, 0, z], [0, value, z]]) + + start += increment + stop += increment + + +figure[0, 0].add_animations(tick) + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.run() \ No newline at end of file diff --git a/examples/desktop/misc/image_animation.py b/examples/desktop/misc/image_animation.py new file mode 100644 index 000000000..df84f3c5a --- /dev/null +++ b/examples/desktop/misc/image_animation.py @@ -0,0 +1,38 @@ +""" +Simple Image Update +=================== + +Example showing updating a single plot with new random 512x512 data. +""" + +# test_example = false +# sphinx_gallery_pygfx_docs = 'animate' + +import fastplotlib as fpl +import numpy as np + +data = np.random.rand(512, 512) + +figure = fpl.Figure() + +# plot the image data +image_graphic = figure[0, 0].add_image(data=data, name="random-image") + + +# a function to update the image_graphic +# a figure-level animation function will optionally take the figure as an argument +def update_data(figure_instance): + new_data = np.random.rand(512, 512) + figure_instance[0, 0]["random-image"].data = new_data + +figure.add_animations(update_data) + +figure.show() + +figure.canvas.set_logical_size(700, 560) + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.run() diff --git a/examples/desktop/misc/line3d_animation.py b/examples/desktop/misc/line3d_animation.py new file mode 100644 index 000000000..27d22c78a --- /dev/null +++ b/examples/desktop/misc/line3d_animation.py @@ -0,0 +1,59 @@ +""" +Simple 3D Line Animation +======================== + +Example showing animation with 3D lines. +""" + +# test_example = false +# sphinx_gallery_pygfx_docs = 'animate 5s' + +import numpy as np +import fastplotlib as fpl + +# create data in the shape of a spiral +phi = np.linspace(0, 30, 200) + +xs = phi * np.cos(phi) +ys = phi * np.sin(phi) +zs = phi + +# make data 3d, with shape [, 3] +spiral = np.dstack([xs, ys, zs])[0] + +figure = fpl.Figure(cameras="3d") + +line_graphic = figure[0,0].add_line(data=spiral, thickness=3, cmap='jet') + +marker = figure[0,0].add_scatter(data=spiral[0], sizes=10, name="marker") + +marker_index = 0 + + +# a function to move the ball along the spiral +def move_marker(): + global marker_index + + marker_index += 1 + + if marker_index == spiral.shape[0]: + marker_index = 0 + + for subplot in figure: + subplot["marker"].data = spiral[marker_index] + + +# add `move_marker` to the animations +figure.add_animations(move_marker) + +figure.show() + +figure.canvas.set_logical_size(700, 560) + +figure[0,0].auto_scale(maintain_aspect=False) + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.run() diff --git a/examples/desktop/misc/line_animation.py b/examples/desktop/misc/line_animation.py new file mode 100644 index 000000000..50faad5c7 --- /dev/null +++ b/examples/desktop/misc/line_animation.py @@ -0,0 +1,53 @@ +""" +Simple Line Animation +===================== + +Example showing animation with lines. +""" + +# test_example = false +# sphinx_gallery_pygfx_docs = 'animate' + +import fastplotlib as fpl +import numpy as np + +# generate some data +start, stop = 0, 2 * np.pi +increment = (2 * np.pi) / 50 + +# make a simple sine wave +xs = np.linspace(start, stop, 100) +ys = np.sin(xs) + +figure = fpl.Figure() + +# plot the image data +sine = figure[0, 0].add_line(ys, name="sine", colors="r") + + +# increment along the x-axis on each render loop :D +def update_line(subplot): + global increment, start, stop + xs = np.linspace(start + increment, stop + increment, 100) + ys = np.sin(xs) + + start += increment + stop += increment + + # change only the y-axis values of the line + subplot["sine"].data[:, 1] = ys + + +figure[0, 0].add_animations(update_line) + +figure.show() + +figure.canvas.set_logical_size(700, 560) + +figure[0,0].auto_scale(maintain_aspect=False) + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.run() \ No newline at end of file diff --git a/examples/desktop/misc/multiplot_animation.py b/examples/desktop/misc/multiplot_animation.py new file mode 100644 index 000000000..a712ce9ef --- /dev/null +++ b/examples/desktop/misc/multiplot_animation.py @@ -0,0 +1,49 @@ +""" +Multi-Subplot Image Update +========================== + +Example showing updating a single plot with new random 512x512 data. +""" + +# test_example = false +# sphinx_gallery_pygfx_docs = 'animate' + +import fastplotlib as fpl +import numpy as np + +# Figure of shape 2 x 3 with all controllers synced +figure = fpl.Figure(shape=(2, 3), controller_ids="sync") + +# Make a random image graphic for each subplot +for subplot in figure: + # create image data + data = np.random.rand(512, 512) + # add an image to the subplot + subplot.add_image(data, name="rand-img") + +figure[0,1]["rand-img"].cmap = "viridis" +figure[1,0]["rand-img"].cmap = "Wistia" +figure[0,2]["rand-img"].cmap = "gray" +figure[1,1]["rand-img"].cmap = "spring" + +# Define a function to update the image graphics with new data +# add_animations will pass the gridplot to the animation function +def update_data(f): + for subplot in f: + new_data = np.random.rand(512, 512) + # index the image graphic by name and set the data + subplot["rand-img"].data = new_data + +# add the animation function +figure.add_animations(update_data) + +# show the gridplot +figure.show() + +figure.canvas.set_logical_size(700, 560) + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.run() \ No newline at end of file diff --git a/examples/desktop/misc/scatter_animation.py b/examples/desktop/misc/scatter_animation.py new file mode 100644 index 000000000..aa1495dd9 --- /dev/null +++ b/examples/desktop/misc/scatter_animation.py @@ -0,0 +1,59 @@ +""" +Scatter Animation Data +====================== + +Example showing animation with a scatter plot. +""" + +# test_example = false +# sphinx_gallery_pygfx_docs = 'animate' + +import fastplotlib as fpl +import numpy as np + +# create a random distribution of 10,000 xyz coordinates +n_points = 10_000 + +# dimensions always have to be [n_points, xyz] +dims = (n_points, 3) + +clouds_offset = 15 + +# create some random clouds +normal = np.random.normal(size=dims, scale=5) +# stack the data into a single array +cloud = np.vstack( + [ + normal - clouds_offset, + normal, + normal + clouds_offset, + ] +) + +# color each of them separately +colors = ["yellow"] * n_points + ["cyan"] * n_points + ["magenta"] * n_points + +# create plot +figure = fpl.Figure() +subplot_scatter = figure[0, 0] +# use an alpha value since this will be a lot of points +scatter_graphic = subplot_scatter.add_scatter(data=cloud, sizes=3, colors=colors, alpha=0.6) + + +def update_points(subplot): + # move every point by a small amount + deltas = np.random.normal(size=scatter_graphic.data.value.shape, loc=0, scale=0.15) + scatter_graphic.data = scatter_graphic.data.value + deltas + + +subplot_scatter.add_animations(update_points) + +figure.show() + +subplot_scatter.canvas.set_logical_size(700, 560) + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.run() \ No newline at end of file diff --git a/examples/desktop/misc/simple_event.py b/examples/desktop/misc/simple_event.py new file mode 100644 index 000000000..b6d408862 --- /dev/null +++ b/examples/desktop/misc/simple_event.py @@ -0,0 +1,56 @@ +""" +Simple Event +============ + +Example showing how to add a simple callback event. +""" + +# test_example = false +# sphinx_gallery_pygfx_docs = 'screenshot' + +import fastplotlib as fpl +import imageio.v3 as iio + +data = iio.imread("imageio:camera.png") + +# Create a figure +figure = fpl.Figure() + +# plot sine wave, use a single color +image_graphic = figure[0,0].add_image(data=data) + +# show the plot +figure.show() + + +# define callback function to print the event data +def callback_func(event_data): + print(event_data.info) + + +# Will print event data when the color changes +image_graphic.add_event_handler(callback_func, "cmap") + +image_graphic.cmap = "viridis" + + +# adding a click event, we can also use decorators to add event handlers +@image_graphic.add_event_handler("click") +def click_event(event_data): + # get the click location in screen coordinates + xy = (event_data.x, event_data.y) + + # map the screen coordinates to world coordinates + xy = figure[0,0].map_screen_to_world(xy)[:-1] + + # print the click location + print(xy) + + +figure.canvas.set_logical_size(700, 560) + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.run() diff --git a/examples/desktop/scatter/README.rst b/examples/desktop/scatter/README.rst new file mode 100644 index 000000000..278170fb4 --- /dev/null +++ b/examples/desktop/scatter/README.rst @@ -0,0 +1,2 @@ +Scatter Examples +================ diff --git a/examples/desktop/scatter/__init__.py b/examples/desktop/scatter/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/examples/desktop/scatter/scatter.py b/examples/desktop/scatter/scatter.py index c47306722..05dd7a99b 100644 --- a/examples/desktop/scatter/scatter.py +++ b/examples/desktop/scatter/scatter.py @@ -1,32 +1,54 @@ """ Scatter Plot ============ + Example showing scatter plot. """ -# test_example = true +# test_example = false +# sphinx_gallery_pygfx_docs = 'screenshot' import fastplotlib as fpl import numpy as np -from pathlib import Path -fig = fpl.Figure() +figure = fpl.Figure() + +# create a random distribution of 10,000 xyz coordinates +n_points = 5_000 + +# dimensions always have to be [n_points, xyz] +dims = (n_points, 3) + +clouds_offset = 15 -data_path = Path(__file__).parent.parent.joinpath("data", "iris.npy") -data = np.load(data_path) +# create some random clouds +normal = np.random.normal(size=dims, scale=5) +# stack the data into a single array +cloud = np.vstack( + [ + normal - clouds_offset, + normal, + normal + clouds_offset, + ] +) -n_points = 50 +# color each of them separately colors = ["yellow"] * n_points + ["cyan"] * n_points + ["magenta"] * n_points -scatter_graphic = fig[0, 0].add_scatter(data=data[:, :-1], sizes=6, alpha=0.7, colors=colors) +# create plot +figure = fpl.Figure() -fig.show() +# use an alpha value since this will be a lot of points +figure[0,0].add_scatter(data=cloud, sizes=3, colors=colors, alpha=0.6) -fig.canvas.set_logical_size(800, 800) +figure.show() -fig[0, 0].auto_scale() +figure.canvas.set_logical_size(700, 560) +figure[0, 0].auto_scale() +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) fpl.run() diff --git a/examples/desktop/scatter/scatter_cmap.py b/examples/desktop/scatter/scatter_cmap.py index 58c43c0ea..0adf72509 100644 --- a/examples/desktop/scatter/scatter_cmap.py +++ b/examples/desktop/scatter/scatter_cmap.py @@ -1,42 +1,53 @@ """ -Scatter Plot -============ +Scatter Colormap +================ + Example showing cmap change for scatter plot. """ -# test_example = true +# test_example = false +# sphinx_gallery_pygfx_docs = 'screenshot' import fastplotlib as fpl import numpy as np -from pathlib import Path -from sklearn.cluster import AgglomerativeClustering +figure = fpl.Figure() -fig = fpl.Figure() +# create a random distribution of 10,000 xyz coordinates +n_points = 5_000 -data_path = Path(__file__).parent.parent.joinpath("data", "iris.npy") -data = np.load(data_path) +# dimensions always have to be [n_points, xyz] +dims = (n_points, 3) -agg = AgglomerativeClustering(n_clusters=3) -agg.fit_predict(data) +clouds_offset = 15 -scatter_graphic = fig[0, 0].add_scatter( - data=data[:, :-1], # use only xy data - sizes=15, - alpha=0.7, - cmap="Set1", - cmap_transform=agg.labels_ # use the labels as a transform to map colors from the colormap +# create some random clouds +normal = np.random.normal(size=dims, scale=5) +# stack the data into a single array +cloud = np.vstack( + [ + normal - clouds_offset, + normal, + normal + clouds_offset, + ] ) -fig.show() +# color each of them separately +colors = ["yellow"] * n_points + ["cyan"] * n_points + ["magenta"] * n_points + +# use an alpha value since this will be a lot of points +figure[0,0].add_scatter(data=cloud, sizes=3, colors=colors, alpha=0.6) -fig.canvas.set_logical_size(800, 800) +figure.show() -fig[0, 0].auto_scale() +figure[0,0].graphics[0].cmap = "viridis" -scatter_graphic.cmap = "tab10" +figure.canvas.set_logical_size(700, 560) +figure[0, 0].auto_scale() +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) fpl.run() diff --git a/examples/desktop/scatter/scatter_cmap_iris.py b/examples/desktop/scatter/scatter_cmap_iris.py new file mode 100644 index 000000000..700f5c136 --- /dev/null +++ b/examples/desktop/scatter/scatter_cmap_iris.py @@ -0,0 +1,42 @@ +""" +Iris Scatter Colormap +===================== + +Example showing cmap change for scatter plot. +""" + +# test_example = true +# sphinx_gallery_pygfx_docs = 'screenshot' + +import fastplotlib as fpl +from sklearn.cluster import AgglomerativeClustering +from sklearn import datasets + + +figure = fpl.Figure() + +data = datasets.load_iris()["data"] + +agg = AgglomerativeClustering(n_clusters=3) +agg.fit_predict(data) + +scatter_graphic = figure[0, 0].add_scatter( + data=data[:, :-1], # use only xy data + sizes=15, + alpha=0.7, + cmap="Set1", + cmap_transform=agg.labels_ # use the labels as a transform to map colors from the colormap +) + +figure.show() + +figure.canvas.set_logical_size(700, 560) + +figure[0, 0].auto_scale() + +scatter_graphic.cmap = "tab10" + + +if __name__ == "__main__": + print(__doc__) + fpl.run() diff --git a/examples/desktop/scatter/scatter_colorslice.py b/examples/desktop/scatter/scatter_colorslice.py index 60433b5f5..3d3a3fa26 100644 --- a/examples/desktop/scatter/scatter_colorslice.py +++ b/examples/desktop/scatter/scatter_colorslice.py @@ -1,42 +1,60 @@ """ -Scatter Plot -============ +Scatter Plot Color Slicing +========================== + Example showing color slice for scatter plot. """ -# test_example = true +# test_example = false +# sphinx_gallery_pygfx_docs = 'screenshot' import fastplotlib as fpl import numpy as np -from pathlib import Path +figure = fpl.Figure() -fig = fpl.Figure() +# create a random distribution of 10,000 xyz coordinates +n_points = 5_000 -data_path = Path(__file__).parent.parent.joinpath("data", "iris.npy") -data = np.load(data_path) +# dimensions always have to be [n_points, xyz] +dims = (n_points, 3) -n_points = 50 -colors = ["yellow"] * n_points + ["cyan"] * n_points + ["magenta"] * n_points +clouds_offset = 30 -scatter_graphic = fig[0, 0].add_scatter( - data=data[:, :-1], - sizes=6, - alpha=0.7, - colors=colors # use colors from the list of strings +# create some random clouds +normal = np.random.normal(size=dims, scale=5) +# stack the data into a single array +cloud = np.vstack( + [ + normal - clouds_offset, + normal, + normal + clouds_offset, + ] ) -fig.show() +# color each of them separately +colors = ["yellow"] * n_points + ["cyan"] * n_points + ["magenta"] * n_points + +# create plot +figure = fpl.Figure() -fig.canvas.set_logical_size(800, 800) +# use an alpha value since this will be a lot of points +figure[0,0].add_scatter(data=cloud, sizes=3, colors=colors, alpha=0.6) -fig[0, 0].auto_scale() +figure.show() + +figure.canvas.set_logical_size(700, 560) + +scatter_graphic = figure[0, 0].graphics[0] + +figure[0, 0].auto_scale() scatter_graphic.colors[0:75] = "red" scatter_graphic.colors[75:150] = "white" scatter_graphic.colors[::2] = "blue" - +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) fpl.run() diff --git a/examples/desktop/scatter/scatter_colorslice_iris.py b/examples/desktop/scatter/scatter_colorslice_iris.py new file mode 100644 index 000000000..a1e6d5318 --- /dev/null +++ b/examples/desktop/scatter/scatter_colorslice_iris.py @@ -0,0 +1,42 @@ +""" +Iris Scatter Plot Color Slicing +=============================== + +Example showing color slice for scatter plot. +""" + +# test_example = true +# sphinx_gallery_pygfx_docs = 'screenshot' + +import fastplotlib as fpl +from sklearn import datasets + + +figure = fpl.Figure() + +data = datasets.load_iris()["data"] + +n_points = 50 +colors = ["yellow"] * n_points + ["cyan"] * n_points + ["magenta"] * n_points + +scatter_graphic = figure[0, 0].add_scatter( + data=data[:, :-1], + sizes=6, + alpha=0.7, + colors=colors # use colors from the list of strings +) + +figure.show() + +figure.canvas.set_logical_size(700, 560) + +figure[0, 0].auto_scale() + +scatter_graphic.colors[0:75] = "red" +scatter_graphic.colors[75:150] = "white" +scatter_graphic.colors[::2] = "blue" + + +if __name__ == "__main__": + print(__doc__) + fpl.run() diff --git a/examples/desktop/scatter/scatter_dataslice.py b/examples/desktop/scatter/scatter_dataslice.py index 989b7c21c..af2fffebd 100644 --- a/examples/desktop/scatter/scatter_dataslice.py +++ b/examples/desktop/scatter/scatter_dataslice.py @@ -1,40 +1,46 @@ """ -Scatter Plot -============ +Scatter Plot Data Slicing +========================= + Example showing data slice for scatter plot. """ -# test_example = true +# test_example = false +# sphinx_gallery_pygfx_docs = 'screenshot' import fastplotlib as fpl import numpy as np -from pathlib import Path -fig = fpl.Figure() +figure = fpl.Figure() -data_path = Path(__file__).parent.parent.joinpath("data", "iris.npy") -data = np.load(data_path) +# create a gaussian cloud of 5_000 points +n_points = 1_000 -n_points = 50 -colors = ["yellow"] * n_points + ["cyan"] * n_points + ["magenta"] * n_points +mean = [0, 0] # mean of the Gaussian distribution +covariance = [[1, 0], [0, 1]] # covariance matrix -scatter_graphic = fig[0, 0].add_scatter(data=data[:, :-1], sizes=6, alpha=0.7, colors=colors) +gaussian_cloud = np.random.multivariate_normal(mean, covariance, n_points) +gaussian_cloud2 = np.random.multivariate_normal(mean, covariance, n_points) -fig.show() +# create plot +figure = fpl.Figure() -fig.canvas.set_logical_size(800, 800) +# use an alpha value since this will be a lot of points +scatter1 = figure[0,0].add_scatter(data=gaussian_cloud, sizes=3) +scatter2 = figure[0,0].add_scatter(data=gaussian_cloud2, colors="r", sizes=3) -fig[0, 0].auto_scale() +figure.show() -scatter_graphic.data[0] = np.array([[5, 3, 1.5]]) -scatter_graphic.data[1] = np.array([[4.3, 3.2, 1.3]]) -scatter_graphic.data[2] = np.array([[5.2, 2.7, 1.7]]) +figure.canvas.set_logical_size(700, 560) -scatter_graphic.data[10:15] = scatter_graphic.data[0:5] + np.array([1, 1, 1]) -scatter_graphic.data[50:100:2] = scatter_graphic.data[100:150:2] + np.array([1, 1, 0]) +figure[0, 0].auto_scale() +scatter1.data[:500] = np.array([0 , 0, 0]) +scatter2.data[500:] = np.array([0 , 0, 0]) +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) fpl.run() diff --git a/examples/desktop/scatter/scatter_dataslice_iris.py b/examples/desktop/scatter/scatter_dataslice_iris.py new file mode 100644 index 000000000..0d47c6efd --- /dev/null +++ b/examples/desktop/scatter/scatter_dataslice_iris.py @@ -0,0 +1,41 @@ +""" +Iris Scatter Plot Data Slicing +============================== + +Example showing data slice for scatter plot. +""" + +# test_example = true +# sphinx_gallery_pygfx_docs = 'screenshot' + +import fastplotlib as fpl +import numpy as np +from sklearn import datasets + + +figure = fpl.Figure() + +data = datasets.load_iris()["data"] + +n_points = 50 +colors = ["yellow"] * n_points + ["cyan"] * n_points + ["magenta"] * n_points + +scatter_graphic = figure[0, 0].add_scatter(data=data[:, :-1], sizes=6, alpha=0.7, colors=colors) + +figure.show() + +figure.canvas.set_logical_size(700, 560) + +figure[0, 0].auto_scale() + +scatter_graphic.data[0] = np.array([[5, 3, 1.5]]) +scatter_graphic.data[1] = np.array([[4.3, 3.2, 1.3]]) +scatter_graphic.data[2] = np.array([[5.2, 2.7, 1.7]]) + +scatter_graphic.data[10:15] = scatter_graphic.data[0:5] + np.array([1, 1, 1]) +scatter_graphic.data[50:100:2] = scatter_graphic.data[100:150:2] + np.array([1, 1, 0]) + + +if __name__ == "__main__": + print(__doc__) + fpl.run() diff --git a/examples/desktop/scatter/scatter_iris.py b/examples/desktop/scatter/scatter_iris.py new file mode 100644 index 000000000..c16a4b135 --- /dev/null +++ b/examples/desktop/scatter/scatter_iris.py @@ -0,0 +1,38 @@ +""" +Iris Scatter Plot +================= + +Example showing scatter plot using sklearn iris dataset. +""" + +# test_example = true +# sphinx_gallery_pygfx_docs = 'hidden' + +import fastplotlib as fpl +import numpy as np +from pathlib import Path +import sys + +figure = fpl.Figure() + +current_file = Path(sys.argv[0]).resolve() + +data_path = Path(__file__).parent.parent.joinpath("data", "iris.npy") +data = np.load(data_path) + +n_points = 50 +colors = ["yellow"] * n_points + ["cyan"] * n_points + ["magenta"] * n_points + +scatter_graphic = figure[0, 0].add_scatter(data=data[:, :-1], sizes=6, alpha=0.7, colors=colors) + +figure.show() + +figure.canvas.set_logical_size(700, 560) + +figure[0, 0].auto_scale() + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.run() \ No newline at end of file diff --git a/examples/desktop/scatter/scatter_size.py b/examples/desktop/scatter/scatter_size.py index 41a97ad53..bd4e2db2b 100644 --- a/examples/desktop/scatter/scatter_size.py +++ b/examples/desktop/scatter/scatter_size.py @@ -1,10 +1,13 @@ """ -Scatter Plot -============ +Scatter Plot Size +================= + Example showing point size change for scatter plot. """ # test_example = true +# sphinx_gallery_pygfx_docs = 'screenshot' + import numpy as np import fastplotlib as fpl @@ -15,7 +18,7 @@ names = [["scalar_size"], ["array_size"]] # Create the grid plot -fig = fpl.Figure(shape=shape, names=names, size=(1000, 1000)) +figure = fpl.Figure(shape=shape, names=names, size=(1000, 1000)) # get y_values using sin function angles = np.arange(0, 20 * np.pi + 0.001, np.pi / 20) @@ -24,18 +27,22 @@ data = np.column_stack([x_values, y_values]) -fig["scalar_size"].add_scatter( +figure["scalar_size"].add_scatter( data=data, sizes=5, colors="blue" ) # add a set of scalar sizes non_scalar_sizes = np.abs((y_values / np.pi)) # ensure minimum size of 5 -fig["array_size"].add_scatter(data=data, sizes=non_scalar_sizes, colors="red") +figure["array_size"].add_scatter(data=data, sizes=non_scalar_sizes, colors="red") -for graph in fig: +for graph in figure: graph.auto_scale(maintain_aspect=True) -fig.show() +figure.show() + +figure.canvas.set_logical_size(700, 560) +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) fpl.run() diff --git a/examples/desktop/screenshots/gridplot.png b/examples/desktop/screenshots/gridplot.png index 9e81fe8c6..6567c57da 100644 --- a/examples/desktop/screenshots/gridplot.png +++ b/examples/desktop/screenshots/gridplot.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:462a06e9c74dc9f0958aa265349dfac9c31d77a3ab3915f85596c85f2e7a6f3a -size 266056 +oid sha256:6499a000911de783b69a7958a1bf2b0290b5a3117fe14ade792baca95557b2a7 +size 263883 diff --git a/examples/desktop/screenshots/gridplot_non_square.png b/examples/desktop/screenshots/gridplot_non_square.png index b74be7065..847ed65cb 100644 --- a/examples/desktop/screenshots/gridplot_non_square.png +++ b/examples/desktop/screenshots/gridplot_non_square.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9ab4b1f8188824b81fe29b5c6ac7177734fb2b9958133e19f02919d1da98b96c -size 174978 +oid sha256:e4a0a002caf10e1e80ca0177bac4085e2f837ad3977f2546830acb42f0106f3f +size 173182 diff --git a/examples/desktop/screenshots/heatmap.png b/examples/desktop/screenshots/heatmap.png index ec6cf9955..1adcc1e1d 100644 --- a/examples/desktop/screenshots/heatmap.png +++ b/examples/desktop/screenshots/heatmap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:754bd8713617bf61d1adf57b3e84c1257b038bf15412aa3c8bd466d1405086e7 -size 48524 +oid sha256:3e742d06167a49ec80253cb3984da88e6d623dc645f93bcfbd1a82966ba44a84 +size 40051 diff --git a/examples/desktop/screenshots/heatmap_cmap.png b/examples/desktop/screenshots/heatmap_cmap.png index c495cf72c..cee81dd30 100644 --- a/examples/desktop/screenshots/heatmap_cmap.png +++ b/examples/desktop/screenshots/heatmap_cmap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d2ba0b76e982ceb1439c5ebaabfaf089ea9b09e50934718eaaa29d7492272196 -size 42746 +oid sha256:8863461569f5b89d1443e3051a5512f3987487fcb9e057215d2f030a180fa09f +size 97996 diff --git a/examples/desktop/screenshots/heatmap_data.png b/examples/desktop/screenshots/heatmap_data.png index 229d6c2cc..316a73753 100644 --- a/examples/desktop/screenshots/heatmap_data.png +++ b/examples/desktop/screenshots/heatmap_data.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a7160c4f034214f8052a6d88001dac706b0a85a5a4df076958ba1a176344b85a -size 53854 +oid sha256:a975179e82893dbb04e4674310761e7b02bb62ae6abb1b89397720bddf96ae5f +size 19084 diff --git a/examples/desktop/screenshots/heatmap_square.png b/examples/desktop/screenshots/heatmap_square.png deleted file mode 100644 index 00a01133e..000000000 --- a/examples/desktop/screenshots/heatmap_square.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8d01171b2bd05b5c88df4312c303094fdede36b1cf930455ace6d1fb12d8eb36 -size 81274 diff --git a/examples/desktop/screenshots/heatmap_vmin_vmax.png b/examples/desktop/screenshots/heatmap_vmin_vmax.png index b028291f7..357683d82 100644 --- a/examples/desktop/screenshots/heatmap_vmin_vmax.png +++ b/examples/desktop/screenshots/heatmap_vmin_vmax.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:61c3754de3a7e6622ce1a77dbbf9bbd6ccfd3ccad3b1463b009bf93511258034 -size 44426 +oid sha256:9592f3724016db1b7431bc100b16bec175e197c111e7b442dc2255d51da3f5e8 +size 114957 diff --git a/examples/desktop/screenshots/heatmap_wide.png b/examples/desktop/screenshots/heatmap_wide.png deleted file mode 100644 index 927b933d6..000000000 --- a/examples/desktop/screenshots/heatmap_wide.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:271e0d769b608d0f34a153ab8b8353f1e5d127f239951fc407ccedd3eee5e2e5 -size 82687 diff --git a/examples/desktop/screenshots/image_cmap.png b/examples/desktop/screenshots/image_cmap.png index be16ba213..9ad74e927 100644 --- a/examples/desktop/screenshots/image_cmap.png +++ b/examples/desktop/screenshots/image_cmap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:552a4d5141a5a87baaedd8a9d7d911dfdddee5792c024c77012665268af865e9 -size 189479 +oid sha256:009326a4c3605622980bf80b20cc8cc807fa610858d155304285a1a5d478b56c +size 187517 diff --git a/examples/desktop/screenshots/image_rgb.png b/examples/desktop/screenshots/image_rgb.png index 8d391f07c..50dfe6761 100644 --- a/examples/desktop/screenshots/image_rgb.png +++ b/examples/desktop/screenshots/image_rgb.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:55e76cea92eb34e1e25d730d2533a9a0d845921e78bc980708d320bb353a2d73 -size 218413 +oid sha256:c7e026664f350a4abd83297dc55a324a12ddcf8fd94625d26381d09a3fdd953b +size 216191 diff --git a/examples/desktop/screenshots/image_rgbvminvmax.png b/examples/desktop/screenshots/image_rgbvminvmax.png index eabe85d28..73707177c 100644 --- a/examples/desktop/screenshots/image_rgbvminvmax.png +++ b/examples/desktop/screenshots/image_rgbvminvmax.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7ee27d89170b30a3da7fe6d752961b30e17712d7905d8fa0686f9597debe68f9 -size 34620 +oid sha256:a6e18e9782514b3f525d8e7f8ed83699fae48fced2b633f3f1831e4b46751c44 +size 33627 diff --git a/examples/desktop/screenshots/image_simple.png b/examples/desktop/screenshots/image_simple.png index 853eb2f01..3857baf9d 100644 --- a/examples/desktop/screenshots/image_simple.png +++ b/examples/desktop/screenshots/image_simple.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e943bd3b1e00acaed274dd185f5362210e39330e0f541db9ceee489fa0a9a344 -size 189822 +oid sha256:1ab7ea5e0aa322c8b4249df30a80cbf3d7b55e0b7a0418f4bed99f81d14fdd7e +size 187665 diff --git a/examples/desktop/screenshots/image_vminvmax.png b/examples/desktop/screenshots/image_vminvmax.png index eabe85d28..73707177c 100644 --- a/examples/desktop/screenshots/image_vminvmax.png +++ b/examples/desktop/screenshots/image_vminvmax.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7ee27d89170b30a3da7fe6d752961b30e17712d7905d8fa0686f9597debe68f9 -size 34620 +oid sha256:a6e18e9782514b3f525d8e7f8ed83699fae48fced2b633f3f1831e4b46751c44 +size 33627 diff --git a/examples/desktop/screenshots/line.png b/examples/desktop/screenshots/line.png index 74cbae39a..c21a522e7 100644 --- a/examples/desktop/screenshots/line.png +++ b/examples/desktop/screenshots/line.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c8b4f4a08d1791b80d226c8c3099e37d33d8cdd7a400e4f85fb7072ee2aa3c2e -size 29121 +oid sha256:aa12348340af55e7472ceb9986a73be35b398761d057e579e199055fe6bcd9ef +size 27740 diff --git a/examples/desktop/screenshots/line_cmap.png b/examples/desktop/screenshots/line_cmap.png index 9cd93f05d..5e092c844 100644 --- a/examples/desktop/screenshots/line_cmap.png +++ b/examples/desktop/screenshots/line_cmap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d60b4ff117298f973be892773dbfc620ac855c35ca7dea42437e20bf7fcef804 -size 31050 +oid sha256:7177ca064170e815870192afd9b01dd9e23332b7cbdd962592932dacac842a76 +size 29567 diff --git a/examples/desktop/screenshots/line_collection.png b/examples/desktop/screenshots/line_collection.png index bcfe85309..cc6bdcd72 100644 --- a/examples/desktop/screenshots/line_collection.png +++ b/examples/desktop/screenshots/line_collection.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7ca99b8d74fdf7f87b0f2fc5637c59c9090b91bef868e85ddd75dbcb1264f699 -size 95146 +oid sha256:8762da25c8d01ad135288e80c91750522e32c13d824862386ad3612f63e29cfc +size 93068 diff --git a/examples/desktop/screenshots/line_collection_cmap_values.png b/examples/desktop/screenshots/line_collection_cmap_values.png index b7fcdbcae..5dbe1cfda 100644 --- a/examples/desktop/screenshots/line_collection_cmap_values.png +++ b/examples/desktop/screenshots/line_collection_cmap_values.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:68090603856eb5b961092cf2ad2d89a1e9cfd7e31f6d089b3abad101874f65d4 -size 61032 +oid sha256:ba04a638118cc6a2dff2a253fa40fae099aa2acfe347570ff4a0044403a2a5b7 +size 59102 diff --git a/examples/desktop/screenshots/line_collection_cmap_values_qualitative.png b/examples/desktop/screenshots/line_collection_cmap_values_qualitative.png index 9f89a24cc..00fde0fdd 100644 --- a/examples/desktop/screenshots/line_collection_cmap_values_qualitative.png +++ b/examples/desktop/screenshots/line_collection_cmap_values_qualitative.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9cff99e5f9faf319909571778631453c043237f5c94eece6680b028a5d7a5ac2 -size 64149 +oid sha256:a592bf90017c25abf163936c4c475b8a96ac0775e450c8d7355011fe20bbfb22 +size 62416 diff --git a/examples/desktop/screenshots/line_collection_colors.png b/examples/desktop/screenshots/line_collection_colors.png index 7bb4152fd..cf7880a6c 100644 --- a/examples/desktop/screenshots/line_collection_colors.png +++ b/examples/desktop/screenshots/line_collection_colors.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a4aa17a65806300da65f4bbbfccb970d6a7207c4ca4d48b25615f627630fb484 -size 51174 +oid sha256:052a46223f73099eacf2b4672348c1110c14b22a85131b73561ac1feb8e9c796 +size 49693 diff --git a/examples/desktop/screenshots/line_collection_slicing.png b/examples/desktop/screenshots/line_collection_slicing.png index ba4170874..54d2a1098 100644 --- a/examples/desktop/screenshots/line_collection_slicing.png +++ b/examples/desktop/screenshots/line_collection_slicing.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:01090d611117fd0d2b3f9971e359871c9598a634a1829e74848b1c78a770d437 -size 131764 +oid sha256:07a02cb8a93527e913d35a0caed5dfc2feb01234d63b73ba3c3e244bbfda5e59 +size 129082 diff --git a/examples/desktop/screenshots/line_colorslice.png b/examples/desktop/screenshots/line_colorslice.png index 789265530..2bcf008a5 100644 --- a/examples/desktop/screenshots/line_colorslice.png +++ b/examples/desktop/screenshots/line_colorslice.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:25e87f566a667c98b54d4acdf115d16b486e047242b9ce8b141e5724b9d0a46a -size 33191 +oid sha256:69c4994a1259511a6480c2227a31472ab527f33f747cd44b9018f409e03ba1f1 +size 32353 diff --git a/examples/desktop/screenshots/line_dataslice.png b/examples/desktop/screenshots/line_dataslice.png index e55a6111e..395d2f1bf 100644 --- a/examples/desktop/screenshots/line_dataslice.png +++ b/examples/desktop/screenshots/line_dataslice.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e76275ea6f5719e16ff0ef3401dc33fe4b70c4c9010b3b673fca26812f33b9e8 -size 46400 +oid sha256:268a0e9a6a32e015466b7a72b8cb9675597db2b3212edaf818871a27602a751c +size 44986 diff --git a/examples/desktop/screenshots/line_present_scaling.png b/examples/desktop/screenshots/line_present_scaling.png new file mode 100644 index 000000000..ba7142106 --- /dev/null +++ b/examples/desktop/screenshots/line_present_scaling.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:06f7dd45eb495fecfcf46478c6430a658640ceb2855c4797bc184cf4134571e3 +size 20180 diff --git a/examples/desktop/screenshots/line_stack.png b/examples/desktop/screenshots/line_stack.png index 29d941fd4..edaae0d0a 100644 --- a/examples/desktop/screenshots/line_stack.png +++ b/examples/desktop/screenshots/line_stack.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:73226917c233f3fd3d7ec0b40d5a7ded904d275c871242dc0578bddf4c19d0bd -size 93687 +oid sha256:2b3ae8275e536669dfabdc8c3b715d1ca9c73f17062ab1f90f9650bd86b1c4f7 +size 91285 diff --git a/examples/desktop/screenshots/multigraphic_gridplot.png b/examples/desktop/screenshots/multigraphic_gridplot.png new file mode 100644 index 000000000..e814eadde --- /dev/null +++ b/examples/desktop/screenshots/multigraphic_gridplot.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e879a39032759006685eff1d5c2331a66b9126b13bc148734c3622bd6cdc68a7 +size 163902 diff --git a/examples/desktop/screenshots/scatter.png b/examples/desktop/screenshots/scatter.png index 94fb858e1..195c5ae64 100644 --- a/examples/desktop/screenshots/scatter.png +++ b/examples/desktop/screenshots/scatter.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4fc16a1ba74a8eca99a2fc7937f8896ca93207b99e231bc4f53845b0d2bdaed7 -size 15283 +oid sha256:b0f6c3d3b5e7216cc17c70ad3ff114092b1556fb9dcd171ae737f28d52ce51c9 +size 106634 diff --git a/examples/desktop/screenshots/scatter_cmap.png b/examples/desktop/screenshots/scatter_cmap.png index 560f1942d..55442aceb 100644 --- a/examples/desktop/screenshots/scatter_cmap.png +++ b/examples/desktop/screenshots/scatter_cmap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9479bb3995bd145a163a2f25592a4c85c52c663d33381efee7743ffc1f16aef1 -size 32894 +oid sha256:0655f8f5f1fdeecbd5d097cf90a4776dd8125a16d0e8edb86aa37f77daba0d9b +size 107892 diff --git a/examples/desktop/screenshots/scatter_cmap_iris.png b/examples/desktop/screenshots/scatter_cmap_iris.png new file mode 100644 index 000000000..eb8802c0e --- /dev/null +++ b/examples/desktop/screenshots/scatter_cmap_iris.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:088579410d42768e68d7e7b298d2ed9e409eaff28ba5a2dd2ec8283e5994b862 +size 31599 diff --git a/examples/desktop/screenshots/scatter_colorslice.png b/examples/desktop/screenshots/scatter_colorslice.png index cede76dfd..7c43dd6cb 100644 --- a/examples/desktop/screenshots/scatter_colorslice.png +++ b/examples/desktop/screenshots/scatter_colorslice.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f7956e02d6c231bab091adb4ce9102ad4943050ccf171a0594a899a381880771 -size 14712 +oid sha256:d3319afffb554eb624d9bba5b87daf331b619ac5ec5006f13e479b613f43ca32 +size 72083 diff --git a/examples/desktop/screenshots/scatter_colorslice_iris.png b/examples/desktop/screenshots/scatter_colorslice_iris.png new file mode 100644 index 000000000..8692f53b3 --- /dev/null +++ b/examples/desktop/screenshots/scatter_colorslice_iris.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f21ab8499384298a74db3e5fbb5aa0113f714d6f77e1e3d614da3d278a2c7a78 +size 13478 diff --git a/examples/desktop/screenshots/scatter_dataslice_iris.png b/examples/desktop/screenshots/scatter_dataslice_iris.png new file mode 100644 index 000000000..c0c6aa5e7 --- /dev/null +++ b/examples/desktop/screenshots/scatter_dataslice_iris.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2032ce0b3c39f35e9dc37bc54fac016dc43aef716cfc65e6d65fd7fc977e74cf +size 14473 diff --git a/examples/desktop/screenshots/scatter_iris.png b/examples/desktop/screenshots/scatter_iris.png new file mode 100644 index 000000000..aa1d2b743 --- /dev/null +++ b/examples/desktop/screenshots/scatter_iris.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:320a02df6de51235b1912b26df4ea7633e9513df4d4034605a4e00724ceb57dc +size 14131 diff --git a/examples/desktop/screenshots/scatter_present.png b/examples/desktop/screenshots/scatter_present.png new file mode 100644 index 000000000..08bc610b3 --- /dev/null +++ b/examples/desktop/screenshots/scatter_present.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bd072918f21ed0ce4ea4e1f4499ec1ff66d867cfdc0ecd6b3ed8092141cd348e +size 14195 diff --git a/examples/desktop/screenshots/scatter_size.png b/examples/desktop/screenshots/scatter_size.png index 056d2a531..2d4e559fe 100644 --- a/examples/desktop/screenshots/scatter_size.png +++ b/examples/desktop/screenshots/scatter_size.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5ccfbac94de6ba122ea420dce58e4a576b2d58d9282aaf8d64de399278df57b3 -size 38076 +oid sha256:a96a5da476b22f3ebd42ec927c3d1c1dee4dc20f261a97feaced69ed4142c645 +size 35484 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-0-reset.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-0-reset.png index 8572f6472..1369669e6 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-0-reset.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-0-reset.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ff83d6bab26b9bbccf66ed100764ffdfc7556f4cb04f0b85f50c2497ba0ab257 -size 134419 +oid sha256:7049dc9344a8d51bcfc3ba7f96a25c84db27f5072f14e8845cd2f01c3f4f5310 +size 133683 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-0.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-0.png index 8572f6472..1369669e6 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-0.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ff83d6bab26b9bbccf66ed100764ffdfc7556f4cb04f0b85f50c2497ba0ab257 -size 134419 +oid sha256:7049dc9344a8d51bcfc3ba7f96a25c84db27f5072f14e8845cd2f01c3f4f5310 +size 133683 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-279.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-279.png index e241ce5a6..5817cd299 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-279.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-279.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:337a22f11649b350f7f47d68d82be165633caeb7f8cef581e50f981d6ec0c52c -size 169615 +oid sha256:1ca687c654679ee9d47a053196fb65d92105ed6cce76f0d2d1775582053e7b07 +size 168750 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-max-33.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-max-33.png index b827fc536..2f2e310d0 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-max-33.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-max-33.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:22ff3ed815fcbe8bc95c321c806a4b42536e7014209cd43ac597a5ccefd8b9c6 -size 149261 +oid sha256:dd51516d18f550f62b9f2c989cfae82d267eb6de1d8757cf44949bb41c3b13c2 +size 148408 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-13.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-13.png index d37f44a3a..39a4f1274 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:11143f73a297d0b59c92db1b115ceac1bc1304135a9925302e616a4fd3669b25 -size 125012 +oid sha256:9613c10e63261ddbcf3274a9804bdf3a1be0734c1190720663b01f8b2ae48389 +size 124532 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-33.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-33.png index 46d3fa9b3..b9feacb82 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-33.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-33.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7438018c3b55d423f57c42a9d03c1af6d5de168a2dfa5df9d535ef2ae1f1c8e9 -size 113981 +oid sha256:8fa86b26f11bd9655c60e43ddd88ada618cea1ba06b3dcddcb408569c09a7750 +size 113897 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-reset.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-reset.png index 6146a7985..ae280a970 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-reset.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-reset.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9b57a1e6640de9471540fa4d86faadb706d6de8cc1de6a29fb65d709b91eef1e -size 146429 +oid sha256:aca573ba6dcda8ad8f018a96790b1b1aa5da25fe9584a57c2c946e1cf2df9e5c +size 145275 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50.png index 6146a7985..ae280a970 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9b57a1e6640de9471540fa4d86faadb706d6de8cc1de6a29fb65d709b91eef1e -size 146429 +oid sha256:aca573ba6dcda8ad8f018a96790b1b1aa5da25fe9584a57c2c946e1cf2df9e5c +size 145275 diff --git a/examples/notebooks/screenshots/nb-lines-3d.png b/examples/notebooks/screenshots/nb-lines-3d.png index a3b75de58..6c19c965c 100644 --- a/examples/notebooks/screenshots/nb-lines-3d.png +++ b/examples/notebooks/screenshots/nb-lines-3d.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:677f544d93cf7a733003c38666f555f0b598d41b92681dd2f09e6c11faa4aed0 -size 14186 +oid sha256:c6ab8d74c980a02505d5d626d170b60847ce2b733ea5b47278a1f48228011a38 +size 14181 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-jet-values-cosine.png b/examples/notebooks/screenshots/nb-lines-cmap-jet-values-cosine.png index 0d9ff5729..f75d83ba4 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-jet-values-cosine.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-jet-values-cosine.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0d981d57d7905879ab68af95da84d2cf530a9a40dc4d0ffb138119a11f4966be -size 11808 +oid sha256:d2b87297587b5f76062132370fdf7e8318cbde68d38d12a0179548f9790c2af8 +size 11765 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-jet-values.png b/examples/notebooks/screenshots/nb-lines-cmap-jet-values.png index dbcbf1e7f..a8229cfc8 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-jet-values.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-jet-values.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:706bffa485dd7994a70602ef2076aee79e1206dd508fbac903549fce526087b6 -size 13095 +oid sha256:99f6a7a9a1399aaf8d1070812c41f03f80ba41812a1acf9d4bdd17641bc589f4 +size 13038 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-jet.png b/examples/notebooks/screenshots/nb-lines-cmap-jet.png index 6a3ae0c1c..ccc897f66 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-jet.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-jet.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cfd3f55e1671ac1fa45a8eb26aeb425ccb8d1ac033f5766f4002fee4380b2a77 -size 11174 +oid sha256:cac3d7cb23d7ec207cdf7fd7032037c7749e30563754d6c2cac50d67a7731a2d +size 11086 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-tab-10.png b/examples/notebooks/screenshots/nb-lines-cmap-tab-10.png index 9bb368e0e..8ddc44ead 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-tab-10.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-tab-10.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:338dbcdf1a87266eee33367bfa08bcaec7eac42ef2dd928bbc699b3a0412ebaf -size 9889 +oid sha256:d38a2c27e5b598883c40b44e45b347f029a7369b638bc4111ef2d69978245378 +size 9943 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-viridis-values.png b/examples/notebooks/screenshots/nb-lines-cmap-viridis-values.png index 23137bdf3..6bf270874 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-viridis-values.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-viridis-values.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e36a7a74ac39dac5ac96a4e7c8e56990794ab1cda1b8ee5276087e9814dd1696 -size 10100 +oid sha256:d0ead67733470d6afcb2228645ee1a056acd525d369d74c16b1054783d6ab4f5 +size 10155 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-viridis.png b/examples/notebooks/screenshots/nb-lines-cmap-viridis.png index 2fcd4749c..4f99e5fb5 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-viridis.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-viridis.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b12ee5e31f64415b57536c59587b91c7a3a7c74e95be3459d8036a43d073d7db -size 13821 +oid sha256:15f4decaa6ba08f09a988771c5dc433629cb9e3bccfa08f820268366a3ff06a6 +size 13761 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-white.png b/examples/notebooks/screenshots/nb-lines-cmap-white.png index 397b5fc94..85e43aa5e 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-white.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-white.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6239f23d3b5c1745879f5706352abb905d5638522b171776bff051e511426c2f -size 8359 +oid sha256:8a623f31d28533cc6052143856d0ef2e827b42891397fa4ac20d358f44d751d7 +size 8332 diff --git a/examples/notebooks/screenshots/nb-lines-colors.png b/examples/notebooks/screenshots/nb-lines-colors.png index 149321e52..ab50dfaaf 100644 --- a/examples/notebooks/screenshots/nb-lines-colors.png +++ b/examples/notebooks/screenshots/nb-lines-colors.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9a5e99c5a872d9bbf8dd909498459ddee7f22f08d3fe3cd68b3ea57c105ab51b -size 27634 +oid sha256:0cbcafef567bff33762d64bcf6f83c7a1982015ea21d508b1e6564cb0d8435d2 +size 27420 diff --git a/examples/notebooks/screenshots/nb-lines-data.png b/examples/notebooks/screenshots/nb-lines-data.png index 9ed38b1f5..88f1001ad 100644 --- a/examples/notebooks/screenshots/nb-lines-data.png +++ b/examples/notebooks/screenshots/nb-lines-data.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cc0d3d6819ce0d9f5d531276bbde0397ef35e3084ac1f9b3575f0209eea07456 -size 39512 +oid sha256:f62dc16cd1ea212cf29974a64e6ad2dd3d7ae10d7b3eef573c25316bbe89b097 +size 38740 diff --git a/examples/notebooks/screenshots/nb-lines-underlay.png b/examples/notebooks/screenshots/nb-lines-underlay.png index e7f6aeb0c..f9650e253 100644 --- a/examples/notebooks/screenshots/nb-lines-underlay.png +++ b/examples/notebooks/screenshots/nb-lines-underlay.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8ed174c362c7e7c491eba02b32102f59423af41537577c694fdcd54d69c065b3 -size 50422 +oid sha256:ec7881cc0084b0ea2182f8d4f6f23388a4d48b88dbbf9c06b9dc2fa8125989b7 +size 49553 diff --git a/examples/notebooks/screenshots/nb-lines.png b/examples/notebooks/screenshots/nb-lines.png index 603f4a8fc..7df6ea14f 100644 --- a/examples/notebooks/screenshots/nb-lines.png +++ b/examples/notebooks/screenshots/nb-lines.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:eeef2c47e7dde62038307fa7929a306801bf8b708fbcf1062ed9c751727bfb2b -size 24300 +oid sha256:b925337438aa24f0179a8b5c1688e154679fa002402a16746b6bb3cd6684af1e +size 24246 diff --git a/examples/tests/test_examples.py b/examples/tests/test_examples.py index b8369e368..c08df9005 100644 --- a/examples/tests/test_examples.py +++ b/examples/tests/test_examples.py @@ -69,7 +69,7 @@ def test_example_screenshots(module, force_offscreen): example = importlib.import_module(module_name) # render a frame - img = np.asarray(example.fig.renderer.target.draw()) + img = np.asarray(example.figure.renderer.target.draw()) # check if _something_ was rendered assert img is not None and img.size > 0 diff --git a/examples/tests/testutils.py b/examples/tests/testutils.py index 1a95e249f..0e4cd2e1b 100644 --- a/examples/tests/testutils.py +++ b/examples/tests/testutils.py @@ -22,7 +22,8 @@ "scatter/*.py", "line/*.py", "line_collection/*.py", - "gridplot/*.py" + "gridplot/*.py", + "misc/*.py" ] diff --git a/fastplotlib/layouts/_figure.py b/fastplotlib/layouts/_figure.py index 2c157db8f..17bb28095 100644 --- a/fastplotlib/layouts/_figure.py +++ b/fastplotlib/layouts/_figure.py @@ -475,11 +475,6 @@ def show( _maintain_aspect = maintain_aspect subplot.auto_scale(maintain_aspect=_maintain_aspect, zoom=0.95) - # used for generating images in docs using nbsphinx - if "NB_SNAPSHOT" in os.environ.keys(): - if os.environ["NB_SNAPSHOT"] == "1": - return self.canvas.snapshot() - # return the appropriate OutputContext based on the current canvas if self.canvas.__class__.__name__ == "JupyterWgpuCanvas": from .output.jupyter_output import ( diff --git a/setup.py b/setup.py index d75edf956..ef79e4774 100644 --- a/setup.py +++ b/setup.py @@ -12,6 +12,7 @@ extras_require = { "docs": [ "sphinx", + "sphinx-gallery", "furo", "glfw", "jupyter-rfb>=0.4.1", # required so ImageWidget docs show up @@ -21,6 +22,9 @@ "pandoc", "jupyterlab", "sidecar", + "imageio", + "matplotlib", + "scikit-learn" ], "notebook": [ "jupyterlab", From 3f5cf9d41a65077c6d0058523cfb0ad153fbf705 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Tue, 18 Jun 2024 18:07:35 -0400 Subject: [PATCH 081/185] Fix off by one error (#530) * ndarray.ptp -> np.ptp for numpy v2 * fix off by one error in Image feature, add test * fix off by one error in Image feature, add test * add example test for small image to prevent off by one errors * update screenshots * update texture array test * update multi graphic gallery example, do not use for tests --- .../desktop/gridplot/multigraphic_gridplot.py | 10 +++--- examples/desktop/image/image_small.py | 33 +++++++++++++++++++ examples/desktop/screenshots/gridplot.png | 4 +-- .../screenshots/gridplot_non_square.png | 4 +-- examples/desktop/screenshots/heatmap.png | 4 +-- examples/desktop/screenshots/heatmap_cmap.png | 3 -- examples/desktop/screenshots/heatmap_data.png | 3 -- .../desktop/screenshots/heatmap_vmin_vmax.png | 3 -- examples/desktop/screenshots/image_cmap.png | 4 +-- examples/desktop/screenshots/image_rgb.png | 4 +-- .../desktop/screenshots/image_rgbvminvmax.png | 4 +-- examples/desktop/screenshots/image_simple.png | 4 +-- examples/desktop/screenshots/image_small.png | 3 ++ .../desktop/screenshots/image_vminvmax.png | 4 +-- examples/desktop/screenshots/line.png | 4 +-- examples/desktop/screenshots/line_cmap.png | 4 +-- .../desktop/screenshots/line_collection.png | 4 +-- .../line_collection_cmap_values.png | 4 +-- ...ine_collection_cmap_values_qualitative.png | 4 +-- .../screenshots/line_collection_colors.png | 4 +-- .../screenshots/line_collection_slicing.png | 4 +-- .../desktop/screenshots/line_colorslice.png | 4 +-- .../desktop/screenshots/line_dataslice.png | 4 +-- .../screenshots/line_present_scaling.png | 3 -- examples/desktop/screenshots/line_stack.png | 4 +-- .../screenshots/multigraphic_gridplot.png | 3 -- examples/desktop/screenshots/scatter.png | 3 -- examples/desktop/screenshots/scatter_cmap.png | 3 -- .../desktop/screenshots/scatter_cmap_iris.png | 4 +-- .../screenshots/scatter_colorslice.png | 3 -- .../screenshots/scatter_colorslice_iris.png | 4 +-- .../desktop/screenshots/scatter_dataslice.png | 3 -- .../screenshots/scatter_dataslice_iris.png | 4 +-- examples/desktop/screenshots/scatter_iris.png | 4 +-- .../desktop/screenshots/scatter_present.png | 3 -- examples/desktop/screenshots/scatter_size.png | 4 +-- .../notebooks/screenshots/nb-astronaut.png | 4 +-- .../screenshots/nb-astronaut_RGB.png | 4 +-- examples/notebooks/screenshots/nb-camera.png | 4 +-- .../nb-image-widget-movie-set_data.png | 4 +-- .../nb-image-widget-movie-single-0-reset.png | 4 +-- .../nb-image-widget-movie-single-0.png | 4 +-- .../nb-image-widget-movie-single-279.png | 4 +-- ...e-widget-movie-single-50-window-max-33.png | 4 +-- ...-widget-movie-single-50-window-mean-13.png | 4 +-- ...-widget-movie-single-50-window-mean-33.png | 4 +-- ...ge-widget-movie-single-50-window-reset.png | 4 +-- .../nb-image-widget-movie-single-50.png | 4 +-- .../nb-image-widget-single-gnuplot2.png | 4 +-- .../screenshots/nb-image-widget-single.png | 4 +-- ...et-zfish-frame-50-frame-apply-gaussian.png | 4 +-- ...idget-zfish-frame-50-frame-apply-reset.png | 4 +-- ...ge-widget-zfish-frame-50-max-window-13.png | 4 +-- ...e-widget-zfish-frame-50-mean-window-13.png | 4 +-- ...ge-widget-zfish-frame-50-mean-window-5.png | 4 +-- .../nb-image-widget-zfish-frame-50.png | 4 +-- .../nb-image-widget-zfish-frame-99.png | 4 +-- ...ish-grid-frame-50-frame-apply-gaussian.png | 4 +-- ...-zfish-grid-frame-50-frame-apply-reset.png | 4 +-- ...dget-zfish-grid-frame-50-max-window-13.png | 4 +-- ...get-zfish-grid-frame-50-mean-window-13.png | 4 +-- ...dget-zfish-grid-frame-50-mean-window-5.png | 4 +-- .../nb-image-widget-zfish-grid-frame-50.png | 4 +-- .../nb-image-widget-zfish-grid-frame-99.png | 4 +-- ...e-widget-zfish-grid-init-mean-window-5.png | 4 +-- ...fish-grid-set_data-reset-indices-false.png | 4 +-- ...zfish-grid-set_data-reset-indices-true.png | 4 +-- ...-image-widget-zfish-init-mean-window-5.png | 4 +-- ...dget-zfish-mixed-rgb-cockatoo-frame-50.png | 4 +-- ...dget-zfish-mixed-rgb-cockatoo-set-data.png | 4 +-- ...get-zfish-mixed-rgb-cockatoo-windowrgb.png | 4 +-- .../notebooks/screenshots/nb-imagewidget.png | 3 -- .../notebooks/screenshots/nb-lines-3d.png | 4 +-- .../nb-lines-cmap-jet-values-cosine.png | 4 +-- .../screenshots/nb-lines-cmap-jet-values.png | 4 +-- .../screenshots/nb-lines-cmap-jet.png | 4 +-- .../screenshots/nb-lines-cmap-tab-10.png | 4 +-- .../nb-lines-cmap-viridis-values.png | 4 +-- .../screenshots/nb-lines-cmap-viridis.png | 4 +-- .../screenshots/nb-lines-cmap-white.png | 4 +-- .../notebooks/screenshots/nb-lines-colors.png | 4 +-- .../notebooks/screenshots/nb-lines-data.png | 4 +-- .../screenshots/nb-lines-underlay.png | 4 +-- examples/notebooks/screenshots/nb-lines.png | 4 +-- fastplotlib/graphics/_features/_image.py | 4 +-- tests/test_image_graphic.py | 5 +++ tests/test_texture_array.py | 4 +-- 87 files changed, 190 insertions(+), 182 deletions(-) create mode 100644 examples/desktop/image/image_small.py delete mode 100644 examples/desktop/screenshots/heatmap_cmap.png delete mode 100644 examples/desktop/screenshots/heatmap_data.png delete mode 100644 examples/desktop/screenshots/heatmap_vmin_vmax.png create mode 100644 examples/desktop/screenshots/image_small.png delete mode 100644 examples/desktop/screenshots/line_present_scaling.png delete mode 100644 examples/desktop/screenshots/multigraphic_gridplot.png delete mode 100644 examples/desktop/screenshots/scatter.png delete mode 100644 examples/desktop/screenshots/scatter_cmap.png delete mode 100644 examples/desktop/screenshots/scatter_colorslice.png delete mode 100644 examples/desktop/screenshots/scatter_dataslice.png delete mode 100644 examples/desktop/screenshots/scatter_present.png delete mode 100644 examples/notebooks/screenshots/nb-imagewidget.png diff --git a/examples/desktop/gridplot/multigraphic_gridplot.py b/examples/desktop/gridplot/multigraphic_gridplot.py index 3d8dbe4a8..edb0aaafd 100644 --- a/examples/desktop/gridplot/multigraphic_gridplot.py +++ b/examples/desktop/gridplot/multigraphic_gridplot.py @@ -5,7 +5,7 @@ Example showing a Figure with multiple subplots and multiple graphic types. """ -# test_example = true +# test_example = false # sphinx_gallery_pygfx_docs = 'screenshot' import fastplotlib as fpl @@ -58,9 +58,9 @@ def make_circle(center, radius: float, n_points: int = 75) -> np.ndarray: ] # add an image to overlay the circles on -img2 = np.ones((60, 60)) +img2 = iio.imread("imageio:coins.png")[10::5, 5::5] -figure["circles"].add_image(data=img2) +figure["circles"].add_image(data=img2, cmap="gray") # add the circles to the figure figure["circles"].add_line_collection( @@ -101,8 +101,8 @@ def make_circle(center, radius: float, n_points: int = 75) -> np.ndarray: gaussian_cloud2 = np.random.multivariate_normal(mean, covariance, n_points) # add the scatter graphics to the figure -figure["scatter"].add_scatter(data=gaussian_cloud, sizes=1, cmap="jet") -figure["scatter"].add_scatter(data=gaussian_cloud2, colors="r", sizes=1) +figure["scatter"].add_scatter(data=gaussian_cloud, sizes=2, cmap="jet") +figure["scatter"].add_scatter(data=gaussian_cloud2, colors="r", sizes=2) figure.show() diff --git a/examples/desktop/image/image_small.py b/examples/desktop/image/image_small.py new file mode 100644 index 000000000..95c263a28 --- /dev/null +++ b/examples/desktop/image/image_small.py @@ -0,0 +1,33 @@ +""" +Small Image +=========== + +Test image to verify dims +""" + +import numpy as np + +# test_example = true +# sphinx_gallery_pygfx_docs = 'hidden' + +import fastplotlib as fpl + +figure = fpl.Figure() + +data = np.array( + [[0, 1, 2], + [3, 4, 5]] +) +image_graphic = figure[0, 0].add_image(data) + +figure.show() + +figure.canvas.set_logical_size(700, 560) + +figure[0, 0].auto_scale() + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.run() diff --git a/examples/desktop/screenshots/gridplot.png b/examples/desktop/screenshots/gridplot.png index 6567c57da..315958673 100644 --- a/examples/desktop/screenshots/gridplot.png +++ b/examples/desktop/screenshots/gridplot.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6499a000911de783b69a7958a1bf2b0290b5a3117fe14ade792baca95557b2a7 -size 263883 +oid sha256:d43e6972bf76aa2de400616bde4275cd05d3a945475742ec7f63f7658628292b +size 264437 diff --git a/examples/desktop/screenshots/gridplot_non_square.png b/examples/desktop/screenshots/gridplot_non_square.png index 847ed65cb..689585b40 100644 --- a/examples/desktop/screenshots/gridplot_non_square.png +++ b/examples/desktop/screenshots/gridplot_non_square.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e4a0a002caf10e1e80ca0177bac4085e2f837ad3977f2546830acb42f0106f3f -size 173182 +oid sha256:703285790dc96500a7a376f6e78953c943643f4ecf3102182072c2bd0bf8190c +size 173753 diff --git a/examples/desktop/screenshots/heatmap.png b/examples/desktop/screenshots/heatmap.png index 1adcc1e1d..0514daf94 100644 --- a/examples/desktop/screenshots/heatmap.png +++ b/examples/desktop/screenshots/heatmap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3e742d06167a49ec80253cb3984da88e6d623dc645f93bcfbd1a82966ba44a84 -size 40051 +oid sha256:03b3ab1fc8aa602eb94beed1f5fa5712452ee802bb3230c4fd066d073bdd4ad2 +size 40100 diff --git a/examples/desktop/screenshots/heatmap_cmap.png b/examples/desktop/screenshots/heatmap_cmap.png deleted file mode 100644 index cee81dd30..000000000 --- a/examples/desktop/screenshots/heatmap_cmap.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8863461569f5b89d1443e3051a5512f3987487fcb9e057215d2f030a180fa09f -size 97996 diff --git a/examples/desktop/screenshots/heatmap_data.png b/examples/desktop/screenshots/heatmap_data.png deleted file mode 100644 index 316a73753..000000000 --- a/examples/desktop/screenshots/heatmap_data.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a975179e82893dbb04e4674310761e7b02bb62ae6abb1b89397720bddf96ae5f -size 19084 diff --git a/examples/desktop/screenshots/heatmap_vmin_vmax.png b/examples/desktop/screenshots/heatmap_vmin_vmax.png deleted file mode 100644 index 357683d82..000000000 --- a/examples/desktop/screenshots/heatmap_vmin_vmax.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9592f3724016db1b7431bc100b16bec175e197c111e7b442dc2255d51da3f5e8 -size 114957 diff --git a/examples/desktop/screenshots/image_cmap.png b/examples/desktop/screenshots/image_cmap.png index 9ad74e927..91124db6a 100644 --- a/examples/desktop/screenshots/image_cmap.png +++ b/examples/desktop/screenshots/image_cmap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:009326a4c3605622980bf80b20cc8cc807fa610858d155304285a1a5d478b56c -size 187517 +oid sha256:f18a55da8cede25dbb77b18e8cf374d158a66b823d029714983218e55ee68249 +size 187688 diff --git a/examples/desktop/screenshots/image_rgb.png b/examples/desktop/screenshots/image_rgb.png index 50dfe6761..8ae39eaad 100644 --- a/examples/desktop/screenshots/image_rgb.png +++ b/examples/desktop/screenshots/image_rgb.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c7e026664f350a4abd83297dc55a324a12ddcf8fd94625d26381d09a3fdd953b -size 216191 +oid sha256:3851bea9ee908a460750b40a0a5709aff1b28afa6adf11c9ad2ed8239958caa4 +size 216343 diff --git a/examples/desktop/screenshots/image_rgbvminvmax.png b/examples/desktop/screenshots/image_rgbvminvmax.png index 73707177c..478ce40fe 100644 --- a/examples/desktop/screenshots/image_rgbvminvmax.png +++ b/examples/desktop/screenshots/image_rgbvminvmax.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a6e18e9782514b3f525d8e7f8ed83699fae48fced2b633f3f1831e4b46751c44 -size 33627 +oid sha256:2ec8ddd362197ba802f8381d5baea226dc30689eee5e5dc744c2da710f0b3482 +size 33860 diff --git a/examples/desktop/screenshots/image_simple.png b/examples/desktop/screenshots/image_simple.png index 3857baf9d..c60293498 100644 --- a/examples/desktop/screenshots/image_simple.png +++ b/examples/desktop/screenshots/image_simple.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1ab7ea5e0aa322c8b4249df30a80cbf3d7b55e0b7a0418f4bed99f81d14fdd7e -size 187665 +oid sha256:216791f48cee8ddb9979ecc8b7b7435c0fe22c2734148c25314f1827a5c9ad66 +size 187868 diff --git a/examples/desktop/screenshots/image_small.png b/examples/desktop/screenshots/image_small.png new file mode 100644 index 000000000..cda3a2584 --- /dev/null +++ b/examples/desktop/screenshots/image_small.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3f2af0ed16ec82842ad9d45d5a8b6189e77a2f2f8adb21dd82bc1636979cd2c7 +size 2325 diff --git a/examples/desktop/screenshots/image_vminvmax.png b/examples/desktop/screenshots/image_vminvmax.png index 73707177c..478ce40fe 100644 --- a/examples/desktop/screenshots/image_vminvmax.png +++ b/examples/desktop/screenshots/image_vminvmax.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a6e18e9782514b3f525d8e7f8ed83699fae48fced2b633f3f1831e4b46751c44 -size 33627 +oid sha256:2ec8ddd362197ba802f8381d5baea226dc30689eee5e5dc744c2da710f0b3482 +size 33860 diff --git a/examples/desktop/screenshots/line.png b/examples/desktop/screenshots/line.png index c21a522e7..605540225 100644 --- a/examples/desktop/screenshots/line.png +++ b/examples/desktop/screenshots/line.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aa12348340af55e7472ceb9986a73be35b398761d057e579e199055fe6bcd9ef -size 27740 +oid sha256:d7f3736d4464cfd942e87d21be1a18d09f5d0d239a7e1c7679e918dcc5c9331c +size 26701 diff --git a/examples/desktop/screenshots/line_cmap.png b/examples/desktop/screenshots/line_cmap.png index 5e092c844..cab91220f 100644 --- a/examples/desktop/screenshots/line_cmap.png +++ b/examples/desktop/screenshots/line_cmap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7177ca064170e815870192afd9b01dd9e23332b7cbdd962592932dacac842a76 -size 29567 +oid sha256:1f154346cffbaa0957a9986d8b7beef417b66ef0cec7dbed3c20780d91425567 +size 29231 diff --git a/examples/desktop/screenshots/line_collection.png b/examples/desktop/screenshots/line_collection.png index cc6bdcd72..f3fb5052b 100644 --- a/examples/desktop/screenshots/line_collection.png +++ b/examples/desktop/screenshots/line_collection.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8762da25c8d01ad135288e80c91750522e32c13d824862386ad3612f63e29cfc -size 93068 +oid sha256:ca08ce57a1cf57c334add1c41351f3b823f06ad8da463017d0815cf7cfea03b3 +size 91085 diff --git a/examples/desktop/screenshots/line_collection_cmap_values.png b/examples/desktop/screenshots/line_collection_cmap_values.png index 5dbe1cfda..33af5b917 100644 --- a/examples/desktop/screenshots/line_collection_cmap_values.png +++ b/examples/desktop/screenshots/line_collection_cmap_values.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ba04a638118cc6a2dff2a253fa40fae099aa2acfe347570ff4a0044403a2a5b7 -size 59102 +oid sha256:12ddca084dc83478c6b3d263f11f456f8b81e7a8a291d6b9024dbcecbfb049c0 +size 57107 diff --git a/examples/desktop/screenshots/line_collection_cmap_values_qualitative.png b/examples/desktop/screenshots/line_collection_cmap_values_qualitative.png index 00fde0fdd..57f45605b 100644 --- a/examples/desktop/screenshots/line_collection_cmap_values_qualitative.png +++ b/examples/desktop/screenshots/line_collection_cmap_values_qualitative.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a592bf90017c25abf163936c4c475b8a96ac0775e450c8d7355011fe20bbfb22 -size 62416 +oid sha256:74d5999cdd0b992f73bafb1bd74c318fd9cf058aed232068ab7dcb76d86df556 +size 60881 diff --git a/examples/desktop/screenshots/line_collection_colors.png b/examples/desktop/screenshots/line_collection_colors.png index cf7880a6c..9c27854ed 100644 --- a/examples/desktop/screenshots/line_collection_colors.png +++ b/examples/desktop/screenshots/line_collection_colors.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:052a46223f73099eacf2b4672348c1110c14b22a85131b73561ac1feb8e9c796 -size 49693 +oid sha256:a152331c51ed5440c5faf2a59439d90832521fbb1498d9635ddae088219ca353 +size 46941 diff --git a/examples/desktop/screenshots/line_collection_slicing.png b/examples/desktop/screenshots/line_collection_slicing.png index 54d2a1098..1145e84dc 100644 --- a/examples/desktop/screenshots/line_collection_slicing.png +++ b/examples/desktop/screenshots/line_collection_slicing.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:07a02cb8a93527e913d35a0caed5dfc2feb01234d63b73ba3c3e244bbfda5e59 -size 129082 +oid sha256:bdfdc2b2c5799e814ef5a1e32748a2a6d2dd88005f6fa0d9c456b8dadfada5db +size 124609 diff --git a/examples/desktop/screenshots/line_colorslice.png b/examples/desktop/screenshots/line_colorslice.png index 2bcf008a5..825ce8e3f 100644 --- a/examples/desktop/screenshots/line_colorslice.png +++ b/examples/desktop/screenshots/line_colorslice.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:69c4994a1259511a6480c2227a31472ab527f33f747cd44b9018f409e03ba1f1 -size 32353 +oid sha256:de5a56c96a062ed0ec154ae21f3a3a67087e0c8aef6d8e4681c67a016424144a +size 31971 diff --git a/examples/desktop/screenshots/line_dataslice.png b/examples/desktop/screenshots/line_dataslice.png index 395d2f1bf..71c3d1918 100644 --- a/examples/desktop/screenshots/line_dataslice.png +++ b/examples/desktop/screenshots/line_dataslice.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:268a0e9a6a32e015466b7a72b8cb9675597db2b3212edaf818871a27602a751c -size 44986 +oid sha256:e4dece6f721068a1ae37c6830110f97df64ea57c467ef4d7f42b73575d2ee476 +size 43995 diff --git a/examples/desktop/screenshots/line_present_scaling.png b/examples/desktop/screenshots/line_present_scaling.png deleted file mode 100644 index ba7142106..000000000 --- a/examples/desktop/screenshots/line_present_scaling.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:06f7dd45eb495fecfcf46478c6430a658640ceb2855c4797bc184cf4134571e3 -size 20180 diff --git a/examples/desktop/screenshots/line_stack.png b/examples/desktop/screenshots/line_stack.png index edaae0d0a..026b1f61e 100644 --- a/examples/desktop/screenshots/line_stack.png +++ b/examples/desktop/screenshots/line_stack.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2b3ae8275e536669dfabdc8c3b715d1ca9c73f17062ab1f90f9650bd86b1c4f7 -size 91285 +oid sha256:1384f1030e81fc05b24db040ac47a3bd62663358dcbdd0e77b3d675d5edd4357 +size 86938 diff --git a/examples/desktop/screenshots/multigraphic_gridplot.png b/examples/desktop/screenshots/multigraphic_gridplot.png deleted file mode 100644 index e814eadde..000000000 --- a/examples/desktop/screenshots/multigraphic_gridplot.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e879a39032759006685eff1d5c2331a66b9126b13bc148734c3622bd6cdc68a7 -size 163902 diff --git a/examples/desktop/screenshots/scatter.png b/examples/desktop/screenshots/scatter.png deleted file mode 100644 index 195c5ae64..000000000 --- a/examples/desktop/screenshots/scatter.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b0f6c3d3b5e7216cc17c70ad3ff114092b1556fb9dcd171ae737f28d52ce51c9 -size 106634 diff --git a/examples/desktop/screenshots/scatter_cmap.png b/examples/desktop/screenshots/scatter_cmap.png deleted file mode 100644 index 55442aceb..000000000 --- a/examples/desktop/screenshots/scatter_cmap.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0655f8f5f1fdeecbd5d097cf90a4776dd8125a16d0e8edb86aa37f77daba0d9b -size 107892 diff --git a/examples/desktop/screenshots/scatter_cmap_iris.png b/examples/desktop/screenshots/scatter_cmap_iris.png index eb8802c0e..2a6ae7016 100644 --- a/examples/desktop/screenshots/scatter_cmap_iris.png +++ b/examples/desktop/screenshots/scatter_cmap_iris.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:088579410d42768e68d7e7b298d2ed9e409eaff28ba5a2dd2ec8283e5994b862 -size 31599 +oid sha256:b781b387476028a5eaf2083c40d57306afbcbc2a6754dce6fb66cf71ddd689d1 +size 31719 diff --git a/examples/desktop/screenshots/scatter_colorslice.png b/examples/desktop/screenshots/scatter_colorslice.png deleted file mode 100644 index 7c43dd6cb..000000000 --- a/examples/desktop/screenshots/scatter_colorslice.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d3319afffb554eb624d9bba5b87daf331b619ac5ec5006f13e479b613f43ca32 -size 72083 diff --git a/examples/desktop/screenshots/scatter_colorslice_iris.png b/examples/desktop/screenshots/scatter_colorslice_iris.png index 8692f53b3..45c5d940c 100644 --- a/examples/desktop/screenshots/scatter_colorslice_iris.png +++ b/examples/desktop/screenshots/scatter_colorslice_iris.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f21ab8499384298a74db3e5fbb5aa0113f714d6f77e1e3d614da3d278a2c7a78 -size 13478 +oid sha256:68f93c08d361232c9be2220a68db8659c9c3c81c3cdb4e1a1ce9b366fb28b4f5 +size 13215 diff --git a/examples/desktop/screenshots/scatter_dataslice.png b/examples/desktop/screenshots/scatter_dataslice.png deleted file mode 100644 index 7a1429663..000000000 --- a/examples/desktop/screenshots/scatter_dataslice.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0ecc6454dd197e6a3f146d0a04881db91b099673b9d74903536ca103b2418c89 -size 15657 diff --git a/examples/desktop/screenshots/scatter_dataslice_iris.png b/examples/desktop/screenshots/scatter_dataslice_iris.png index c0c6aa5e7..1121d032c 100644 --- a/examples/desktop/screenshots/scatter_dataslice_iris.png +++ b/examples/desktop/screenshots/scatter_dataslice_iris.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2032ce0b3c39f35e9dc37bc54fac016dc43aef716cfc65e6d65fd7fc977e74cf -size 14473 +oid sha256:5d662e151062a136a17dac1f8693ba13f41daac05e91e32ee9c7053715f9ee17 +size 14437 diff --git a/examples/desktop/screenshots/scatter_iris.png b/examples/desktop/screenshots/scatter_iris.png index aa1d2b743..7d107d964 100644 --- a/examples/desktop/screenshots/scatter_iris.png +++ b/examples/desktop/screenshots/scatter_iris.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:320a02df6de51235b1912b26df4ea7633e9513df4d4034605a4e00724ceb57dc -size 14131 +oid sha256:4fc88e52cc4ede6d1453746461da645f8b3df0a3099155caf639768a5ad4424c +size 14148 diff --git a/examples/desktop/screenshots/scatter_present.png b/examples/desktop/screenshots/scatter_present.png deleted file mode 100644 index 08bc610b3..000000000 --- a/examples/desktop/screenshots/scatter_present.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:bd072918f21ed0ce4ea4e1f4499ec1ff66d867cfdc0ecd6b3ed8092141cd348e -size 14195 diff --git a/examples/desktop/screenshots/scatter_size.png b/examples/desktop/screenshots/scatter_size.png index 2d4e559fe..66b31cab9 100644 --- a/examples/desktop/screenshots/scatter_size.png +++ b/examples/desktop/screenshots/scatter_size.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a96a5da476b22f3ebd42ec927c3d1c1dee4dc20f261a97feaced69ed4142c645 -size 35484 +oid sha256:9d1eeb96dc1f52c4d48889a8b00387387cccb7b83d479c1c4b47789b281a1cd5 +size 34222 diff --git a/examples/notebooks/screenshots/nb-astronaut.png b/examples/notebooks/screenshots/nb-astronaut.png index 3e55979ee..9c28b6cfa 100644 --- a/examples/notebooks/screenshots/nb-astronaut.png +++ b/examples/notebooks/screenshots/nb-astronaut.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a0fdb5b319347b4db4611dcf92cf08359c938f42a64b05d0dd163e0ca289e3c3 -size 112299 +oid sha256:afb405dfcd90d9165b4be8c2b79a82b45964debb119d25851835b8a6e2f18785 +size 111986 diff --git a/examples/notebooks/screenshots/nb-astronaut_RGB.png b/examples/notebooks/screenshots/nb-astronaut_RGB.png index fbb514e3e..1939c12d7 100644 --- a/examples/notebooks/screenshots/nb-astronaut_RGB.png +++ b/examples/notebooks/screenshots/nb-astronaut_RGB.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2d312ce9097114bc32886c0370861bcf7deebfb4fda99e03817ebec2226eabdc -size 110338 +oid sha256:2f86ef886266279ace4672904860bdaeee49dd23498998c8f68ae0b36cecc529 +size 110588 diff --git a/examples/notebooks/screenshots/nb-camera.png b/examples/notebooks/screenshots/nb-camera.png index 5629bd211..cfdf2673e 100644 --- a/examples/notebooks/screenshots/nb-camera.png +++ b/examples/notebooks/screenshots/nb-camera.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0a415917cc16f09ab7b78eea5e5579d7dd45b6d92e80d87ba0970e9dd0568eb2 -size 77419 +oid sha256:124e52fdb8c200be3295f79331f25a51d423d159a7f8cde1863daa00e54c0894 +size 77665 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-set_data.png b/examples/notebooks/screenshots/nb-image-widget-movie-set_data.png index 486c89963..e49ad3c38 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-set_data.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-set_data.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3dbb4d04175c5603ff7e56a04438c8f0cfff7deff61889a06c342cedc04ac323 -size 43172 +oid sha256:5acd7eeccbf47af45aa8306befb040f9b53d21f1727e7366b536d73261b407ce +size 43494 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-0-reset.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-0-reset.png index 1369669e6..dfcb98736 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-0-reset.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-0-reset.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7049dc9344a8d51bcfc3ba7f96a25c84db27f5072f14e8845cd2f01c3f4f5310 -size 133683 +oid sha256:9ca702fffc4eebea5ba31b77062b60f848c2e5d689568d16b39a62561a0b8b73 +size 134201 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-0.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-0.png index 1369669e6..dfcb98736 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-0.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7049dc9344a8d51bcfc3ba7f96a25c84db27f5072f14e8845cd2f01c3f4f5310 -size 133683 +oid sha256:9ca702fffc4eebea5ba31b77062b60f848c2e5d689568d16b39a62561a0b8b73 +size 134201 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-279.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-279.png index 5817cd299..787e2757e 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-279.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-279.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1ca687c654679ee9d47a053196fb65d92105ed6cce76f0d2d1775582053e7b07 -size 168750 +oid sha256:73bdd6a91ab679dcf237626bc7d3edd267d402ea8de2b6e2c3db7bba9b9418ac +size 169211 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-max-33.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-max-33.png index 2f2e310d0..ca2357ddd 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-max-33.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-max-33.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dd51516d18f550f62b9f2c989cfae82d267eb6de1d8757cf44949bb41c3b13c2 -size 148408 +oid sha256:afb9c5bfbfbc2ce800d613f779021b0a93d098f415d89157f994cc9b1632361b +size 149454 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-13.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-13.png index 39a4f1274..ac3f4cb61 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9613c10e63261ddbcf3274a9804bdf3a1be0734c1190720663b01f8b2ae48389 -size 124532 +oid sha256:c3c07d75cd4673e411d814c1dab1e62d6543b26c89f208eed15ccb941bbe3ab2 +size 124795 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-33.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-33.png index b9feacb82..3a77efced 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-33.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-33.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8fa86b26f11bd9655c60e43ddd88ada618cea1ba06b3dcddcb408569c09a7750 -size 113897 +oid sha256:5f39d68bbc2c7d52cc13609ff60274dbfe49bea4d4a03cfbf1d1c15cf7fb8e8c +size 114013 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-reset.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-reset.png index ae280a970..e34f9deb3 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-reset.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-reset.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aca573ba6dcda8ad8f018a96790b1b1aa5da25fe9584a57c2c946e1cf2df9e5c -size 145275 +oid sha256:2825af49b1964fb76dcf2ccd494bb61623df4d5fffad7be30cf389b9b7e6d4bf +size 146186 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50.png index ae280a970..e34f9deb3 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aca573ba6dcda8ad8f018a96790b1b1aa5da25fe9584a57c2c946e1cf2df9e5c -size 145275 +oid sha256:2825af49b1964fb76dcf2ccd494bb61623df4d5fffad7be30cf389b9b7e6d4bf +size 146186 diff --git a/examples/notebooks/screenshots/nb-image-widget-single-gnuplot2.png b/examples/notebooks/screenshots/nb-image-widget-single-gnuplot2.png index 02423a02a..4cd3248a0 100644 --- a/examples/notebooks/screenshots/nb-image-widget-single-gnuplot2.png +++ b/examples/notebooks/screenshots/nb-image-widget-single-gnuplot2.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d7384d1a69629cfcdbebdc9e9e6a152383446f3cb696e69a11543253cdde2e64 -size 434060 +oid sha256:aff55757a29cac06c1c158599681e8c10e27fd772425c6b3137a06d5d604f95e +size 435106 diff --git a/examples/notebooks/screenshots/nb-image-widget-single.png b/examples/notebooks/screenshots/nb-image-widget-single.png index 408739d6e..dd37a74db 100644 --- a/examples/notebooks/screenshots/nb-image-widget-single.png +++ b/examples/notebooks/screenshots/nb-image-widget-single.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b57dffe179b6f52d204968085c885d70183d1f6a9a3f5a1dc2d005229b7acd01 -size 404179 +oid sha256:1e70812decf8d1c591b1d97c24346159255e8b5cba5722f9c4d67c5b5aa92a8a +size 403368 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-gaussian.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-gaussian.png index 596548486..9be76e5bd 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-gaussian.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-gaussian.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:da1c660e4fb779ac6a4baed3d329cf80274981288ea076f243bb43aee7fb8eff -size 157731 +oid sha256:8d6b97c351f51ee8b0429e7001ba16cb3862c9cfc4f4e0f0227524b8c20d5906 +size 157300 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-reset.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-reset.png index 1318be413..c877ac887 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-reset.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-reset.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0d4f6407c3b029b01088fab9522a21f7d90d7a87def1ecbbb45f0fb4f8508f87 -size 69106 +oid sha256:d74649c5ca7b0401a8e42ffe9b73cebeebdce80953c4790f44a99bfe6624902b +size 71618 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-max-window-13.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-max-window-13.png index e5fdbdd28..7613ae2a9 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-max-window-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-max-window-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e4176109805f4f521a1b630b68df1dce80a63b82a5ed01a6ba4c2cae0dfeb6bd -size 184423 +oid sha256:e9c99c189dbfffbc3fa24fb6f48015518a2e1c3e681191abb45cf4e29185dcff +size 196855 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-13.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-13.png index bf9548962..e803cdc68 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:12df56b1045cdaddb94355b7e960aa58137a44eff4ff22aab3596c53ea7944c8 -size 179403 +oid sha256:916800ae449d12e875f14be3d13a75db85339524dbd594f9963074b9fc5316ae +size 177769 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-5.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-5.png index 7b3e6bfba..5b5ef1009 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-5.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d5fd2f0918f4a29769ebb572f5163abb52667cf24e89efdd1d99bc57a0f5f607 -size 140124 +oid sha256:3006a07bfbf6276967ca55809788f61a1852db9851f959cc1db00016a9b3747f +size 140019 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50.png index a72245f3b..4e8803a7b 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:981c925f52ae8789f7f0d24ef3fe34efb7a08f221a7bc6079dd12f01099c3d25 -size 75054 +oid sha256:3e55ffde023955d00804a7272a4e963b4d2a74b74fb401962d32c1a29d76bc24 +size 80880 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-99.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-99.png index 19c19dc1f..061195a98 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-99.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-99.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6a5ecd1f966250ead16a96df996ff39877b4ee28534b7724a4a8e1db9c8984d2 -size 58334 +oid sha256:405495c384aa52d6e3c8a65237876682f4be62967dce1b6af526e4d069fa44d3 +size 62621 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-gaussian.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-gaussian.png index bcf663279..0da3abb21 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-gaussian.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-gaussian.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dbe1375ae8d2f348ad5d7e030fa67d45c250c6ed263c67179d542d0bd903e0d3 -size 177334 +oid sha256:7b30ef1dca9711bd72eb28f0035552f93e143a683f818c3f2aec8df4323306e4 +size 178459 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-reset.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-reset.png index 963290515..21ea17c27 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-reset.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-reset.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0ff341df374d816411e58c82d432e98a9f4cec1551725dafcd65cdb0c43edb12 -size 138235 +oid sha256:b3e8fc84f5ea2d5a93bc02e19965781fbe9ec697b660430a5203cb1c91803974 +size 142748 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-max-window-13.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-max-window-13.png index a049a484c..ece0fee5f 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-max-window-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-max-window-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bd4c51f7e07e46d7c660d28563aff1b7d3759387fc10db10addca29dfc0919b0 -size 365838 +oid sha256:7b01f2385991f4941f35d1b913fe54c72cbe42c62522ab181ddb2466b2f2be8d +size 372324 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-13.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-13.png index ada15017c..93dd3b254 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1d13cc8a32b40f5c721ab30020632d7dc1c679c8c8e5857476176e986de18ad3 -size 211240 +oid sha256:4bac6aedfebab2bf97497dbecd17f59b36cb78b27dcdb1547c6d78f902d5f89b +size 213579 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-5.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-5.png index 2e71fd30d..b6392f095 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-5.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:39044f4bb54038eee17f0d75940fd848b304629858d9e747ac0c81ce076d3c25 -size 199075 +oid sha256:5458f9488a19207c7d4f8a971de06a028dfb22e4a2847c3a0b1e1f45c41109f0 +size 200566 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50.png index 690b1c578..8165824cb 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:109a4c8d708114e1b36d1a9fa129dbd9a768617baa953f37500310c0968b688a -size 154169 +oid sha256:8588b720e7d970a0c5d0b9e43c68ee0695d7ced8c51797d50143b0737d3ae2c1 +size 160340 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-99.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-99.png index 3e577698c..f46e58b4f 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-99.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-99.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:568ae05e8889cec56530db0149613826f2697f55d8252cffbd32ff692b565fcf -size 141338 +oid sha256:b86bc324f13ca3a958d0db80251874478e0191b0c30c301f3022913e7b1f62d5 +size 147084 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-init-mean-window-5.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-init-mean-window-5.png index 1ab48d117..8e3e7e2de 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-init-mean-window-5.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-init-mean-window-5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e7847dc083d6df2b20788b42153674c557b627b75db74d9446b06e165aa5a50a -size 182713 +oid sha256:9993fe8f8d3e6d6e48d863b251fdd7b37926ba7b97b2d70683cbc3ab45910c99 +size 184668 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-false.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-false.png index 0b0f05fc3..aae5c9066 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-false.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-false.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:839dd3fdc9db98d7044d88e816c179bff34f30584ba26ce7a96ea3b35fc3374e -size 122463 +oid sha256:f4cdb28c8aa72b1cd968f4f78f3c2413d2338b6a2b5c200df02ecdd2bce1568b +size 126337 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-true.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-true.png index 534403b1e..346495cfc 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-true.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-true.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f7088e11517a4a78492d16746ac8101b2e5e9142ebd61966030c555ab173443e -size 126267 +oid sha256:19000f2cc6d78e2cc18dd5213778e595ee6710ca3fcd71cb4cbe6286b42b1e8b +size 130255 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-init-mean-window-5.png b/examples/notebooks/screenshots/nb-image-widget-zfish-init-mean-window-5.png index 94993c688..2298f904e 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-init-mean-window-5.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-init-mean-window-5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:709c7ec07e3e37e0415fad3fa542729d2896e8e546b6ea8d1373e7b46005bc26 -size 97278 +oid sha256:4a141cd3e0d3647accb18c55d84026d16ca2280611b80682737a61151dd9c377 +size 99397 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-frame-50.png b/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-frame-50.png index 27c693c1a..58f4fd87e 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-frame-50.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-frame-50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c66db583e0d455319b665d35a8c5c8a5f717653c11311cdaba90e2c85e64235f -size 58941 +oid sha256:cbd3cb8399c32cc611a86bb482782bfe55393ec73f2c2a3f4eb0d4e8af2442d6 +size 58842 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-set-data.png b/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-set-data.png index 7444d7dbf..0eff22834 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-set-data.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-set-data.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:36867cd793634d00c46c782911abae6e7c579067aeeed891e40ddedbb0c228d9 -size 56505 +oid sha256:e6e201ecce9db938796d1fc710a154ae8bc49e0a7e1f51d9af586f29f4ee63de +size 57116 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-windowrgb.png b/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-windowrgb.png index 3941f3120..03a1fc30c 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-windowrgb.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-windowrgb.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8b23f3655fcfcd85f6138f4de5cedf228a6dbad0c8cff0c87900042f83b8f409 -size 55269 +oid sha256:608c9a0b1466886652299887a4f0f16a77dfb400fc46200a453df25c5a0e7016 +size 55903 diff --git a/examples/notebooks/screenshots/nb-imagewidget.png b/examples/notebooks/screenshots/nb-imagewidget.png deleted file mode 100644 index 9acfdb0f9..000000000 --- a/examples/notebooks/screenshots/nb-imagewidget.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0f7a4a260ef4f9f2bdee9adab4ed376147cff39fcdd2f07eaf6e87e8b899f7d3 -size 89842 diff --git a/examples/notebooks/screenshots/nb-lines-3d.png b/examples/notebooks/screenshots/nb-lines-3d.png index 6c19c965c..d1e46a618 100644 --- a/examples/notebooks/screenshots/nb-lines-3d.png +++ b/examples/notebooks/screenshots/nb-lines-3d.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c6ab8d74c980a02505d5d626d170b60847ce2b733ea5b47278a1f48228011a38 -size 14181 +oid sha256:91f74b1ad6d4eeb08da8a33bfccfc0e9e80d48fc33b2a783cb94890f3c603a94 +size 14131 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-jet-values-cosine.png b/examples/notebooks/screenshots/nb-lines-cmap-jet-values-cosine.png index f75d83ba4..db1a0e658 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-jet-values-cosine.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-jet-values-cosine.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d2b87297587b5f76062132370fdf7e8318cbde68d38d12a0179548f9790c2af8 -size 11765 +oid sha256:58af931da3307204f2699b2ac04d8546b93aa0b4d3c058ab6d181656fd79fae8 +size 11674 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-jet-values.png b/examples/notebooks/screenshots/nb-lines-cmap-jet-values.png index a8229cfc8..9bb734365 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-jet-values.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-jet-values.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:99f6a7a9a1399aaf8d1070812c41f03f80ba41812a1acf9d4bdd17641bc589f4 -size 13038 +oid sha256:9949949767455061caa08b96dfdf0948d511d604d39ded4a028a9a50deca9797 +size 12990 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-jet.png b/examples/notebooks/screenshots/nb-lines-cmap-jet.png index ccc897f66..10f9252f3 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-jet.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-jet.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cac3d7cb23d7ec207cdf7fd7032037c7749e30563754d6c2cac50d67a7731a2d -size 11086 +oid sha256:c04746bb9c6e168644981e808b83b878d5d72e2101f441979765c74bb36c087a +size 10979 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-tab-10.png b/examples/notebooks/screenshots/nb-lines-cmap-tab-10.png index 8ddc44ead..a769ff769 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-tab-10.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-tab-10.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d38a2c27e5b598883c40b44e45b347f029a7369b638bc4111ef2d69978245378 -size 9943 +oid sha256:704cddf180de18dfc02cccced26dc57a7d8bff3938ceaf5ca9b6db7ccaed5928 +size 9582 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-viridis-values.png b/examples/notebooks/screenshots/nb-lines-cmap-viridis-values.png index 6bf270874..861efcef5 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-viridis-values.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-viridis-values.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d0ead67733470d6afcb2228645ee1a056acd525d369d74c16b1054783d6ab4f5 -size 10155 +oid sha256:67310ed0deb418bf0d6d10e1184e902f928f0e914518b91c23e948f3bb9e7b25 +size 9850 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-viridis.png b/examples/notebooks/screenshots/nb-lines-cmap-viridis.png index 4f99e5fb5..2d71b4428 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-viridis.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-viridis.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:15f4decaa6ba08f09a988771c5dc433629cb9e3bccfa08f820268366a3ff06a6 -size 13761 +oid sha256:6295649505902ac1f37ae6453e278dbbcdacb64426f1c51e27e16ef38650f8a8 +size 13725 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-white.png b/examples/notebooks/screenshots/nb-lines-cmap-white.png index 85e43aa5e..b450a8ea4 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-white.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-white.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8a623f31d28533cc6052143856d0ef2e827b42891397fa4ac20d358f44d751d7 -size 8332 +oid sha256:a1abc26476bbabf31094bd70929afc918e4064a1996d7742adb716ed6e9c2617 +size 7532 diff --git a/examples/notebooks/screenshots/nb-lines-colors.png b/examples/notebooks/screenshots/nb-lines-colors.png index ab50dfaaf..88fef4e39 100644 --- a/examples/notebooks/screenshots/nb-lines-colors.png +++ b/examples/notebooks/screenshots/nb-lines-colors.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0cbcafef567bff33762d64bcf6f83c7a1982015ea21d508b1e6564cb0d8435d2 -size 27420 +oid sha256:bbbb1b63c69ef4061f0b64fc2360e0c613ee4732d581929657068f55141d6fd9 +size 27274 diff --git a/examples/notebooks/screenshots/nb-lines-data.png b/examples/notebooks/screenshots/nb-lines-data.png index 88f1001ad..b8c5bf582 100644 --- a/examples/notebooks/screenshots/nb-lines-data.png +++ b/examples/notebooks/screenshots/nb-lines-data.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f62dc16cd1ea212cf29974a64e6ad2dd3d7ae10d7b3eef573c25316bbe89b097 -size 38740 +oid sha256:6f677a3c0a1b2fb57771af6118d45d23b1d86f88d3431ca06ef89b79a48dad06 +size 38880 diff --git a/examples/notebooks/screenshots/nb-lines-underlay.png b/examples/notebooks/screenshots/nb-lines-underlay.png index f9650e253..93edd81d6 100644 --- a/examples/notebooks/screenshots/nb-lines-underlay.png +++ b/examples/notebooks/screenshots/nb-lines-underlay.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ec7881cc0084b0ea2182f8d4f6f23388a4d48b88dbbf9c06b9dc2fa8125989b7 -size 49553 +oid sha256:35e0ea48cac0242e79da491629bda9fccedb94814e8d3d1188323c7d9668e513 +size 49940 diff --git a/examples/notebooks/screenshots/nb-lines.png b/examples/notebooks/screenshots/nb-lines.png index 7df6ea14f..e28486bf4 100644 --- a/examples/notebooks/screenshots/nb-lines.png +++ b/examples/notebooks/screenshots/nb-lines.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b925337438aa24f0179a8b5c1688e154679fa002402a16746b6bb3cd6684af1e -size 24246 +oid sha256:17ee8c3de59b9e80d66c30d61287a38ac06ee996833f32648506a6bf1ebb0da8 +size 23317 diff --git a/fastplotlib/graphics/_features/_image.py b/fastplotlib/graphics/_features/_image.py index e31184c4b..2d93745bf 100644 --- a/fastplotlib/graphics/_features/_image.py +++ b/fastplotlib/graphics/_features/_image.py @@ -118,8 +118,8 @@ def __next__(self) -> tuple[pygfx.Texture, tuple[int, int], tuple[slice, slice]] chunk_index = (chunk_row, chunk_col) # stop indices of big data array for this chunk - row_stop = min(self.value.shape[0] - 1, data_row_start + WGPU_MAX_TEXTURE_SIZE) - col_stop = min(self.value.shape[1] - 1, data_col_start + WGPU_MAX_TEXTURE_SIZE) + row_stop = min(self.value.shape[0], data_row_start + WGPU_MAX_TEXTURE_SIZE) + col_stop = min(self.value.shape[1], data_col_start + WGPU_MAX_TEXTURE_SIZE) # row and column slices that slice the data for this chunk from the big data array data_slice = (slice(data_row_start, row_stop), slice(data_col_start, col_stop)) diff --git a/tests/test_image_graphic.py b/tests/test_image_graphic.py index 9f89e8aa8..541129079 100644 --- a/tests/test_image_graphic.py +++ b/tests/test_image_graphic.py @@ -79,8 +79,13 @@ def test_gray(): "cmap_interpolation", ) + # make sure entire data is the same npt.assert_almost_equal(ig.data.value, GRAY_IMAGE) + # since this entire image is under the wgpu max texture limit, + # the entire image should be in the single Texture buffer + npt.assert_almost_equal(ig.data.buffer[0, 0].data, GRAY_IMAGE) + ig.cmap = "viridis" assert ig.cmap == "viridis" check_event(graphic=ig, feature="cmap", value="viridis") diff --git a/tests/test_texture_array.py b/tests/test_texture_array.py index 5aecf49a5..e1a6a1753 100644 --- a/tests/test_texture_array.py +++ b/tests/test_texture_array.py @@ -54,10 +54,10 @@ def check_texture_array( data_col_start_index = chunk_col * WGPU_MAX_TEXTURE_SIZE data_row_stop_index = min( - data.shape[0] - 1, data_row_start_index + WGPU_MAX_TEXTURE_SIZE + data.shape[0], data_row_start_index + WGPU_MAX_TEXTURE_SIZE ) data_col_stop_index = min( - data.shape[1] - 1, data_col_start_index + WGPU_MAX_TEXTURE_SIZE + data.shape[1], data_col_start_index + WGPU_MAX_TEXTURE_SIZE ) row_slice = slice(data_row_start_index, data_row_stop_index) From a8bc4ee85cb36efa9e7ac49f2130f6ee9b865033 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Tue, 18 Jun 2024 22:23:57 -0400 Subject: [PATCH 082/185] Figure.export() (#531) --- docs/source/api/layouts/figure.rst | 1 + fastplotlib/layouts/_figure.py | 47 ++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/docs/source/api/layouts/figure.rst b/docs/source/api/layouts/figure.rst index a2d5e5758..817284e18 100644 --- a/docs/source/api/layouts/figure.rst +++ b/docs/source/api/layouts/figure.rst @@ -37,6 +37,7 @@ Methods Figure.add_animations Figure.clear Figure.close + Figure.export Figure.remove_animation Figure.render Figure.show diff --git a/fastplotlib/layouts/_figure.py b/fastplotlib/layouts/_figure.py index 17bb28095..d330c6928 100644 --- a/fastplotlib/layouts/_figure.py +++ b/fastplotlib/layouts/_figure.py @@ -128,6 +128,7 @@ def __init__( # if controller instances have been specified for each subplot if controllers is not None: + # one controller for all subplots if isinstance(controllers, pygfx.Controller): controllers = [controllers] * len(self) @@ -579,6 +580,52 @@ def clear(self): for subplot in self: subplot.clear() + def export(self, uri: str | Path | bytes, **kwargs): + """ + Use ``imageio`` for writing the current Figure to a file, or return a byte string. + Must have ``imageio`` installed. + + Parameters + ---------- + uri: str | Path | bytes + + kwargs: passed to imageio.v3.imwrite, see: https://imageio.readthedocs.io/en/stable/_autosummary/imageio.v3.imwrite.html + + Returns + ------- + None | bytes + see https://imageio.readthedocs.io/en/stable/_autosummary/imageio.v3.imwrite.html + """ + try: + import imageio.v3 as iio + except ModuleNotFoundError: + raise ImportError( + "imageio is required to use Figure.export(). Install it using pip or conda:\n" + "pip install imageio\n" + "conda install -c conda-forge imageio\n" + ) + else: + snapshot = self.renderer.snapshot() + remove_alpha = True + + # image formats that support alpha channel: + # https://en.wikipedia.org/wiki/Alpha_compositing#Image_formats_supporting_alpha_channels + alpha_support = [".png", ".exr", ".tiff", ".tif", ".gif", ".jxl", ".svg"] + + if isinstance(uri, str): + if any([uri.endswith(ext) for ext in alpha_support]): + remove_alpha = False + + elif isinstance(uri, Path): + if uri.suffix in alpha_support: + remove_alpha = False + + if remove_alpha: + # remove alpha channel if it's not supported + snapshot = snapshot[..., :-1].shape + + return iio.imwrite(uri, snapshot, **kwargs) + def _get_iterator(self): return product(range(self.shape[0]), range(self.shape[1])) From 385aced6b1e86e26df4a9c2639121bccd3f0c221 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Tue, 18 Jun 2024 22:24:14 -0400 Subject: [PATCH 083/185] Update README.md (#532) --- README.md | 39 +++++++++++++-------------------------- 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 5c4eb2d45..9f3f9b236 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ [**Examples**](https://github.com/kushalkolar/fastplotlib#examples) | [**Contributing**](https://github.com/kushalkolar/fastplotlib#heart-contributing) -Next-gen plotting library built using the [`pygfx`](https://github.com/pygfx/pygfx) rendering engine that can utilize [Vulkan](https://en.wikipedia.org/wiki/Vulkan), [DX12](https://en.wikipedia.org/wiki/DirectX#DirectX_12), or [Metal](https://developer.apple.com/metal/) via WGPU, so it is very fast! `fastplotlib` also aims to be an expressive plotting library that enables rapid prototyping for large scale explorative scientific visualization. +Next-gen plotting library built using the [`pygfx`](https://github.com/pygfx/pygfx) rendering engine that can utilize [Vulkan](https://en.wikipedia.org/wiki/Vulkan), [DX12](https://en.wikipedia.org/wiki/DirectX#DirectX_12), or [Metal](https://developer.apple.com/metal/) via WGPU, so it is very fast! `fastplotlib` is an expressive plotting library that enables rapid prototyping for large scale explorative scientific visualization. ![scipy-fpl](https://github.com/fastplotlib/fastplotlib/assets/9403332/b981a54c-05f9-443f-a8e4-52cd01cd802a) @@ -41,17 +41,15 @@ concepts are similar to those from the API shown in the video. > **Note** > -> `fastplotlib` is currently in the **alpha stage with breaking changes every ~month**, but you're welcome to try it out or contribute! See our [Roadmap](https://github.com/kushalkolar/fastplotlib/issues/55). See this for a discussion on API stability: https://github.com/fastplotlib/fastplotlib/issues/121 +> `fastplotlib` is currently in the **late alpha stage**, but you're welcome to try it out or contribute! See our [Roadmap](https://github.com/kushalkolar/fastplotlib/issues/55). See this for a discussion on API stability: https://github.com/fastplotlib/fastplotlib/issues/121 # Documentation http://fastplotlib.readthedocs.io/ -The Quickstart guide is not interactive. We recommend cloning/downloading the repo and trying out the `desktop` or `notebook` examples: https://github.com/kushalkolar/fastplotlib/tree/main/examples +The examples are interactive if you run them locally on your computer. If someone wants to integrate `pyodide` with `pygfx` we would be able to have live interactive examples on the website! -If someone wants to integrate `pyodide` with `pygfx` we would be able to have live interactive examples! :smiley: - -Questions, issues, ideas? Post an [issue](https://github.com/fastplotlib/fastplotlib/issues) or post on the [discussion forum](https://github.com/fastplotlib/fastplotlib/discussions)! +Questions, issues, ideas? You are welcome to post an [issue](https://github.com/fastplotlib/fastplotlib/issues) or post on the [discussion forum](https://github.com/fastplotlib/fastplotlib/discussions)! :smiley: # Installation @@ -87,51 +85,40 @@ cd fastplotlib # install all extras in place pip install -e ".[notebook,docs,tests]" + +# install latest pygfx +pip install git+https://github.com/pygfx/pygfx.git@main ``` Se [Contributing](https://github.com/fastplotlib/fastplotlib?tab=readme-ov-file#heart-contributing) for more details on development # Examples +Examples gallery: https://fastplotlib.readthedocs.io/en/latest/_gallery/index.html + > **Note:** `fastplotlib` and `pygfx` are fast evolving, you will probably require the latest `pygfx` and `fastplotlib` from github to use the examples in the main branch. `fastplotlib` code is identical across notebook (`jupyter`), and desktop use with `Qt`/`PySide` or `glfw`. -Even if you do not intend to use notebooks with `fastplotlib`, the `quickstart.ipynb` notebook is currently the best way to get familiar with the API: https://github.com/fastplotlib/fastplotlib/tree/main/examples/notebooks/quickstart.ipynb +Even if you do not intend to use notebooks with `fastplotlib`, the `quickstart.ipynb` tutorial notebook is the best way to get familiar with the API: https://github.com/fastplotlib/fastplotlib/tree/main/examples/notebooks/quickstart.ipynb The specifics for running `fastplotlib` in different GUI frameworks are: - Running in `glfw` requires a `fastplotlib.run()` call (which is really just a `wgpu` `run()` call) - With `Qt` you can encapsulate it within a `QApplication`, see `examples/qt` - Notebooks plots have ipywidget-based toolbars and widgets. There are plans to move toward an identical in-canvas toolbar with UI elements across all supported frameworks 😄 -### Desktop examples using `glfw` or `Qt` +### Embedding in a `Qt` app -GLFW examples are here. GLFW is a "minimal" desktop framework. - -https://github.com/fastplotlib/fastplotlib/tree/main/examples/desktop - -Qt examples are here: +See these for examples on embedding within a Qt app. Note that you can also use `fastplotlib` with qt interactively using `%gui qt` in jupyter or ipython. https://github.com/fastplotlib/fastplotlib/tree/main/examples/qt -Some of the examples require imageio: -``` -pip install imageio -``` - ### Notebook examples -Notebook examples are here: +Notebook examples are here, these include examples on selector tools. https://github.com/fastplotlib/fastplotlib/tree/main/examples/notebooks -**Start with `quickstart.ipynb`.** - -Some of the examples require imageio: -``` -pip install imageio -``` - ### Video Our SciPy 2023 talk walks through numerous demos: https://github.com/fastplotlib/fastplotlib#scipy-talk From 0edfe7f117fd2dd740f3ae6eab0e4d18d45ee6a3 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Tue, 18 Jun 2024 22:24:38 -0400 Subject: [PATCH 084/185] Update deps (#533) * pin to wgpu<0.16.0, set pygfx max ver pin * pin wgpu max version for release * another instance of reverting info to get_adapter_info * typo --- docs/source/user_guide/gpu.rst | 4 ++-- examples/tests/testutils.py | 2 +- fastplotlib/utils/gui.py | 4 ++-- setup.py | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/source/user_guide/gpu.rst b/docs/source/user_guide/gpu.rst index f4ef89a0c..1bbc0c030 100644 --- a/docs/source/user_guide/gpu.rst +++ b/docs/source/user_guide/gpu.rst @@ -59,7 +59,7 @@ You can get more detailed info on each adapter like this:: import pprint for a in fpl.enumerate_adapters(): - pprint.pprint(a.info) + pprint.pprint(a.request_adapter_info()) General description of the fields: * vendor: GPU manufacturer @@ -267,7 +267,7 @@ to ``fpl.select_adapter()``:: # get info or summary of all adapters to pick an adapter import pprint for a in fpl.enumerate_adapters(): - pprint.pprint(a.info) + pprint.pprint(a.request_adapter_info()) # example, pick adapter at index 2 chosen_gpu = fpl.enumerate_adapters()[2] diff --git a/examples/tests/testutils.py b/examples/tests/testutils.py index 0e4cd2e1b..22747ce08 100644 --- a/examples/tests/testutils.py +++ b/examples/tests/testutils.py @@ -31,7 +31,7 @@ def get_wgpu_backend(): """ Query the configured wgpu backend driver. """ - code = "import wgpu.utils; info = wgpu.utils.get_default_device().adapter.info; print(info['adapter_type'], info['backend_type'])" + code = "import wgpu.utils; info = wgpu.utils.get_default_device().adapter.request_adapter_info(); print(info['adapter_type'], info['backend_type'])" result = subprocess.run( [ sys.executable, diff --git a/fastplotlib/utils/gui.py b/fastplotlib/utils/gui.py index 1f13c1406..1941674ee 100644 --- a/fastplotlib/utils/gui.py +++ b/fastplotlib/utils/gui.py @@ -60,9 +60,9 @@ def _notebook_print_banner(): # print logo and adapter info adapters = [a for a in wgpu.gpu.enumerate_adapters()] - adapters_info = [a.info for a in adapters] + adapters_info = [a.request_adapter_info() for a in adapters] - default_adapter_info = wgpu.gpu.request_adapter().info + default_adapter_info = wgpu.gpu.request_adapter().request_adapter_info() default_ix = adapters_info.index(default_adapter_info) if len(adapters) > 0: diff --git a/setup.py b/setup.py index ef79e4774..7229dcf25 100644 --- a/setup.py +++ b/setup.py @@ -4,8 +4,8 @@ install_requires = [ "numpy>=1.23.0", - "wgpu>=0.16.0", - "pygfx>=0.1.14", + "wgpu<0.16.0", + "pygfx>=0.1.14,<=0.2.0", ] From d0b8bb13714f44f0cfab249f536f75a6357809d0 Mon Sep 17 00:00:00 2001 From: Caitlin Lewis <69729525+clewis7@users.noreply.github.com> Date: Sat, 22 Jun 2024 19:11:07 -0400 Subject: [PATCH 085/185] add user guide (#513) * start guide based on new API changes * Update guide.rst * progress on guide * guide updates * add spaces and interactive use sections * add selectors to guide * guide updates * rough draft * Apply suggestions from code review Co-authored-by: Kushal Kolar * update guide --------- Co-authored-by: Kushal Kolar --- docs/source/_static/guide_animation.gif | 3 + docs/source/_static/guide_hello_world.png | 3 + .../source/_static/guide_hello_world_data.png | 3 + .../source/_static/guide_hello_world_vmax.png | 3 + docs/source/_static/guide_image_widget.gif | 3 + docs/source/_static/guide_linear_selector.gif | 3 + docs/source/conf.py | 2 +- docs/source/index.rst | 1 + docs/source/user_guide/guide.rst | 560 ++++++++++++++++++ 9 files changed, 580 insertions(+), 1 deletion(-) create mode 100644 docs/source/_static/guide_animation.gif create mode 100644 docs/source/_static/guide_hello_world.png create mode 100644 docs/source/_static/guide_hello_world_data.png create mode 100644 docs/source/_static/guide_hello_world_vmax.png create mode 100644 docs/source/_static/guide_image_widget.gif create mode 100644 docs/source/_static/guide_linear_selector.gif create mode 100644 docs/source/user_guide/guide.rst diff --git a/docs/source/_static/guide_animation.gif b/docs/source/_static/guide_animation.gif new file mode 100644 index 000000000..6328dbefc --- /dev/null +++ b/docs/source/_static/guide_animation.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0b69ad02a527f4c7353cb94ce22deedd92134e84b2cc0776bf81f3d0083d0e37 +size 4095880 diff --git a/docs/source/_static/guide_hello_world.png b/docs/source/_static/guide_hello_world.png new file mode 100644 index 000000000..b66b3d592 --- /dev/null +++ b/docs/source/_static/guide_hello_world.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:81fdee4dda9bbc46d810a6e80ddc4d6ceb8ff0db8dfea2faf90424f977c000d4 +size 201157 diff --git a/docs/source/_static/guide_hello_world_data.png b/docs/source/_static/guide_hello_world_data.png new file mode 100644 index 000000000..6164f4d55 --- /dev/null +++ b/docs/source/_static/guide_hello_world_data.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cda3395fb158706eeffc669f3f7cf09b99316870e32f51f69aedc9a6f1590ddb +size 123621 diff --git a/docs/source/_static/guide_hello_world_vmax.png b/docs/source/_static/guide_hello_world_vmax.png new file mode 100644 index 000000000..91441a16c --- /dev/null +++ b/docs/source/_static/guide_hello_world_vmax.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:36902d1e082730af8dc383ff7f89434c93b9e605f0410dd199d39495cd61c552 +size 143561 diff --git a/docs/source/_static/guide_image_widget.gif b/docs/source/_static/guide_image_widget.gif new file mode 100644 index 000000000..06f23c52d --- /dev/null +++ b/docs/source/_static/guide_image_widget.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4a3c90078ab4ec8552c4eac199c726d413c89ec63143988e3ea0fca5114393d6 +size 2122357 diff --git a/docs/source/_static/guide_linear_selector.gif b/docs/source/_static/guide_linear_selector.gif new file mode 100644 index 000000000..383c3ef43 --- /dev/null +++ b/docs/source/_static/guide_linear_selector.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:acf1cab72eb4d268581b26295e47cd0ca9aebe08beaf80e1424b100086ed59ca +size 331446 diff --git a/docs/source/conf.py b/docs/source/conf.py index 4d94ec7e7..0df47e579 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -100,7 +100,7 @@ intersphinx_mapping = { "python": ("https://docs.python.org/3", None), "numpy": ("https://numpy.org/doc/stable/", None), - "pygfx": ("https://pygfx.com/latest", None), + "pygfx": ("https://pygfx.com/stable", None), "wgpu": ("https://wgpu-py.readthedocs.io/en/latest", None), } diff --git a/docs/source/index.rst b/docs/source/index.rst index 6385c2aee..2b40cdeca 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -5,6 +5,7 @@ Welcome to fastplotlib's documentation! :caption: User Guide :maxdepth: 2 + Guide GPU Info .. toctree:: diff --git a/docs/source/user_guide/guide.rst b/docs/source/user_guide/guide.rst new file mode 100644 index 000000000..87e2b7fab --- /dev/null +++ b/docs/source/user_guide/guide.rst @@ -0,0 +1,560 @@ +The `fastplotlib` guide +======================= + +Installation +------------ + +To install use pip: + +.. code-block:: + + pip install -U fastplotlib + +or install the bleeding edge from Github: + +.. code-block:: + + pip install git+https://github.com/fastplotlib/fastplotlib.git@main + + +What is ``fastplotlib``? +---------------------- + +``fastplotlib`` is a cutting-edge plotting library built using the ```pygfx`` `_ rendering engine. +The lower-level details of the rendering process (i.e. defining a scene, camera, renderer, etc.) are abstracted away, allowing users to focus on their data. +The fundamental goal of ``fastplotlib`` is to provide a high-level, expressive API that promotes large-scale explorative scientific visualization. We want to +make it easy and intuitive to produce interactive visualizations that are as performant and vibrant as a modern video game 😄 + + +How to use ``fastplotlib`` +------------------------ + +Before giving a detailed overview of the library, here is a minimal example:: + + import fastplotlib as fpl + import imageio.v3 as iio + + # create a `Figure` + fig = fpl.Figure() + + # read data + data = iio.imread("imageio:astronaut.png") + + # add image graphic + image_graphic = fig[0, 0].add_image(data=data) + + # show the plot + fig.show() + + if __name__ == "__main__": + fpl.run() + +.. image:: ./_static/guide_hello_world.png + + +This is just a simple example of how the ``fastplotlib`` API works to create a plot, add some image data to the plot, and then visualize it. +However, we are just scratching the surface of what is possible with ``fastplotlib``. +Next, let's take a look at the building blocks of ``fastplotlib`` and how they can be used to create more complex visualizations. + +**Figure** + +The starting point for creating any visualization in ``fastplotlib`` is a ``Figure`` object. This can be a single plot or a grid of subplots. +The ``Figure`` object houses and takes care of the underlying rendering components such as the camera, controller, renderer, and canvas. +Most users won't need to use these directly; however, the ability to directly interact with the rendering engine is still available if +needed. + +By default, if no ``shape`` argument is provided when creating a ``Figure``, there will be a single subplot. All subplots in a ``Figure`` can be accessed using +indexing (i.e. ``fig_object[i ,j]``). + +After defining a ``Figure``, we can begin to add ``Graphic`` objects. + +**Graphics** + +A ``Graphic`` can be an image, a line, a scatter, a collection of lines, and more. All graphics can also be given a convenient ``name``. This allows graphics +to be easily accessed from figures:: + + # create a `Figure` + fig = fpl.Figure() + + # read data + data = iio.imread("imageio:astronaut.png") + + add image graphic + image_graphic = fig[0, 0].add_image(data=data, name="astronaut") + + # show plot + fig.show() + + # index plot to get graphic + fig[0, 0]["astronaut"] + +.. + +Graphics also have mutable properties that can be linked to events. Some of these properties, such as the ``data`` or ``colors`` of a line can even be indexed, +allowing for the creation of very powerful visualizations. + +(1) Common properties that all graphics have + ++--------------+--------------------------------------------------------------------------------------------------------------+ +| Feature Name | Description | ++==============+==============================================================================================================+ +| name | Graphic name | ++--------------+--------------------------------------------------------------------------------------------------------------+ +| offset | Offset position of the graphic, [x, y, z] | ++--------------+--------------------------------------------------------------------------------------------------------------+ +| rotation | Graphic rotation quaternion | ++--------------+--------------------------------------------------------------------------------------------------------------+ +| visible | Access or change the visibility | ++--------------+--------------------------------------------------------------------------------------------------------------+ +| deleted | Used when a graphic is deleted, triggers events that can be useful to indicate this graphic has been deleted | ++--------------+--------------------------------------------------------------------------------------------------------------+ + +(2) Graphic-Specific properties + + (a) ``ImageGraphic`` + + +------------------------+------------------------------------+ + | Feature Name | Description | + +========================+====================================+ + | data | Underlying image data | + +------------------------+------------------------------------+ + | vmin | Lower contrast limit of an image | + +------------------------+------------------------------------+ + | vmax | Upper contrast limit of an image | + +------------------------+------------------------------------+ + | cmap | Colormap of an image | + +------------------------+------------------------------------+ + + (b) ``LineGraphic``, ``LineCollection``, ``LineStack`` + + +--------------+--------------------------------+ + | Feature Name | Description | + +==============+================================+ + | data | underlying data of the line(s) | + +--------------+--------------------------------+ + | colors | colors of the line(s) | + +--------------+--------------------------------+ + | cmap | colormap of the line(s) | + +--------------+--------------------------------+ + | thickness | thickness of the line(s) | + +--------------+--------------------------------+ + + (c) ``ScatterGraphic`` + + +--------------+---------------------------------------+ + | Feature Name | Description | + +==============+=======================================+ + | data | underlying data of the scatter points | + +--------------+---------------------------------------+ + | colors | colors of the scatter points | + +--------------+---------------------------------------+ + | cmap | colormap of the scatter points | + +--------------+---------------------------------------+ + | sizes | size of the scatter points | + +--------------+---------------------------------------+ + + (d) ``TextGraphic`` + + +-------------------+---------------------------+ + | Feature Name | Description | + +===================+===========================+ + | text | data of the text | + +-------------------+---------------------------+ + | font_size | size of the text | + +-------------------+---------------------------+ + | face_color | color of the text face | + +-------------------+---------------------------+ + | outline_color | color of the text outline | + +-------------------+---------------------------+ + | outline_thickness | thickness of the text | + +-------------------+---------------------------+ + +Using our example from above: once we add a ``Graphic`` to the figure, we can then begin to change its properties. :: + + image_graphic.vmax = 150 + +.. image:: ./_static/hello_world_vmax.png + +``Graphic`` properties also support slicing and indexing. For example :: + + image_graphic.data[::8, :, :] = 1 + image_graphic.data[:, ::8, :] = 1 + +.. image:: ./_static/hello_world_data.png + + +Selectors +--------- + +A primary feature of ``fastplotlib`` is the ability to easily interact with your data. Two extremely helpful tools that can +be used in order to facilitate this process are a ``LinearSelector`` and ``LinearRegionSelector``. + +A ``LinearSelector`` is a horizontal or vertical line slider. This tool allows you to very easily select different points in your +data. Let's look at an example: :: + + import fastplotlib as fpl + import numpy as np + + # generate data + xs = np.linspace(-10, 10, 100) + ys = np.sin(xs) + sine = np.column_stack([xs, ys]) + + fig = fpl.Figure() + + sine_graphic = fig[0, 0].add_line(data=sine, colors="w") + + # add a linear selector the sine wave + selector = sine_graphic.add_linear_selector() + + fig[0, 0].auto_scale() + + fig.show(maintain_aspect=False) + +.. image:: _static/guide_linear_selector.gif + + +A ``LinearRegionSelector`` is very similar to a ``LinearSelector`` but as opposed to selecting a singular point of +your data, you are able to select an entire region. + + +Now we have the basics of creating a ``Figure``, adding ``Graphics`` to a ``Figure``, and working with ``Graphic`` properties to dynamically change or alter them. +Let's take a look at how we can define events to link ``Graphics`` and their properties together. + +Events +------ + +Events can be a multitude of things: traditional events such as mouse or keyboard events, but they can also be +You can use renderer events, such as mouse or keyboard events, or events related to ``Graphic`` properties. + + +There are two ways to add events in ``fastplotlib``. + +1) Use the method :: + + def event_handler(ev): + pass + + graphic.add_event_handler(event_handler, "event_type") + +.. + + +2) or a decorator :: + + @graphic.add_event_handler("event_type") + def event_handler(ev): + pass + +.. + + +The ``event_handler`` is a user-defined function that accepts an event instance as the first and only positional argument. +Information about the structure of event instances are described below. The `"event_type"` +is a string that identifies the type of event; this can be either a ``pygfx.Event`` or a ``Graphic`` property event. +See the above graphic-specific properties that can be used for events and below for the available ``pygfx`` events. + +Rendering engine (``pygfx``) events: + - "key_down" + - "key_up" + - "pointer_down" + - "pointer_move" + - "pointer_up" + - "pointer_enter" + - "pointer_leave" + - "click" + - "double_click" + - "wheel" + - "close" + - "resize" + +When an event occurs, the user-defined event handler will receive and event object. Depending on the type of event, the +event object will have relevant information that can be used in the callback. See below for event tables. + + +**All ``Graphic`` events have the following attributes:** + + +------------+-------------+-----------------------------------------------+ + | attribute | type | description | + +============+=============+===============================================+ + | type | str | "colors" - name of the event | + +------------+-------------+-----------------------------------------------+ + | graphic | Graphic | graphic instance that the event is from | + +------------+-------------+-----------------------------------------------+ + | info | dict | event info dictionary | + +------------+-------------+-----------------------------------------------+ + | target | WorldObject | pygfx rendering engine object for the graphic | + +------------+-------------+-----------------------------------------------+ + | time_stamp | float | time when the event occurred, in ms | + +------------+-------------+-----------------------------------------------+ + +The ``info`` attribute will house additional information for different ``Graphic`` property events: + +event_type: "colors" + + Vertex Colors + + **info dict** + + +------------+-----------------------------------------------------------+----------------------------------------------------------------------------------+ + | dict key | value type | value description | + +============+===========================================================+==================================================================================+ + | key | int | slice | np.ndarray[int | bool] | tuple[slice, ...] | key at which colors were indexed/sliced | + +------------+-----------------------------------------------------------+----------------------------------------------------------------------------------+ + | value | np.ndarray | new color values for points that were changed, shape is [n_points_changed, RGBA] | + +------------+-----------------------------------------------------------+----------------------------------------------------------------------------------+ + | user_value | str | np.ndarray | tuple[float] | list[float] | list[str] | user input value that was parsed into the RGBA array | + +------------+-----------------------------------------------------------+----------------------------------------------------------------------------------+ + + Uniform Colors + + **info dict** + + +------------+-----------------------------------------------------------+----------------------------------------------------------------------------------+ + | dict key | value type | value description | + +============+===========================================================+==================================================================================+ + | value | np.ndarray | new color values for points that were changed, shape is [n_points_changed, RGBA] | + +------------+-----------------------------------------------------------+----------------------------------------------------------------------------------+ + +event_type: "sizes" + + **info dict** + + +----------+----------------------------------------------------------+------------------------------------------------------------------------------------------+ + | dict key | value type | value description | + +==========+==========================================================+==========================================================================================+ + | key | int | slice | np.ndarray[int | bool] | tuple[slice, ...] | key at which vertex positions data were indexed/sliced | + +----------+----------------------------------------------------------+------------------------------------------------------------------------------------------+ + | value | np.ndarray | float | list[float] | new data values for points that were changed, shape depends on the indices that were set | + +----------+----------------------------------------------------------+------------------------------------------------------------------------------------------+ + +event_type: "data" + + **info dict** + + +----------+----------------------------------------------------------+------------------------------------------------------------------------------------------+ + | dict key | value type | value description | + +==========+==========================================================+==========================================================================================+ + | key | int | slice | np.ndarray[int | bool] | tuple[slice, ...] | key at which vertex positions data were indexed/sliced | + +----------+----------------------------------------------------------+------------------------------------------------------------------------------------------+ + | value | np.ndarray | float | list[float] | new data values for points that were changed, shape depends on the indices that were set | + +----------+----------------------------------------------------------+------------------------------------------------------------------------------------------+ + +event_type: "thickness" + + **info dict** + + +------------+-----------------------------------------------------------+----------------------------------------------------------------------------------+ + | dict key | value type | value description | + +============+===========================================================+==================================================================================+ + | value | float | new thickness value | + +------------+-----------------------------------------------------------+----------------------------------------------------------------------------------+ + +event_type: "cmap" + + **info dict** + + +------------+-----------------------------------------------------------+----------------------------------------------------------------------------------+ + | dict key | value type | value description | + +============+===========================================================+==================================================================================+ + | value | string | new colormap value | + +------------+-----------------------------------------------------------+----------------------------------------------------------------------------------+ + +event_type: "selection" + + ``LinearSelector`` + + **additional event attributes:** + + +--------------------+----------+------------------------------------+ + | attribute | type | description | + +====================+==========+====================================+ + | get_selected_index | callable | returns indices under the selector | + +--------------------+----------+------------------------------------+ + + **info dict:** + + +----------+------------+-------------------------------+ + | dict key | value type | value description | + +==========+============+===============================+ + | value | np.ndarray | new x or y value of selection | + +----------+------------+-------------------------------+ + + ``LinearRegionSelector`` + + **additional event attributes:** + + +----------------------+----------+------------------------------------+ + | attribute | type | description | + +======================+==========+====================================+ + | get_selected_indices | callable | returns indices under the selector | + +----------------------+----------+------------------------------------+ + | get_selected_data | callable | returns data under the selector | + +----------------------+----------+------------------------------------+ + + **info dict:** + + +----------+------------+-----------------------------+ + | dict key | value type | value description | + +==========+============+=============================+ + | value | np.ndarray | new [min, max] of selection | + +----------+------------+-----------------------------+ + +**Renderer Events:** + +You can also add events to a ``Figure`` object's renderer. This is useful for defining click events where +you want to map your click position to the nearest graphic object for example. + +Renderer events can be added using either method mentioned above (i.e. using the method or via a decorator). + +For example: :: + + import fastplotlib as fpl + import numpy as np + + # generate some circles + def make_circle(center, radius: float, n_points: int = 75) -> np.ndarray: + theta = np.linspace(0, 2 * np.pi, n_points) + xs = radius * np.sin(theta) + ys = radius * np.cos(theta) + + return np.column_stack([xs, ys]) + center + + # this makes 5 circles, so we can create 5 cmap values, so it will use these values to set the + # color of the line based by using the cmap as a LUT with the corresponding cmap_value + circles = list() + for x in range(0, 50, 10): + circles.append(make_circle(center=(x, 0), radius=4, n_points=100)) + + # create figure + fig = fpl.Figure() + + # add circles to plot + circles_graphic = fig[0,0].add_line_collection(data=circles, cmap="tab10", thickness=10) + + # get the nearest graphic that is clicked and change the color + @fig.renderer.add_event_handler("click") + def click_event(ev): + # reset colors + circles_graphic.cmap = "tab10" + + # map the click position to world coordinates + xy = fig[0, 0].map_screen_to_world(ev)[:-1] + + # get the nearest graphic to the position + nearest = fpl.utils.get_nearest_graphics(xy, circles_graphic)[0] + + # change the closest graphic color to white + nearest.colors = "w" + + fig.show() +.. image:: ./_static/click_event.gif + +ImageWidget +----------- + +Often times, developing UIs for interacting with multi-dimension image data can be tedious and repetitive. +In order to aid with common image and video visualization requirements the ``ImageWidget`` automatically generates sliders +to easily navigate through different dimensions of your data. Let's look at an example: :: + + import fastplotlib as fpl + import imageio.v3 as iio + + movie = iio.imread("imageio:cockatoo.mp4") + + # convert RGB movie to grayscale + gray_movie = np.dot(movie[..., :3], [0.299, 0.587, 0.114]) + + iw_movie = ImageWidget( + data=gray_movie, + cmap="gray" + ) + + iw_movie.show() + +.. image:: _static/guide_image_widget.gif + +Animations +---------- + +An animation function is a user-defined function that gets called on every rendering cycle. Let's look at an example: :: + + import fastplotlib as fpl + import numpy as np + + data = np.random.rand(512, 512) + + fig = fpl.Figure() + + fig[0,0].add_image(data=data, name="random-img") + + def update_data(plot_instance): + new_data = np.random.rand(512, 512) + plot_instance["random-img"].data = new_data + + fig[0,0].add_animations(update_data) + + fig.show() + +.. image:: _static/guide_animation.gif + +Here we are defining a function that updates the data of the ``ImageGraphic`` in the plot with new random data. When adding an animation function, the +user-defined function will receive a plot instance as an argument when it is called. + +Spaces +------ + +There are several spaces to consider when using ``fastplotlib``: + +1) World Space + + World space is the 3D space in which graphical objects live. Objects + and the camera can exist anywhere in this space. + +2) Data Space + + Data space is simply the world space plus any offset or rotation that has been applied to an object. + +.. note:: + World space does not always correspond directly to data space, you may have to adjust for any offset or rotation of the ``Graphic``. + +3) Screen Space + + Screen space is the 2D space in which your screen pixels reside. This space is constrained by the screen width and height in pixels. + In the rendering process, the camera is responsible for projecting the world space into screen space. + +.. note:: + When interacting with ``Graphic`` objects, there is a very helpful function for mapping screen space to world space + (``Figure.map_screen_to_world(pos=(x, y))``). This can be particularly useful when working with click events where click + positions are returned in screen space but ``Graphic`` objects that you may want to interact with exist in world + space. + +For more information on the various spaces used by rendering engines please see this `article `_ + + +Using ``fastplotlib`` interactively +----------------------------------- + +There are multiple ways to use ``fastplotlib`` interactively. + +1) Jupyter + +On ``jupyter lab`` the jupyter backend (i.e. ``jupyter_rfb``) is normally selected. This works via +client-server rendering. Images generated on the server are streamed to the client (Jupyter) via a jpeg byte stream. +Events (such as mouse or keyboard events) are then streamed in the opposite direction prompting new images to be generated +by the server if necessary. This remote-frame-buffer approach makes the rendering process very fast. ``fastplotlib`` viusalizations +can be displayed in cell output or on the side using ``sidecar``. + +A Qt backend can also optionally be used as well. If ``%gui qt`` is selected before importing ``fastplotlib`` then this backend +will be used instead. + +Lastly, users can also force using ``glfw`` by specifying this as an argument when instantiating a ``Figure`` (i.e. ``Figure(canvas="gflw"``). + +.. note:: + Do not mix between gui backends. For example, if you start the notebook using Qt, do not attempt to force using another backend such + as ``jupyter_rfb`` later. + +2) IPython + +Users can select between using a Qt backend or gflw using the same methods as above. + From efbb8c1d885098c7e898eb5c5f396e073ab688e1 Mon Sep 17 00:00:00 2001 From: Caitlin Lewis <69729525+clewis7@users.noreply.github.com> Date: Sun, 23 Jun 2024 06:10:06 -0400 Subject: [PATCH 086/185] fix guide (#536) --- docs/source/_static/click_event.gif | 3 +++ docs/source/_static/guide_hello_world.png | 4 ++-- .../source/_static/guide_hello_world_data.png | 4 ++-- .../source/_static/guide_hello_world_vmax.png | 4 ++-- docs/source/user_guide/guide.rst | 19 ++++++++++--------- 5 files changed, 19 insertions(+), 15 deletions(-) create mode 100644 docs/source/_static/click_event.gif diff --git a/docs/source/_static/click_event.gif b/docs/source/_static/click_event.gif new file mode 100644 index 000000000..81a334318 --- /dev/null +++ b/docs/source/_static/click_event.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b45c8aba01ba9557ee707e11952798df151d111a144cc09432323d98a3e2ee17 +size 110505 diff --git a/docs/source/_static/guide_hello_world.png b/docs/source/_static/guide_hello_world.png index b66b3d592..dbdc7029a 100644 --- a/docs/source/_static/guide_hello_world.png +++ b/docs/source/_static/guide_hello_world.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:81fdee4dda9bbc46d810a6e80ddc4d6ceb8ff0db8dfea2faf90424f977c000d4 -size 201157 +oid sha256:b621c2034eb2998e602409ab41b5c574d24e265088787fd7331c8308eec6d7cb +size 178268 diff --git a/docs/source/_static/guide_hello_world_data.png b/docs/source/_static/guide_hello_world_data.png index 6164f4d55..105a2292a 100644 --- a/docs/source/_static/guide_hello_world_data.png +++ b/docs/source/_static/guide_hello_world_data.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cda3395fb158706eeffc669f3f7cf09b99316870e32f51f69aedc9a6f1590ddb -size 123621 +oid sha256:11652d0a0cfef535eb7e9fe1b796cbd2a6ef3f779dd5ce477273fcfda4cefb99 +size 207602 diff --git a/docs/source/_static/guide_hello_world_vmax.png b/docs/source/_static/guide_hello_world_vmax.png index 91441a16c..c90785b1b 100644 --- a/docs/source/_static/guide_hello_world_vmax.png +++ b/docs/source/_static/guide_hello_world_vmax.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:36902d1e082730af8dc383ff7f89434c93b9e605f0410dd199d39495cd61c552 -size 143561 +oid sha256:14f07430628acef116b5e426fa274978127b2f8decb2655b8367edf5ca731501 +size 170500 diff --git a/docs/source/user_guide/guide.rst b/docs/source/user_guide/guide.rst index 87e2b7fab..4ee374389 100644 --- a/docs/source/user_guide/guide.rst +++ b/docs/source/user_guide/guide.rst @@ -18,7 +18,7 @@ or install the bleeding edge from Github: What is ``fastplotlib``? ----------------------- +------------------------ ``fastplotlib`` is a cutting-edge plotting library built using the ```pygfx`` `_ rendering engine. The lower-level details of the rendering process (i.e. defining a scene, camera, renderer, etc.) are abstracted away, allowing users to focus on their data. @@ -27,7 +27,7 @@ make it easy and intuitive to produce interactive visualizations that are as per How to use ``fastplotlib`` ------------------------- +-------------------------- Before giving a detailed overview of the library, here is a minimal example:: @@ -49,7 +49,7 @@ Before giving a detailed overview of the library, here is a minimal example:: if __name__ == "__main__": fpl.run() -.. image:: ./_static/guide_hello_world.png +.. image:: ../_static/guide_hello_world.png This is just a simple example of how the ``fastplotlib`` API works to create a plot, add some image data to the plot, and then visualize it. @@ -173,14 +173,14 @@ Using our example from above: once we add a ``Graphic`` to the figure, we can th image_graphic.vmax = 150 -.. image:: ./_static/hello_world_vmax.png +.. image:: ../_static/guide_hello_world_vmax.png ``Graphic`` properties also support slicing and indexing. For example :: image_graphic.data[::8, :, :] = 1 image_graphic.data[:, ::8, :] = 1 -.. image:: ./_static/hello_world_data.png +.. image:: ../_static/guide_hello_world_data.png Selectors @@ -211,7 +211,7 @@ data. Let's look at an example: :: fig.show(maintain_aspect=False) -.. image:: _static/guide_linear_selector.gif +.. image:: ../_static/guide_linear_selector.gif A ``LinearRegionSelector`` is very similar to a ``LinearSelector`` but as opposed to selecting a singular point of @@ -448,7 +448,8 @@ For example: :: nearest.colors = "w" fig.show() -.. image:: ./_static/click_event.gif + +.. image:: ../_static/click_event.gif ImageWidget ----------- @@ -472,7 +473,7 @@ to easily navigate through different dimensions of your data. Let's look at an e iw_movie.show() -.. image:: _static/guide_image_widget.gif +.. image:: ../_static/guide_image_widget.gif Animations ---------- @@ -496,7 +497,7 @@ An animation function is a user-defined function that gets called on every rende fig.show() -.. image:: _static/guide_animation.gif +.. image:: ../_static/guide_animation.gif Here we are defining a function that updates the data of the ``ImageGraphic`` in the plot with new random data. When adding an animation function, the user-defined function will receive a plot instance as an argument when it is called. From 4670fdebd80bcc5da1e0b7df2dd3462aa4237a5f Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Fri, 28 Jun 2024 18:01:59 +0200 Subject: [PATCH 087/185] fix legend (#544) * ndarray.ptp -> np.ptp for numpy v2 * fix legend --- fastplotlib/legends/legend.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/fastplotlib/legends/legend.py b/fastplotlib/legends/legend.py index 8ab3ddedb..df78d5662 100644 --- a/fastplotlib/legends/legend.py +++ b/fastplotlib/legends/legend.py @@ -56,10 +56,10 @@ def __init__( ) # for now only support lines with a single color - if np.unique(graphic.colors(), axis=0).shape[0] > 1: + if np.unique(graphic.colors.value, axis=0).shape[0] > 1: raise ValueError("Use colorbars for multi-colored lines, not legends") - color = pygfx.Color(np.unique(graphic.colors(), axis=0).ravel()) + color = pygfx.Color(np.unique(graphic.colors.value, axis=0).ravel()) self._parent = parent @@ -117,7 +117,7 @@ def label(self, text: str): self._label_world_object.geometry.set_text(text) def _update_color(self, ev: FeatureEvent): - new_color = ev.pick_info["new_data"] + new_color = ev.info["value"] if np.unique(new_color, axis=0).shape[0] > 1: raise ValueError( "LegendError: LineGraphic colors no longer appropriate for legend" @@ -126,8 +126,8 @@ def _update_color(self, ev: FeatureEvent): self._color = new_color[0] self._line_world_object.material.color = pygfx.Color(self._color) - def _highlight_graphic(self, graphic, ev): - graphic_color = pygfx.Color(np.unique(graphic.colors(), axis=0).ravel()) + def _highlight_graphic(self, graphic: Graphic, ev): + graphic_color = pygfx.Color(np.unique(graphic.colors.value, axis=0).ravel()) if graphic_color == self._parent.highlight_color: graphic.colors = self._color @@ -270,7 +270,7 @@ def add_graphic(self, graphic: Graphic, label: str = None): self._graphics.append(graphic) self._items[graphic._fpl_address] = legend_item - graphic.deleted.add_event_handler(partial(self.remove_graphic, graphic)) + graphic.add_event_handler(partial(self.remove_graphic, graphic), "deleted") self._col_counter = new_col_ix self._row_counter = new_row_ix From 3621471b4c4d00ba33d9957eaa3de3dc96e9d963 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Fri, 28 Jun 2024 18:02:11 +0200 Subject: [PATCH 088/185] Bump version (#543) * ndarray.ptp -> np.ptp for numpy v2 * bump version --- fastplotlib/VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastplotlib/VERSION b/fastplotlib/VERSION index 0ea3a944b..0d91a54c7 100644 --- a/fastplotlib/VERSION +++ b/fastplotlib/VERSION @@ -1 +1 @@ -0.2.0 +0.3.0 From 598fc29770b8e11ed23a6b01a95f1f53ee6f0e8a Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Fri, 28 Jun 2024 18:03:29 +0200 Subject: [PATCH 089/185] adjust to latest wgpu (#542) * ndarray.ptp -> np.ptp for numpy v2 * a.request_adapter_infot() -> a.info * update min wgpu and pygfx versions --- docs/source/user_guide/gpu.rst | 4 ++-- examples/tests/testutils.py | 2 +- fastplotlib/utils/gui.py | 4 ++-- setup.py | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/source/user_guide/gpu.rst b/docs/source/user_guide/gpu.rst index 1bbc0c030..f4ef89a0c 100644 --- a/docs/source/user_guide/gpu.rst +++ b/docs/source/user_guide/gpu.rst @@ -59,7 +59,7 @@ You can get more detailed info on each adapter like this:: import pprint for a in fpl.enumerate_adapters(): - pprint.pprint(a.request_adapter_info()) + pprint.pprint(a.info) General description of the fields: * vendor: GPU manufacturer @@ -267,7 +267,7 @@ to ``fpl.select_adapter()``:: # get info or summary of all adapters to pick an adapter import pprint for a in fpl.enumerate_adapters(): - pprint.pprint(a.request_adapter_info()) + pprint.pprint(a.info) # example, pick adapter at index 2 chosen_gpu = fpl.enumerate_adapters()[2] diff --git a/examples/tests/testutils.py b/examples/tests/testutils.py index 22747ce08..0e4cd2e1b 100644 --- a/examples/tests/testutils.py +++ b/examples/tests/testutils.py @@ -31,7 +31,7 @@ def get_wgpu_backend(): """ Query the configured wgpu backend driver. """ - code = "import wgpu.utils; info = wgpu.utils.get_default_device().adapter.request_adapter_info(); print(info['adapter_type'], info['backend_type'])" + code = "import wgpu.utils; info = wgpu.utils.get_default_device().adapter.info; print(info['adapter_type'], info['backend_type'])" result = subprocess.run( [ sys.executable, diff --git a/fastplotlib/utils/gui.py b/fastplotlib/utils/gui.py index 1941674ee..1f13c1406 100644 --- a/fastplotlib/utils/gui.py +++ b/fastplotlib/utils/gui.py @@ -60,9 +60,9 @@ def _notebook_print_banner(): # print logo and adapter info adapters = [a for a in wgpu.gpu.enumerate_adapters()] - adapters_info = [a.request_adapter_info() for a in adapters] + adapters_info = [a.info for a in adapters] - default_adapter_info = wgpu.gpu.request_adapter().request_adapter_info() + default_adapter_info = wgpu.gpu.request_adapter().info default_ix = adapters_info.index(default_adapter_info) if len(adapters) > 0: diff --git a/setup.py b/setup.py index 7229dcf25..56900c1fa 100644 --- a/setup.py +++ b/setup.py @@ -4,8 +4,8 @@ install_requires = [ "numpy>=1.23.0", - "wgpu<0.16.0", - "pygfx>=0.1.14,<=0.2.0", + "wgpu>=0.16.0", + "pygfx>=0.3.0", ] From 9ff92a7660bb4c9e32d232391b049a9398befef8 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Wed, 10 Jul 2024 15:38:03 +0200 Subject: [PATCH 090/185] Use real references to `Graphics`, proper garbage collection in ipython and jupyter (#546) * ndarray.ptp -> np.ptp for numpy v2 * use real refs to graphics * code review, cleaner * small fixes --- docs/source/api/layouts/subplot.rst | 1 - examples/notebooks/test_gc.ipynb | 109 ++++++++++--- fastplotlib/graphics/_base.py | 30 ++-- fastplotlib/graphics/_collection_base.py | 44 ++--- fastplotlib/graphics/image.py | 9 +- fastplotlib/graphics/line.py | 9 +- fastplotlib/graphics/line_collection.py | 9 +- .../graphics/selectors/_base_selector.py | 4 +- fastplotlib/graphics/selectors/_linear.py | 4 +- fastplotlib/layouts/_graphic_methods_mixin.py | 4 +- fastplotlib/layouts/_plot_area.py | 150 +++++------------- fastplotlib/widgets/histogram_lut.py | 6 +- scripts/generate_add_graphic_methods.py | 6 +- 13 files changed, 179 insertions(+), 206 deletions(-) diff --git a/docs/source/api/layouts/subplot.rst b/docs/source/api/layouts/subplot.rst index 61f5da307..dc77a725a 100644 --- a/docs/source/api/layouts/subplot.rst +++ b/docs/source/api/layouts/subplot.rst @@ -55,7 +55,6 @@ Methods Subplot.clear Subplot.delete_graphic Subplot.get_rect - Subplot.get_refcounts Subplot.insert_graphic Subplot.map_screen_to_world Subplot.remove_animation diff --git a/examples/notebooks/test_gc.ipynb b/examples/notebooks/test_gc.ipynb index 57d7bb576..6a0130d8e 100644 --- a/examples/notebooks/test_gc.ipynb +++ b/examples/notebooks/test_gc.ipynb @@ -7,6 +7,7 @@ "metadata": {}, "outputs": [], "source": [ + "import weakref\n", "import fastplotlib as fpl\n", "import numpy as np\n", "import pytest" @@ -23,7 +24,7 @@ " for i in range(len(plot_objects)):\n", " with pytest.raises(ReferenceError) as failure:\n", " plot_objects[i]\n", - " pytest.fail(f\"GC failed for object: {objects[i]}\")" + " pytest.fail(f\"GC failed for object: {plot_objects[i]} of type: {plot_objects[i].__class__.__name__}\")" ] }, { @@ -49,7 +50,15 @@ "\n", "line_collection_data = [points_data[:, 1].copy() for i in range(10)]\n", "\n", - "img_data = np.random.rand(2_000, 2_000)" + "img_data = np.random.rand(1_000, 1_000)" + ] + }, + { + "cell_type": "markdown", + "id": "2a8a92e1-70bc-41b5-9ad8-b86dab6e74eb", + "metadata": {}, + "source": [ + "# Make references to each graphic" ] }, { @@ -76,42 +85,98 @@ "linear_region_sel_img = image.add_linear_region_selector(name=\"image_linear_region_sel\")" ] }, + { + "cell_type": "markdown", + "id": "d691c3c6-0d82-4aa8-90e9-165efffda369", + "metadata": {}, + "source": [ + "# Add event handlers" + ] + }, { "cell_type": "code", "execution_count": null, - "id": "bb2083c1-f6b7-417c-86b8-9980819917db", + "id": "64198fd0-edd4-4ba1-8082-a65d57b83881", "metadata": {}, "outputs": [], "source": [ "def feature_changed_handler(ev):\n", - " pass\n", - "\n", - "\n", + " pass" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4a86c37b-41ce-4b50-af43-ef61d36b7d81", + "metadata": {}, + "outputs": [], + "source": [ "objects = list()\n", + "weakrefs = list() # used to make sure the real objs are garbage collected\n", "for subplot in fig:\n", - " objects += subplot.objects\n", - "\n", + " for obj in subplot.objects:\n", + " objects.append(obj)\n", + " weakrefs.append(weakref.proxy(obj))\n", "\n", "for g in objects:\n", " for feature in g._features:\n", - " # if isinstance(g, fpl.LineCollection):?\n", - " # continue # skip collections for now\n", - " \n", - " g.add_event_handler(feature_changed_handler, feature)\n", - "\n", + " g.add_event_handler(feature_changed_handler, feature)" + ] + }, + { + "cell_type": "markdown", + "id": "ecd09bc8-f051-4ffd-93d3-63c262064bb4", + "metadata": {}, + "source": [ + "# Show figure" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "11cf43c0-94fa-4e75-a85d-04a3f5c97729", + "metadata": {}, + "outputs": [], + "source": [ "fig.show()" ] }, + { + "cell_type": "markdown", + "id": "ad58698e-1a21-466d-b640-78500cfcb229", + "metadata": {}, + "source": [ + "# Clear fig and user-created objects list" + ] + }, { "cell_type": "code", "execution_count": null, - "id": "ba9fffeb-45bd-4a0c-a941-e7c7e68f2e55", + "id": "5849b8b3-8765-4e37-868f-6be0d127bdee", "metadata": {}, "outputs": [], "source": [ "fig.clear()" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "8ea2206b-2522-40c2-beba-c3a377990219", + "metadata": {}, + "outputs": [], + "source": [ + "objects.clear()" + ] + }, + { + "cell_type": "markdown", + "id": "a7686046-65b6-4eb4-832a-7ca72c7f9bad", + "metadata": {}, + "source": [ + "# test gc" + ] + }, { "cell_type": "code", "execution_count": null, @@ -119,7 +184,15 @@ "metadata": {}, "outputs": [], "source": [ - "test_references(objects)" + "test_references(weakrefs)" + ] + }, + { + "cell_type": "markdown", + "id": "4f927111-61c5-468e-8c90-b7b5338606ba", + "metadata": {}, + "source": [ + "# test for ImageWidget" ] }, { @@ -152,11 +225,11 @@ { "cell_type": "code", "execution_count": null, - "id": "38557b63-997f-433a-b744-e562e30be6ae", + "id": "7e855043-91c1-4f6c-bed3-b69cf4a87f84", "metadata": {}, "outputs": [], "source": [ - "old_graphics = iw.managed_graphics\n", + "old_graphics = [weakref.proxy(g) for g in iw.managed_graphics]\n", "\n", "new_movies = [np.random.rand(100, 200, 200) for i in range(6)]\n", "\n", @@ -176,7 +249,7 @@ { "cell_type": "code", "execution_count": null, - "id": "712bb6ea-7244-4e03-8dfa-9419daa34915", + "id": "ad3d2a24-88b3-4071-a49c-49667d5a7813", "metadata": {}, "outputs": [], "source": [] diff --git a/fastplotlib/graphics/_base.py b/fastplotlib/graphics/_base.py index cab941894..01482ae6e 100644 --- a/fastplotlib/graphics/_base.py +++ b/fastplotlib/graphics/_base.py @@ -42,7 +42,7 @@ class Graphic: - _features = {} + _features: set[str] = {} def __init_subclass__(cls, **kwargs): # set the type of the graphic in lower case like "image", "line_collection", etc. @@ -177,7 +177,7 @@ def block_events(self, value: bool): def world_object(self) -> pygfx.WorldObject: """Associated pygfx WorldObject. Always returns a proxy, real object cannot be accessed directly.""" # We use weakref to simplify garbage collection - return weakref.proxy(WORLD_OBJECTS[self._fpl_address]) + return weakref.proxy(WORLD_OBJECTS[hex(id(self))]) def _set_world_object(self, wo: pygfx.WorldObject): WORLD_OBJECTS[self._fpl_address] = wo @@ -348,24 +348,17 @@ def __repr__(self): else: return rval - def __eq__(self, other): - # This is necessary because we use Graphics as weakref proxies - if not isinstance(other, Graphic): - raise TypeError("`==` operator is only valid between two Graphics") - - if self._fpl_address == other._fpl_address: - return True - - return False - - def _fpl_cleanup(self): + def _fpl_prepare_del(self): """ Cleans up the graphic in preparation for __del__(), such as removing event handlers from plot renderer, feature event handlers, etc. Optionally implemented in subclasses """ - # remove event handlers + # signal that a deletion has been requested + self.deleted = True + + # clear event handlers self.clear_event_handlers() # clear any attached event handlers and animation functions @@ -394,13 +387,10 @@ def _fpl_cleanup(self): self.world_object._event_handlers.clear() - for n in self._features: - fea = getattr(self, f"_{n}") - fea.clear_event_handlers() - def __del__(self): - self.deleted = True - del WORLD_OBJECTS[self._fpl_address] + # remove world object if created + # world object does not exist if an exception was raised during __init__ which is why this check exists + WORLD_OBJECTS.pop(hex(id(self)), None) def rotate(self, alpha: float, axis: Literal["x", "y", "z"] = "y"): """Rotate the Graphic with respect to the world. diff --git a/fastplotlib/graphics/_collection_base.py b/fastplotlib/graphics/_collection_base.py index 2805c684d..36f83ec7a 100644 --- a/fastplotlib/graphics/_collection_base.py +++ b/fastplotlib/graphics/_collection_base.py @@ -1,12 +1,9 @@ +from contextlib import suppress from typing import Any -import weakref import numpy as np -from ._base import HexStr, Graphic - -# Dict that holds all collection graphics in one python instance -COLLECTION_GRAPHICS: dict[HexStr, Graphic] = dict() +from ._base import Graphic class CollectionProperties: @@ -193,25 +190,17 @@ def __init__(self, name: str = None, metadata: Any = None, **kwargs): super().__init__(name=name, metadata=metadata, **kwargs) # list of mem locations of the graphics - self._graphics: list[str] = list() + self._graphics: list[Graphic] = list() self._graphics_changed: bool = True - self._graphics_array: np.ndarray[Graphic] = None self._iter = None @property def graphics(self) -> np.ndarray[Graphic]: - """The Graphics within this collection. Always returns a proxy to the Graphics.""" - if self._graphics_changed: - proxies = [ - weakref.proxy(COLLECTION_GRAPHICS[addr]) for addr in self._graphics - ] - self._graphics_array = np.array(proxies) - self._graphics_array.flags["WRITEABLE"] = False - self._graphics_changed = False + """The Graphics within this collection.""" - return self._graphics_array + return np.asarray(self._graphics) def add_graphic(self, graphic: Graphic): """ @@ -231,10 +220,7 @@ def add_graphic(self, graphic: Graphic): f"you are trying to add a {graphic.__class__.__name__}." ) - addr = graphic._fpl_address - COLLECTION_GRAPHICS[addr] = graphic - - self._graphics.append(addr) + self._graphics.append(graphic) self.world_object.add(graphic.world_object) @@ -254,7 +240,7 @@ def remove_graphic(self, graphic: Graphic): """ - self._graphics.remove(graphic._fpl_address) + self._graphics.remove(graphic) self.world_object.remove(graphic.world_object) @@ -313,7 +299,7 @@ def _fpl_add_plot_area_hook(self, plot_area): for g in self: g._fpl_add_plot_area_hook(plot_area) - def _fpl_cleanup(self): + def _fpl_prepare_del(self): """ Cleans up the graphic in preparation for __del__(), such as removing event handlers from plot renderer, feature event handlers, etc. @@ -324,20 +310,21 @@ def _fpl_cleanup(self): self.world_object._event_handlers.clear() for g in self: - g._fpl_cleanup() + g._fpl_prepare_del() def __getitem__(self, key) -> CollectionIndexer: if np.issubdtype(type(key), np.integer): - addr = self._graphics[key] - return weakref.proxy(COLLECTION_GRAPHICS[addr]) + return self.graphics[key] return self._indexer(selection=self.graphics[key], features=self._features) def __del__(self): + # detach children self.world_object.clear() - for addr in self._graphics: - del COLLECTION_GRAPHICS[addr] + for g in self.graphics: + g._fpl_prepare_del() + del g super().__del__() @@ -350,9 +337,8 @@ def __iter__(self): def __next__(self) -> Graphic: index = next(self._iter) - addr = self._graphics[index] - return weakref.proxy(COLLECTION_GRAPHICS[addr]) + return self._graphics[index] def __repr__(self): rval = super().__repr__() diff --git a/fastplotlib/graphics/image.py b/fastplotlib/graphics/image.py index 5805804c7..866ed2486 100644 --- a/fastplotlib/graphics/image.py +++ b/fastplotlib/graphics/image.py @@ -1,5 +1,4 @@ from typing import * -import weakref import pygfx @@ -307,7 +306,7 @@ def add_linear_selector( size=size, center=center, axis=axis, - parent=weakref.proxy(self), + parent=self, **kwargs, ) @@ -316,7 +315,7 @@ def add_linear_selector( # place selector above this graphic selector.offset = selector.offset + (0.0, 0.0, self.offset[-1] + 1) - return weakref.proxy(selector) + return selector def add_linear_region_selector( self, @@ -384,7 +383,7 @@ def add_linear_region_selector( center=center, axis=axis, fill_color=fill_color, - parent=weakref.proxy(self), + parent=self, **kwargs, ) @@ -393,4 +392,4 @@ def add_linear_region_selector( # place above this graphic selector.offset = selector.offset + (0.0, 0.0, self.offset[-1] + 1) - return weakref.proxy(selector) + return selector diff --git a/fastplotlib/graphics/line.py b/fastplotlib/graphics/line.py index d0a8cc336..f352dfde5 100644 --- a/fastplotlib/graphics/line.py +++ b/fastplotlib/graphics/line.py @@ -1,5 +1,4 @@ from typing import * -import weakref import numpy as np @@ -147,7 +146,7 @@ def add_linear_selector( size=size, center=center, axis=axis, - parent=weakref.proxy(self), + parent=self, **kwargs, ) @@ -156,7 +155,7 @@ def add_linear_selector( # place selector above this graphic selector.offset = selector.offset + (0.0, 0.0, self.offset[-1] + 1) - return weakref.proxy(selector) + return selector def add_linear_region_selector( self, @@ -204,7 +203,7 @@ def add_linear_region_selector( size=size, center=center, axis=axis, - parent=weakref.proxy(self), + parent=self, **kwargs, ) @@ -215,7 +214,7 @@ def add_linear_region_selector( # PlotArea manages this for garbage collection etc. just like all other Graphics # so we should only work with a proxy on the user-end - return weakref.proxy(selector) + return selector # TODO: this method is a bit of a mess, can refactor later def _get_linear_selector_init_args( diff --git a/fastplotlib/graphics/line_collection.py b/fastplotlib/graphics/line_collection.py index 01faa9164..666d441e4 100644 --- a/fastplotlib/graphics/line_collection.py +++ b/fastplotlib/graphics/line_collection.py @@ -1,5 +1,4 @@ from typing import * -import weakref import numpy as np @@ -378,7 +377,7 @@ def add_linear_selector( size=size, center=center, axis=axis, - parent=weakref.proxy(self), + parent=self, **kwargs, ) @@ -387,7 +386,7 @@ def add_linear_selector( # place selector above this graphic selector.offset = selector.offset + (0.0, 0.0, self.offset[-1] + 1) - return weakref.proxy(selector) + return selector def add_linear_region_selector( self, @@ -435,7 +434,7 @@ def add_linear_region_selector( size=size, center=center, axis=axis, - parent=weakref.proxy(self), + parent=self, **kwargs, ) @@ -446,7 +445,7 @@ def add_linear_region_selector( # PlotArea manages this for garbage collection etc. just like all other Graphics # so we should only work with a proxy on the user-end - return weakref.proxy(selector) + return selector def _get_linear_selector_init_args(self, axis, padding): # use bbox to get size and center diff --git a/fastplotlib/graphics/selectors/_base_selector.py b/fastplotlib/graphics/selectors/_base_selector.py index 0fc48058d..ab7bda049 100644 --- a/fastplotlib/graphics/selectors/_base_selector.py +++ b/fastplotlib/graphics/selectors/_base_selector.py @@ -364,10 +364,10 @@ def _key_up(self, ev): self._move_info = None - def _fpl_cleanup(self): + def _fpl_prepare_del(self): if hasattr(self, "_pfunc_fill"): self._plot_area.renderer.remove_event_handler( self._pfunc_fill, "pointer_down" ) del self._pfunc_fill - super()._fpl_cleanup() + super()._fpl_prepare_del() diff --git a/fastplotlib/graphics/selectors/_linear.py b/fastplotlib/graphics/selectors/_linear.py index 22ba96a28..e7b0032f9 100644 --- a/fastplotlib/graphics/selectors/_linear.py +++ b/fastplotlib/graphics/selectors/_linear.py @@ -390,8 +390,8 @@ def _move_graphic(self, delta: np.ndarray): else: self.selection = self.selection + delta[1] - def _fpl_cleanup(self): + def _fpl_prepare_del(self): for widget in self._handled_widgets: widget.unobserve(self._ipywidget_callback, "value") - super()._fpl_cleanup() + super()._fpl_prepare_del() diff --git a/fastplotlib/layouts/_graphic_methods_mixin.py b/fastplotlib/layouts/_graphic_methods_mixin.py index 387549ade..ea553f119 100644 --- a/fastplotlib/layouts/_graphic_methods_mixin.py +++ b/fastplotlib/layouts/_graphic_methods_mixin.py @@ -3,7 +3,6 @@ from typing import * import numpy -import weakref from ..graphics import * from ..graphics._base import Graphic @@ -25,8 +24,7 @@ def _create_graphic(self, graphic_class, *args, **kwargs) -> Graphic: graphic = graphic_class(*args, **kwargs) self.add_graphic(graphic, center=center) - # only return a proxy to the real graphic - return weakref.proxy(graphic) + return graphic def add_image( self, diff --git a/fastplotlib/layouts/_plot_area.py b/fastplotlib/layouts/_plot_area.py index d8e0adebc..36d9c4019 100644 --- a/fastplotlib/layouts/_plot_area.py +++ b/fastplotlib/layouts/_plot_area.py @@ -1,7 +1,5 @@ from inspect import getfullargspec -from sys import getrefcount -from typing import TypeAlias, Literal, Union -import weakref +from typing import Literal, Union from warnings import warn import numpy as np @@ -12,85 +10,19 @@ from ._utils import create_controller from ..graphics._base import Graphic -from ..graphics._collection_base import GraphicCollection from ..graphics.selectors._base_selector import BaseSelector from ..legends import Legend -HexStr: TypeAlias = str - - -class References: - """ - This is the only place where the real graphic objects are stored. Everywhere else gets a proxy. - """ - - _graphics: dict[HexStr, Graphic] = dict() - _selectors: dict[HexStr, BaseSelector] = dict() - _legends: dict[HexStr, Legend] = dict() - - def add(self, graphic: Graphic | BaseSelector | Legend): - """Adds the real graphic to the dict""" - addr = graphic._fpl_address - - if isinstance(graphic, BaseSelector): - self._selectors[addr] = graphic - - elif isinstance(graphic, Legend): - self._legends[addr] = graphic - - elif isinstance(graphic, Graphic): - self._graphics[addr] = graphic - - else: - raise TypeError("Can only add Graphic, Selector or Legend types") - - def remove(self, address): - if address in self._graphics.keys(): - del self._graphics[address] - elif address in self._selectors.keys(): - del self._selectors[address] - elif address in self._legends.keys(): - del self._legends[address] - else: - raise KeyError(f"graphic with address not found: {address}") - - def get_proxies(self, refs: list[HexStr]) -> tuple[weakref.proxy]: - proxies = list() - for key in refs: - if key in self._graphics.keys(): - proxies.append(weakref.proxy(self._graphics[key])) - - elif key in self._selectors.keys(): - proxies.append(weakref.proxy(self._selectors[key])) - - elif key in self._legends.keys(): - proxies.append(weakref.proxy(self._legends[key])) - - else: - raise KeyError(f"graphic object with address not found: {key}") - - return tuple(proxies) - - def get_refcounts(self) -> dict[HexStr:int]: - counts = dict() - - for item in (self._graphics, self._selectors, self._legends): - for k in item.keys(): - counts[(k, item[k].name, item[k].__class__.__name__)] = getrefcount( - item[k] - ) - - return counts - - -REFERENCES = References() +try: + ip = get_ipython() +except NameError: + IS_IPYTHON = False +else: + IS_IPYTHON = True class PlotArea: - def get_refcounts(self): - return REFERENCES.get_refcounts() - def __init__( self, parent: Union["PlotArea", "Figure"], @@ -158,14 +90,14 @@ def __init__( # list of hex id strings for all graphics managed by this PlotArea # the real Graphic instances are managed by REFERENCES - self._graphics: list[HexStr] = list() + self._graphics: list[Graphic] = list() # selectors are in their own list so they can be excluded from scene bbox calculations # managed similar to GRAPHICS for garbage collection etc. - self._selectors: list[HexStr] = list() + self._selectors: list[BaseSelector] = list() # legends, managed just like other graphics as explained above - self._legends: list[HexStr] = list() + self._legends: list[Legend] = list() self._name = name @@ -276,18 +208,18 @@ def controller(self, new_controller: str | pygfx.Controller): @property def graphics(self) -> tuple[Graphic, ...]: - """Graphics in the plot area. Always returns a proxy to the Graphic instances.""" - return REFERENCES.get_proxies(self._graphics) + """Graphics in the plot area.""" + return tuple(self._graphics) @property def selectors(self) -> tuple[BaseSelector, ...]: - """Selectors in the plot area. Always returns a proxy to the Graphic instances.""" - return REFERENCES.get_proxies(self._selectors) + """Selectors in the plot area.""" + return tuple(self._selectors) @property def legends(self) -> tuple[Legend, ...]: """Legends in the plot area.""" - return REFERENCES.get_proxies(self._legends) + return tuple(self._legends) @property def objects(self) -> tuple[Graphic | BaseSelector | Legend, ...]: @@ -531,32 +463,25 @@ def _add_or_insert_graphic( if graphic.name is not None: # skip for those that have no name self._check_graphic_name_exists(graphic.name) - addr = graphic._fpl_address - if isinstance(graphic, BaseSelector): - addr_list = self._selectors + obj_list = self._selectors elif isinstance(graphic, Legend): - addr_list = self._legends + obj_list = self._legends elif isinstance(graphic, Graphic): - addr_list = self._graphics + obj_list = self._graphics else: raise TypeError("graphic must be of type Graphic | BaseSelector | Legend") if action == "insert": - addr_list.insert(index, addr) + obj_list.insert(index, graphic) elif action == "add": - addr_list.append(addr) + obj_list.append(graphic) else: raise ValueError("valid actions are 'insert' | 'add'") - REFERENCES.add(graphic) - - # now that it's in the dict, just use the weakref - graphic = weakref.proxy(graphic) - # add world object to scene self.scene.add(graphic.world_object) @@ -696,29 +621,36 @@ def delete_graphic(self, graphic: Graphic): The graphic to delete """ - # TODO: proper gc of selectors, RAM is freed for regular graphics but not selectors - # TODO: references to selectors must be lingering somewhere - # TODO: update March 2024, I think selectors are gc properly, should check - # get memory address - address = graphic._fpl_address - if graphic not in self: raise KeyError(f"Graphic not found in plot area: {graphic}") - # check which type it is - for l in [self._graphics, self._selectors, self._legends]: - if address in l: - l.remove(address) - break + if isinstance(graphic, BaseSelector): + self._selectors.remove(graphic) + elif isinstance(graphic, Legend): + self._legends.remove(graphic) + + elif isinstance(graphic, Graphic): + self._graphics.remove(graphic) # remove from scene if necessary if graphic.world_object in self.scene.children: self.scene.remove(graphic.world_object) # cleanup - graphic._fpl_cleanup() - - REFERENCES.remove(address) + graphic._fpl_prepare_del() + + if IS_IPYTHON: + # remove any references that ipython might have made + ip = get_ipython() + + # check both namespaces + for namespace in [ip.user_ns, ip.user_ns_hidden]: + # find the reference + for ref, obj in namespace.items(): + if graphic is obj: + # we found the reference, remove from ipython + ip.del_var(ref) + break def clear(self): """ diff --git a/fastplotlib/widgets/histogram_lut.py b/fastplotlib/widgets/histogram_lut.py index 02c21aa38..872cf4319 100644 --- a/fastplotlib/widgets/histogram_lut.py +++ b/fastplotlib/widgets/histogram_lut.py @@ -311,8 +311,8 @@ def disconnect_image_graphic(self): del self._image_graphic # self._image_graphic = None - def _fpl_cleanup(self): - self._linear_region_selector._fpl_cleanup() - self._histogram_line._fpl_cleanup() + def _fpl_prepare_del(self): + self._linear_region_selector._fpl_prepare_del() + self._histogram_line._fpl_prepare_del() del self._histogram_line del self._linear_region_selector diff --git a/scripts/generate_add_graphic_methods.py b/scripts/generate_add_graphic_methods.py index 3f45d9007..d69185521 100644 --- a/scripts/generate_add_graphic_methods.py +++ b/scripts/generate_add_graphic_methods.py @@ -29,8 +29,7 @@ def generate_add_graphics_methods(): f.write("# This is an auto-generated file and should not be modified directly\n\n") f.write("from typing import *\n\n") - f.write("import numpy\n") - f.write("import weakref\n\n") + f.write("import numpy\n\n") f.write("from ..graphics import *\n") f.write("from ..graphics._base import Graphic\n\n") @@ -49,8 +48,7 @@ def generate_add_graphics_methods(): f.write(" self._check_graphic_name_exists(kwargs['name'])\n\n") f.write(" graphic = graphic_class(*args, **kwargs)\n") f.write(" self.add_graphic(graphic, center=center)\n\n") - f.write(" # only return a proxy to the real graphic\n") - f.write(" return weakref.proxy(graphic)\n\n") + f.write(" return graphic\n\n") for m in modules: class_name = m From e79e5faea39a7ebee9b9f21d16422b0c0b9cf16e Mon Sep 17 00:00:00 2001 From: apasarkar Date: Mon, 15 Jul 2024 14:03:36 -0400 Subject: [PATCH 091/185] Updates LUT to deal with divide by zero errors and updates figure shape logic in imagewidget to deal with multiple shape values in constructor --- fastplotlib/widgets/histogram_lut.py | 15 +++++++++------ fastplotlib/widgets/image.py | 21 ++++++++++++++++----- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/fastplotlib/widgets/histogram_lut.py b/fastplotlib/widgets/histogram_lut.py index 872cf4319..e0f316326 100644 --- a/fastplotlib/widgets/histogram_lut.py +++ b/fastplotlib/widgets/histogram_lut.py @@ -25,8 +25,8 @@ def __init__( ---------- data image_graphic - nbins - flank_divisor: float, default 5.0 + nbins: int, defaut 100. Total number of bins used in the histogram + flank_divisor: float, default 5.0. Fraction of empty histogram bins on the tails of the distribution set `np.inf` for no flanks kwargs """ @@ -161,9 +161,10 @@ def _calculate_histogram(self, data): hist, edges = np.histogram(data_ss, bins=self._nbins) # used if data ptp <= 10 because event things get weird - # with tiny world objects due to floating point error + # with tiny world objects due to floating point error # so if ptp <= 10, scale up by a factor - self._scale_factor: int = max(1, 100 * int(10 / np.ptp(data_ss))) + data_interval = edges[-1] - edges[0] + self._scale_factor: int = max(1, 100 * int(10 / data_interval)) edges = edges * self._scale_factor @@ -178,7 +179,6 @@ def _calculate_histogram(self, data): ) edges_flanked = np.concatenate((flank_left, edges, flank_right)) - np.unique(np.diff(edges_flanked)) hist_flanked = np.concatenate( (np.zeros(flank_nbins), hist, np.zeros(flank_nbins)) @@ -186,7 +186,10 @@ def _calculate_histogram(self, data): # scale 0-100 to make it easier to see # float32 data can produce unnecessarily high values - hist_scaled = hist_flanked / (hist_flanked.max() / 100) + hist_scale_value = hist_flanked.max() + if np.allclose(hist_scale_value, 0): + hist_scale_value = 1 + hist_scaled = hist_flanked / (hist_scale_value / 100) if edges_flanked.size > hist_scaled.size: # we don't care about accuracy here so if it's off by 1-2 bins that's fine diff --git a/fastplotlib/widgets/image.py b/fastplotlib/widgets/image.py index df9b46b55..60f1720fa 100644 --- a/fastplotlib/widgets/image.py +++ b/fastplotlib/widgets/image.py @@ -368,15 +368,25 @@ def __init__( if all([_is_arraylike(d) for d in data]): # Grid computations if figure_shape is None: - figure_shape = calculate_figure_shape(len(data)) + if figure_kwargs is not None: + if 'shape' in figure_kwargs: + figure_shape = figure_kwargs['shape'] + else: + figure_shape = calculate_figure_shape(len(data)) - # verify that user-specified figure shape is large enough for the number of image arrays passed - elif figure_shape[0] * figure_shape[1] < len(data): + # Regardless of how figure_shape is computed, below code + # verifies that figure shape is large enough for the number of image arrays passed + if figure_shape[0] * figure_shape[1] < len(data): + original_shape = (figure_shape[0], figure_shape[1]) figure_shape = calculate_figure_shape(len(data)) warn( - f"Invalid `figure_shape` passed, setting figure shape to: {figure_shape}" + f"Original `figure_shape` was: {original_shape} " + f" but data length is {len(data)}" + f" Resetting figure shape to: {figure_shape}" ) + + self._data: list[np.ndarray] = data # Establish number of image dimensions and number of scrollable dimensions for each array @@ -506,13 +516,14 @@ def __init__( # update the default kwargs with any user-specified kwargs # user specified kwargs will overwrite the defaults figure_kwargs_default.update(figure_kwargs) + figure_kwargs_default['shape'] = figure_shape if graphic_kwargs is None: graphic_kwargs = dict() graphic_kwargs.update({"cmap": cmap}) - self._figure: Figure = Figure(shape=figure_shape, **figure_kwargs_default) + self._figure: Figure = Figure(**figure_kwargs_default) self._histogram_widget = histogram_widget for data_ix, (d, subplot) in enumerate(zip(self.data, self.figure)): From aad49960ab97e69d86dcf928819047d36263374e Mon Sep 17 00:00:00 2001 From: apasarkar Date: Mon, 15 Jul 2024 14:27:20 -0400 Subject: [PATCH 092/185] Includes formatting on imagewidget code --- fastplotlib/widgets/image.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/fastplotlib/widgets/image.py b/fastplotlib/widgets/image.py index 60f1720fa..6c270b378 100644 --- a/fastplotlib/widgets/image.py +++ b/fastplotlib/widgets/image.py @@ -369,8 +369,8 @@ def __init__( # Grid computations if figure_shape is None: if figure_kwargs is not None: - if 'shape' in figure_kwargs: - figure_shape = figure_kwargs['shape'] + if "shape" in figure_kwargs: + figure_shape = figure_kwargs["shape"] else: figure_shape = calculate_figure_shape(len(data)) @@ -385,8 +385,6 @@ def __init__( f" Resetting figure shape to: {figure_shape}" ) - - self._data: list[np.ndarray] = data # Establish number of image dimensions and number of scrollable dimensions for each array @@ -516,7 +514,7 @@ def __init__( # update the default kwargs with any user-specified kwargs # user specified kwargs will overwrite the defaults figure_kwargs_default.update(figure_kwargs) - figure_kwargs_default['shape'] = figure_shape + figure_kwargs_default["shape"] = figure_shape if graphic_kwargs is None: graphic_kwargs = dict() From c2071675c2b927ccf50c648305f609f87ec017e8 Mon Sep 17 00:00:00 2001 From: apasarkar Date: Mon, 15 Jul 2024 14:51:51 -0400 Subject: [PATCH 093/185] Updates docstring for histogram_lut --- fastplotlib/widgets/histogram_lut.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fastplotlib/widgets/histogram_lut.py b/fastplotlib/widgets/histogram_lut.py index e0f316326..a3b36eb45 100644 --- a/fastplotlib/widgets/histogram_lut.py +++ b/fastplotlib/widgets/histogram_lut.py @@ -25,9 +25,10 @@ def __init__( ---------- data image_graphic - nbins: int, defaut 100. Total number of bins used in the histogram - flank_divisor: float, default 5.0. Fraction of empty histogram bins on the tails of the distribution - set `np.inf` for no flanks + nbins: int, defaut 100. + Total number of bins used in the histogram + flank_divisor: float, default 5.0. + Fraction of empty histogram bins on the tails of the distribution set `np.inf` for no flanks kwargs """ super().__init__(**kwargs) From 0b3d36465e89ea6bd7f38d72244229e22924a653 Mon Sep 17 00:00:00 2001 From: Caitlin Lewis <69729525+clewis7@users.noreply.github.com> Date: Tue, 16 Jul 2024 11:28:46 -0400 Subject: [PATCH 094/185] Update GOVERNANCE.md (#556) add Almar to advisory board --- GOVERNANCE.md | 1 + 1 file changed, 1 insertion(+) diff --git a/GOVERNANCE.md b/GOVERNANCE.md index 27acb1c45..59b844621 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -45,6 +45,7 @@ The Advisory Committee holds a significant interest in fastplotlib as determined 1. Eric Thomson 1. Andrea Giovannucci 1. John Pearson +1. Almar Klein Responsibilities: From 90af0d4a136d918f99551b3fead28773693e40cf Mon Sep 17 00:00:00 2001 From: apasarkar Date: Tue, 16 Jul 2024 12:12:08 -0400 Subject: [PATCH 095/185] Changes when figure_kwargs is defined for cleaner conditional logic --- fastplotlib/widgets/image.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/fastplotlib/widgets/image.py b/fastplotlib/widgets/image.py index 6c270b378..749403781 100644 --- a/fastplotlib/widgets/image.py +++ b/fastplotlib/widgets/image.py @@ -357,6 +357,9 @@ def __init__( """ self._names = None + if figure_kwargs is None: + figure_kwargs = dict() + # output context self._output = None @@ -368,9 +371,8 @@ def __init__( if all([_is_arraylike(d) for d in data]): # Grid computations if figure_shape is None: - if figure_kwargs is not None: - if "shape" in figure_kwargs: - figure_shape = figure_kwargs["shape"] + if "shape" in figure_kwargs: + figure_shape = figure_kwargs["shape"] else: figure_shape = calculate_figure_shape(len(data)) @@ -508,8 +510,6 @@ def __init__( ) figure_kwargs_default = {"controller_ids": "sync"} - if figure_kwargs is None: - figure_kwargs = dict() # update the default kwargs with any user-specified kwargs # user specified kwargs will overwrite the defaults From 23e8957f3ef06d4a3adba37884a16b31aeec5d2e Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Thu, 25 Jul 2024 09:21:20 -0400 Subject: [PATCH 096/185] remove checks for pending uploads from tests (#562) --- tests/test_colors_buffer_manager.py | 10 ---------- tests/test_positions_data_buffer_manager.py | 7 ------- tests/test_positions_graphics.py | 1 - tests/test_sizes_buffer_manager.py | 8 +------- tests/utils.py | 11 ----------- 5 files changed, 1 insertion(+), 36 deletions(-) diff --git a/tests/test_colors_buffer_manager.py b/tests/test_colors_buffer_manager.py index 252c6e5c3..8a6c5700f 100644 --- a/tests/test_colors_buffer_manager.py +++ b/tests/test_colors_buffer_manager.py @@ -8,7 +8,6 @@ from fastplotlib.graphics._features import VertexColors, FeatureEvent from .utils import ( generate_slice_indices, - assert_pending_uploads, generate_color_inputs, generate_positions_spiral_data, ) @@ -61,11 +60,8 @@ def test_int(test_graphic): colors = make_colors_buffer() # TODO: placeholder until I make a testing figure where we draw frames only on call - colors.buffer._gfx_pending_uploads.clear() - colors[3] = "r" npt.assert_almost_equal(colors[3], [1.0, 0.0, 0.0, 1.0]) - assert colors.buffer._gfx_pending_uploads[-1] == (3, 1) if test_graphic: # test event @@ -206,9 +202,6 @@ def test_slice(color_input, slice_method: dict, test_graphic: bool): else: colors = make_colors_buffer() - # TODO: placeholder until I make a testing figure where we draw frames only on call - colors.buffer._gfx_pending_uploads.clear() - s = slice_method["slice"] indices = slice_method["indices"] offset = slice_method["offset"] @@ -238,9 +231,6 @@ def test_slice(color_input, slice_method: dict, test_graphic: bool): else: npt.assert_almost_equal(EVENT_RETURN_VALUE.info["user_value"], color_input) - # make sure correct offset and size marked for pending upload - assert_pending_uploads(colors.buffer, offset, size) - # check that others are not touched others_truth = np.repeat([[1.0, 1.0, 1.0, 1.0]], repeats=len(others), axis=0) npt.assert_almost_equal(colors[others], others_truth) diff --git a/tests/test_positions_data_buffer_manager.py b/tests/test_positions_data_buffer_manager.py index de9d179d8..77d049ab5 100644 --- a/tests/test_positions_data_buffer_manager.py +++ b/tests/test_positions_data_buffer_manager.py @@ -6,7 +6,6 @@ from fastplotlib.graphics._features import VertexPositions, FeatureEvent from .utils import ( generate_slice_indices, - assert_pending_uploads, generate_positions_spiral_data, ) @@ -134,9 +133,6 @@ def test_slice(test_graphic, slice_method: dict, test_axis: str): size = slice_method["size"] others = slice_method["others"] - # TODO: placeholder until I make a testing figure where we draw frames only on call - points.buffer._gfx_pending_uploads.clear() - match test_axis: case "y": points[s, 1] = -data[s, 1] @@ -203,6 +199,3 @@ def test_slice(test_graphic, slice_method: dict, test_axis: str): else: npt.assert_almost_equal(EVENT_RETURN_VALUE.info["key"], s) npt.assert_almost_equal(EVENT_RETURN_VALUE.info["value"], -data[s]) - - # make sure correct offset and size marked for pending upload - assert_pending_uploads(points.buffer, offset, size) diff --git a/tests/test_positions_graphics.py b/tests/test_positions_graphics.py index d9c3a4871..81403c06b 100644 --- a/tests/test_positions_graphics.py +++ b/tests/test_positions_graphics.py @@ -21,7 +21,6 @@ generate_color_inputs, MULTI_COLORS_TRUTH, generate_slice_indices, - assert_pending_uploads, ) diff --git a/tests/test_sizes_buffer_manager.py b/tests/test_sizes_buffer_manager.py index 0b34f9588..1d0a17f3d 100644 --- a/tests/test_sizes_buffer_manager.py +++ b/tests/test_sizes_buffer_manager.py @@ -3,7 +3,7 @@ import pytest from fastplotlib.graphics._features import PointsSizesFeature -from .utils import generate_slice_indices, assert_pending_uploads +from .utils import generate_slice_indices def generate_data(input_type: str) -> np.ndarray | float: @@ -52,9 +52,6 @@ def test_slice(slice_method: dict, user_input: str): sizes = PointsSizesFeature(data, n_datapoints=10) - # TODO: placeholder until I make a testing figure where we draw frames only on call - sizes.buffer._gfx_pending_uploads.clear() - match user_input: case "float": sizes[s] = 20.0 @@ -71,6 +68,3 @@ def test_slice(slice_method: dict, user_input: str): npt.assert_almost_equal(sizes[indices], cosine[s]) # make sure other sizes not modified npt.assert_almost_equal(sizes[others], data[others]) - - # make sure correct offset and size marked for pending upload - assert_pending_uploads(sizes.buffer, offset, size) diff --git a/tests/utils.py b/tests/utils.py index 6a25968e1..bc9a092c8 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -106,17 +106,6 @@ def generate_slice_indices(kind: int): } -def assert_pending_uploads(buffer: pygfx.Buffer, offset: int, size: int): - upload_offset, upload_size = buffer._gfx_pending_uploads[-1] - # sometimes when slicing with step, it will over-estimate offset - # but it overestimates to upload 1 extra point so it's fine - assert (upload_offset == offset) or (upload_offset == offset - 1) - - # sometimes when slicing with step, it will over-estimate size - # but it overestimates to upload 1 extra point so it's fine - assert (upload_size == size) or (upload_size == size + 1) - - def generate_positions_spiral_data(inputs: str) -> np.ndarray: """ Generates a spiral/spring From c27f4897973413f2c42ed24c546bb5f97d9abb13 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Fri, 26 Jul 2024 09:08:47 -0400 Subject: [PATCH 097/185] Basic axes (#551) * add colorbar to hlut tool * pause_events contextmanager * cleanup * very basic stuff works * perspective projection implementation * Graphic.add_axes, refactor grids into an object instead of dict * update example * docstrings * type annotations * fixes for gridplots * bbox account for camera state * change method name * Ruler tick text mapping * update docs * docstring * update docs * set axes using scence bbox in Figure.show() for WgpuManualOffscreenCanvas so axes are somewhat sensible * update year * docs * update examples * PlotArea has background * fix linear selector for images * linear selector examples with axes stuff * comments * change title * docs conf.py * api docs * remove pause_events from graphics.__all__ * remove gpu.rst * accidentally removed gpu guide from docs * smaller heatmap * smaller heatmap * fix gc * hlut fixes * update example nbs * add new nb tests screenshots * add desktop test screenshots * better stuff * fix Subplot.get_rect() * Figure.show() works well for tests and docs gallery * fix axes * update examples * fix axes when non auto grid * desktop screenshots * add selector tools test screenshot * update nb test and screenshots * update doc * black * very basic stuff works * perspective projection implementation * Graphic.add_axes, refactor grids into an object instead of dict * update example * docstrings * add colorbar to hlut tool * pause_events contextmanager * cleanup * type annotations * fixes for gridplots * bbox account for camera state * change method name * Ruler tick text mapping * update docs * docstring * update docs * set axes using scence bbox in Figure.show() for WgpuManualOffscreenCanvas so axes are somewhat sensible * update year * docs * update examples * PlotArea has background * fix linear selector for images * linear selector examples with axes stuff * comments * change title * docs conf.py * api docs * remove pause_events from graphics.__all__ * remove gpu.rst * accidentally removed gpu guide from docs * smaller heatmap * smaller heatmap * fix gc * hlut fixes * update example nbs * add new nb tests screenshots * add desktop test screenshots * better stuff * fix Subplot.get_rect() * Figure.show() works well for tests and docs gallery * fix axes * update examples * fix axes when non auto grid * desktop screenshots * add selector tools test screenshot * update nb test and screenshots * update doc * black * add fastplotlib to intersphinx for examples links * don't test linear selector example because github action is being weird * set timeout of 10 mins for CI actions * remove selectors from testutils * try something * try anotehr thing * add back axes update using camera * try yet another thing * axes.update_using_camera in Figure.show() is fine, but is viewport.render blocking * see if we can move the render call to test_examples.py * wrong attr * should work on rtd and CI test examples screenshots * fix keyerror * diffs are a bit larger now because of axes, increase tolerance and remove linear selector from tests because the diff is very large * update tolerances for nb test * forgot to update some test screenshots --- .github/workflows/ci.yml | 3 + .github/workflows/screenshots.yml | 1 + docs/source/api/fastplotlib.rst | 14 + docs/source/api/gpu.rst | 6 - docs/source/api/graphics/ImageGraphic.rst | 2 + docs/source/api/graphics/LineCollection.rst | 2 + docs/source/api/graphics/LineGraphic.rst | 2 + docs/source/api/graphics/LineStack.rst | 2 + docs/source/api/graphics/ScatterGraphic.rst | 2 + docs/source/api/graphics/TextGraphic.rst | 2 + docs/source/api/layouts/subplot.rst | 4 +- .../api/selectors/LinearRegionSelector.rst | 2 + docs/source/api/selectors/LinearSelector.rst | 2 + docs/source/conf.py | 6 +- docs/source/index.rst | 4 +- examples/desktop/gridplot/gridplot.py | 6 +- .../desktop/gridplot/gridplot_non_square.py | 6 +- .../desktop/gridplot/multigraphic_gridplot.py | 8 +- examples/desktop/heatmap/heatmap.py | 10 +- examples/desktop/heatmap/heatmap_cmap.py | 6 +- examples/desktop/heatmap/heatmap_data.py | 5 +- examples/desktop/heatmap/heatmap_square.py | 5 +- examples/desktop/heatmap/heatmap_vmin_vmax.py | 6 +- examples/desktop/heatmap/heatmap_wide.py | 5 +- examples/desktop/image/image_cmap.py | 6 +- examples/desktop/image/image_rgb.py | 5 +- examples/desktop/image/image_rgbvminvmax.py | 6 +- examples/desktop/image/image_simple.py | 5 +- examples/desktop/image/image_small.py | 5 +- examples/desktop/image/image_vminvmax.py | 6 +- examples/desktop/image/image_widget.py | 6 +- examples/desktop/line/line.py | 6 +- examples/desktop/line/line_cmap.py | 3 +- examples/desktop/line/line_colorslice.py | 5 +- examples/desktop/line/line_dataslice.py | 5 +- .../line_collection/line_collection.py | 6 +- .../line_collection_cmap_values.py | 6 +- ...line_collection_cmap_values_qualitative.py | 7 +- .../line_collection/line_collection_colors.py | 6 +- .../line_collection_slicing.py | 14 +- .../desktop/line_collection/line_stack.py | 3 +- .../desktop/line_collection/line_stack_3d.py | 8 +- examples/desktop/misc/cycle_animation.py | 5 +- examples/desktop/misc/em_wave_animation.py | 28 +- examples/desktop/misc/image_animation.py | 3 +- examples/desktop/misc/line3d_animation.py | 12 +- examples/desktop/misc/line_animation.py | 9 +- examples/desktop/misc/multiplot_animation.py | 7 +- examples/desktop/misc/scatter_animation.py | 5 +- examples/desktop/misc/simple_event.py | 4 +- examples/desktop/scatter/scatter.py | 8 +- examples/desktop/scatter/scatter_cmap.py | 5 +- examples/desktop/scatter/scatter_cmap_iris.py | 6 +- .../desktop/scatter/scatter_colorslice.py | 9 +- .../scatter/scatter_colorslice_iris.py | 6 +- examples/desktop/scatter/scatter_dataslice.py | 9 +- .../desktop/scatter/scatter_dataslice_iris.py | 6 +- examples/desktop/scatter/scatter_iris.py | 7 +- examples/desktop/scatter/scatter_size.py | 3 +- examples/desktop/screenshots/gridplot.png | 4 +- .../screenshots/gridplot_non_square.png | 4 +- examples/desktop/screenshots/heatmap.png | 4 +- examples/desktop/screenshots/image_cmap.png | 4 +- examples/desktop/screenshots/image_rgb.png | 4 +- .../desktop/screenshots/image_rgbvminvmax.png | 4 +- examples/desktop/screenshots/image_simple.png | 4 +- examples/desktop/screenshots/image_small.png | 4 +- .../desktop/screenshots/image_vminvmax.png | 4 +- examples/desktop/screenshots/line.png | 4 +- examples/desktop/screenshots/line_cmap.png | 4 +- .../desktop/screenshots/line_collection.png | 4 +- .../line_collection_cmap_values.png | 4 +- ...ine_collection_cmap_values_qualitative.png | 4 +- .../screenshots/line_collection_colors.png | 4 +- .../screenshots/line_collection_slicing.png | 4 +- .../desktop/screenshots/line_colorslice.png | 4 +- .../desktop/screenshots/line_dataslice.png | 4 +- examples/desktop/screenshots/line_stack.png | 4 +- .../desktop/screenshots/linear_selector.png | 3 + .../desktop/screenshots/scatter_cmap_iris.png | 4 +- .../screenshots/scatter_colorslice_iris.png | 4 +- .../screenshots/scatter_dataslice_iris.png | 4 +- examples/desktop/screenshots/scatter_iris.png | 4 +- examples/desktop/screenshots/scatter_size.png | 4 +- examples/desktop/selectors/README.rst | 2 + examples/desktop/selectors/linear_selector.py | 157 +++++ examples/notebooks/image_widget_test.ipynb | 20 +- examples/notebooks/lineplot.ipynb | 7 +- examples/notebooks/nb_test_utils.py | 2 +- examples/notebooks/scatter.ipynb | 5 +- .../notebooks/screenshots/nb-astronaut.png | 4 +- .../screenshots/nb-astronaut_RGB.png | 4 +- examples/notebooks/screenshots/nb-camera.png | 4 +- .../nb-image-widget-movie-set_data.png | 4 +- .../nb-image-widget-movie-single-0-reset.png | 4 +- .../nb-image-widget-movie-single-0.png | 4 +- .../nb-image-widget-movie-single-279.png | 4 +- ...e-widget-movie-single-50-window-max-33.png | 4 +- ...-widget-movie-single-50-window-mean-13.png | 4 +- ...-widget-movie-single-50-window-mean-33.png | 4 +- ...ge-widget-movie-single-50-window-reset.png | 4 +- .../nb-image-widget-movie-single-50.png | 4 +- .../nb-image-widget-single-gnuplot2.png | 4 +- .../screenshots/nb-image-widget-single.png | 4 +- ...et-zfish-frame-50-frame-apply-gaussian.png | 4 +- ...idget-zfish-frame-50-frame-apply-reset.png | 4 +- ...ge-widget-zfish-frame-50-max-window-13.png | 4 +- ...e-widget-zfish-frame-50-mean-window-13.png | 4 +- ...ge-widget-zfish-frame-50-mean-window-5.png | 4 +- .../nb-image-widget-zfish-frame-50.png | 4 +- .../nb-image-widget-zfish-frame-99.png | 4 +- ...ish-grid-frame-50-frame-apply-gaussian.png | 4 +- ...-zfish-grid-frame-50-frame-apply-reset.png | 4 +- ...dget-zfish-grid-frame-50-max-window-13.png | 4 +- ...get-zfish-grid-frame-50-mean-window-13.png | 4 +- ...dget-zfish-grid-frame-50-mean-window-5.png | 4 +- .../nb-image-widget-zfish-grid-frame-50.png | 4 +- .../nb-image-widget-zfish-grid-frame-99.png | 4 +- ...e-widget-zfish-grid-init-mean-window-5.png | 4 +- ...fish-grid-set_data-reset-indices-false.png | 4 +- ...zfish-grid-set_data-reset-indices-true.png | 4 +- ...-image-widget-zfish-init-mean-window-5.png | 4 +- ...dget-zfish-mixed-rgb-cockatoo-frame-50.png | 4 +- ...dget-zfish-mixed-rgb-cockatoo-set-data.png | 4 +- ...get-zfish-mixed-rgb-cockatoo-windowrgb.png | 4 +- .../notebooks/screenshots/nb-lines-3d.png | 4 +- .../nb-lines-cmap-jet-values-cosine.png | 4 +- .../screenshots/nb-lines-cmap-jet-values.png | 4 +- .../screenshots/nb-lines-cmap-jet.png | 4 +- .../screenshots/nb-lines-cmap-tab-10.png | 4 +- .../nb-lines-cmap-viridis-values.png | 4 +- .../screenshots/nb-lines-cmap-viridis.png | 4 +- .../screenshots/nb-lines-cmap-white.png | 4 +- .../notebooks/screenshots/nb-lines-colors.png | 4 +- .../notebooks/screenshots/nb-lines-data.png | 4 +- .../screenshots/nb-lines-underlay.png | 4 +- examples/notebooks/screenshots/nb-lines.png | 4 +- examples/tests/test_examples.py | 6 +- examples/tests/testutils.py | 3 +- fastplotlib/__init__.py | 1 + fastplotlib/graphics/_axes.py | 598 ++++++++++++++++++ fastplotlib/graphics/_base.py | 24 + fastplotlib/graphics/image.py | 10 +- fastplotlib/graphics/selectors/_linear.py | 11 +- fastplotlib/graphics/utils.py | 37 ++ fastplotlib/layouts/_figure.py | 16 +- fastplotlib/layouts/_plot_area.py | 70 +- fastplotlib/layouts/_subplot.py | 34 +- fastplotlib/widgets/histogram_lut.py | 159 +++-- fastplotlib/widgets/image.py | 2 +- 150 files changed, 1351 insertions(+), 451 deletions(-) create mode 100644 docs/source/api/fastplotlib.rst delete mode 100644 docs/source/api/gpu.rst create mode 100644 examples/desktop/screenshots/linear_selector.png create mode 100644 examples/desktop/selectors/README.rst create mode 100644 examples/desktop/selectors/linear_selector.py create mode 100644 fastplotlib/graphics/_axes.py create mode 100644 fastplotlib/graphics/utils.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9adb67f77..bc223ae27 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,6 +17,7 @@ jobs: docs-build: name: Docs runs-on: bigmem + timeout-minutes: 10 if: ${{ !github.event.pull_request.draft }} strategy: fail-fast: false @@ -49,6 +50,7 @@ jobs: test-build-full: name: Test Linux, notebook + glfw runs-on: bigmem + timeout-minutes: 10 if: ${{ !github.event.pull_request.draft }} strategy: fail-fast: false @@ -105,6 +107,7 @@ jobs: test-build-desktop: name: Test Linux, only glfw runs-on: bigmem + timeout-minutes: 10 if: ${{ !github.event.pull_request.draft }} strategy: fail-fast: false diff --git a/.github/workflows/screenshots.yml b/.github/workflows/screenshots.yml index baad8b655..c1ed81644 100644 --- a/.github/workflows/screenshots.yml +++ b/.github/workflows/screenshots.yml @@ -14,6 +14,7 @@ jobs: screenshots: name: Regenerate runs-on: bigmem + timeout-minutes: 10 if: ${{ !github.event.pull_request.draft }} steps: - name: Install git-lfs diff --git a/docs/source/api/fastplotlib.rst b/docs/source/api/fastplotlib.rst new file mode 100644 index 000000000..74349156f --- /dev/null +++ b/docs/source/api/fastplotlib.rst @@ -0,0 +1,14 @@ +fastplotlib +*********** + +.. currentmodule:: fastplotlib + +.. autofunction:: fastplotlib.pause_events + +.. autofunction:: fastplotlib.enumerate_adapters + +.. autofunction:: fastplotlib.select_adapter + +.. autofunction:: fastplotlib.print_wgpu_report + +.. autofunction:: fastplotlib.run diff --git a/docs/source/api/gpu.rst b/docs/source/api/gpu.rst deleted file mode 100644 index 6f94aff23..000000000 --- a/docs/source/api/gpu.rst +++ /dev/null @@ -1,6 +0,0 @@ -fastplotlib.utils.gpu -********************* - -.. currentmodule:: fastplotlib.utils.gpu -.. automodule:: fastplotlib - :members: diff --git a/docs/source/api/graphics/ImageGraphic.rst b/docs/source/api/graphics/ImageGraphic.rst index a0ae8a5ed..1f15c6963 100644 --- a/docs/source/api/graphics/ImageGraphic.rst +++ b/docs/source/api/graphics/ImageGraphic.rst @@ -20,6 +20,7 @@ Properties .. autosummary:: :toctree: ImageGraphic_api + ImageGraphic.axes ImageGraphic.block_events ImageGraphic.cmap ImageGraphic.cmap_interpolation @@ -41,6 +42,7 @@ Methods .. autosummary:: :toctree: ImageGraphic_api + ImageGraphic.add_axes ImageGraphic.add_event_handler ImageGraphic.add_linear_region_selector ImageGraphic.add_linear_selector diff --git a/docs/source/api/graphics/LineCollection.rst b/docs/source/api/graphics/LineCollection.rst index c000b7334..23e0b512d 100644 --- a/docs/source/api/graphics/LineCollection.rst +++ b/docs/source/api/graphics/LineCollection.rst @@ -20,6 +20,7 @@ Properties .. autosummary:: :toctree: LineCollection_api + LineCollection.axes LineCollection.block_events LineCollection.cmap LineCollection.colors @@ -45,6 +46,7 @@ Methods .. autosummary:: :toctree: LineCollection_api + LineCollection.add_axes LineCollection.add_event_handler LineCollection.add_graphic LineCollection.add_linear_region_selector diff --git a/docs/source/api/graphics/LineGraphic.rst b/docs/source/api/graphics/LineGraphic.rst index d260c3214..96c9ff62b 100644 --- a/docs/source/api/graphics/LineGraphic.rst +++ b/docs/source/api/graphics/LineGraphic.rst @@ -20,6 +20,7 @@ Properties .. autosummary:: :toctree: LineGraphic_api + LineGraphic.axes LineGraphic.block_events LineGraphic.cmap LineGraphic.colors @@ -39,6 +40,7 @@ Methods .. autosummary:: :toctree: LineGraphic_api + LineGraphic.add_axes LineGraphic.add_event_handler LineGraphic.add_linear_region_selector LineGraphic.add_linear_selector diff --git a/docs/source/api/graphics/LineStack.rst b/docs/source/api/graphics/LineStack.rst index 18b35932d..41cd3fbc8 100644 --- a/docs/source/api/graphics/LineStack.rst +++ b/docs/source/api/graphics/LineStack.rst @@ -20,6 +20,7 @@ Properties .. autosummary:: :toctree: LineStack_api + LineStack.axes LineStack.block_events LineStack.cmap LineStack.colors @@ -45,6 +46,7 @@ Methods .. autosummary:: :toctree: LineStack_api + LineStack.add_axes LineStack.add_event_handler LineStack.add_graphic LineStack.add_linear_region_selector diff --git a/docs/source/api/graphics/ScatterGraphic.rst b/docs/source/api/graphics/ScatterGraphic.rst index 8f2b17fd6..595346f07 100644 --- a/docs/source/api/graphics/ScatterGraphic.rst +++ b/docs/source/api/graphics/ScatterGraphic.rst @@ -20,6 +20,7 @@ Properties .. autosummary:: :toctree: ScatterGraphic_api + ScatterGraphic.axes ScatterGraphic.block_events ScatterGraphic.cmap ScatterGraphic.colors @@ -39,6 +40,7 @@ Methods .. autosummary:: :toctree: ScatterGraphic_api + ScatterGraphic.add_axes ScatterGraphic.add_event_handler ScatterGraphic.clear_event_handlers ScatterGraphic.remove_event_handler diff --git a/docs/source/api/graphics/TextGraphic.rst b/docs/source/api/graphics/TextGraphic.rst index a3cd9bbb9..107bc1c74 100644 --- a/docs/source/api/graphics/TextGraphic.rst +++ b/docs/source/api/graphics/TextGraphic.rst @@ -20,6 +20,7 @@ Properties .. autosummary:: :toctree: TextGraphic_api + TextGraphic.axes TextGraphic.block_events TextGraphic.deleted TextGraphic.event_handlers @@ -40,6 +41,7 @@ Methods .. autosummary:: :toctree: TextGraphic_api + TextGraphic.add_axes TextGraphic.add_event_handler TextGraphic.clear_event_handlers TextGraphic.remove_event_handler diff --git a/docs/source/api/layouts/subplot.rst b/docs/source/api/layouts/subplot.rst index dc77a725a..efe2fa4fc 100644 --- a/docs/source/api/layouts/subplot.rst +++ b/docs/source/api/layouts/subplot.rst @@ -20,6 +20,8 @@ Properties .. autosummary:: :toctree: Subplot_api + Subplot.axes + Subplot.background_color Subplot.camera Subplot.canvas Subplot.controller @@ -60,8 +62,6 @@ Methods Subplot.remove_animation Subplot.remove_graphic Subplot.render - Subplot.set_axes_visibility - Subplot.set_grid_visibility Subplot.set_title Subplot.set_viewport_rect diff --git a/docs/source/api/selectors/LinearRegionSelector.rst b/docs/source/api/selectors/LinearRegionSelector.rst index c9140bc7d..34df92b2a 100644 --- a/docs/source/api/selectors/LinearRegionSelector.rst +++ b/docs/source/api/selectors/LinearRegionSelector.rst @@ -20,6 +20,7 @@ Properties .. autosummary:: :toctree: LinearRegionSelector_api + LinearRegionSelector.axes LinearRegionSelector.axis LinearRegionSelector.block_events LinearRegionSelector.deleted @@ -39,6 +40,7 @@ Methods .. autosummary:: :toctree: LinearRegionSelector_api + LinearRegionSelector.add_axes LinearRegionSelector.add_event_handler LinearRegionSelector.add_ipywidget_handler LinearRegionSelector.clear_event_handlers diff --git a/docs/source/api/selectors/LinearSelector.rst b/docs/source/api/selectors/LinearSelector.rst index fa21f8f15..31f546e2c 100644 --- a/docs/source/api/selectors/LinearSelector.rst +++ b/docs/source/api/selectors/LinearSelector.rst @@ -20,6 +20,7 @@ Properties .. autosummary:: :toctree: LinearSelector_api + LinearSelector.axes LinearSelector.axis LinearSelector.block_events LinearSelector.deleted @@ -39,6 +40,7 @@ Methods .. autosummary:: :toctree: LinearSelector_api + LinearSelector.add_axes LinearSelector.add_event_handler LinearSelector.add_ipywidget_handler LinearSelector.clear_event_handlers diff --git a/docs/source/conf.py b/docs/source/conf.py index 0df47e579..68eb728a3 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -25,7 +25,7 @@ # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information project = "fastplotlib" -copyright = "2023, Kushal Kolar, Caitlin Lewis" +copyright = "2024, Kushal Kolar, Caitlin Lewis" author = "Kushal Kolar, Caitlin Lewis" release = fastplotlib.__version__ @@ -57,7 +57,8 @@ "../../examples/desktop/line_collection", "../../examples/desktop/scatter", "../../examples/desktop/heatmap", - "../../examples/desktop/misc" + "../../examples/desktop/misc", + "../../examples/desktop/selectors", ] ), "ignore_pattern": r'__init__\.py', @@ -102,6 +103,7 @@ "numpy": ("https://numpy.org/doc/stable/", None), "pygfx": ("https://pygfx.com/stable", None), "wgpu": ("https://wgpu-py.readthedocs.io/en/latest", None), + "fastplotlib": ("https://fastplotlib.readthedocs.io/en/latest/", None), } html_theme_options = { diff --git a/docs/source/index.rst b/docs/source/index.rst index 2b40cdeca..cf752a83b 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -11,7 +11,8 @@ Welcome to fastplotlib's documentation! .. toctree:: :maxdepth: 1 :caption: API - + + fastplotlib Figure Subplot Graphics @@ -19,7 +20,6 @@ Welcome to fastplotlib's documentation! Selectors Widgets Utils - GPU .. toctree:: :caption: Gallery diff --git a/examples/desktop/gridplot/gridplot.py b/examples/desktop/gridplot/gridplot.py index 044adae80..a77cb7872 100644 --- a/examples/desktop/gridplot/gridplot.py +++ b/examples/desktop/gridplot/gridplot.py @@ -11,7 +11,7 @@ import fastplotlib as fpl import imageio.v3 as iio -figure = fpl.Figure(shape=(2, 2)) +figure = fpl.Figure(shape=(2, 2), size=(700, 560)) im = iio.imread("imageio:clock.png") im2 = iio.imread("imageio:astronaut.png") @@ -25,10 +25,6 @@ figure.show() -figure.canvas.set_logical_size(700, 560) - -for subplot in figure: - subplot.auto_scale() # NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively # please see our docs for using fastplotlib interactively in ipython and jupyter diff --git a/examples/desktop/gridplot/gridplot_non_square.py b/examples/desktop/gridplot/gridplot_non_square.py index c8a68cc85..a7874319e 100644 --- a/examples/desktop/gridplot/gridplot_non_square.py +++ b/examples/desktop/gridplot/gridplot_non_square.py @@ -11,7 +11,7 @@ import fastplotlib as fpl import imageio.v3 as iio -figure = fpl.Figure(shape=(2, 2), controller_ids="sync") +figure = fpl.Figure(shape=(2, 2), size=(700, 560)) im = iio.imread("imageio:clock.png") im2 = iio.imread("imageio:astronaut.png") @@ -23,10 +23,6 @@ figure.show() -figure.canvas.set_logical_size(700, 560) - -for subplot in figure: - subplot.auto_scale() # NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively # please see our docs for using fastplotlib interactively in ipython and jupyter diff --git a/examples/desktop/gridplot/multigraphic_gridplot.py b/examples/desktop/gridplot/multigraphic_gridplot.py index edb0aaafd..eec0d06fa 100644 --- a/examples/desktop/gridplot/multigraphic_gridplot.py +++ b/examples/desktop/gridplot/multigraphic_gridplot.py @@ -14,7 +14,11 @@ from itertools import product # define figure -figure = fpl.Figure(shape=(2, 2), names=[["image-overlay", "circles"], ["line-stack", "scatter"]]) +figure = fpl.Figure( + shape=(2, 2), + names=[["image-overlay", "circles"], ["line-stack", "scatter"]], + size=(700, 560) +) img = iio.imread("imageio:coffee.png") @@ -106,8 +110,6 @@ def make_circle(center, radius: float, n_points: int = 75) -> np.ndarray: figure.show() -figure.canvas.set_logical_size(700, 560) - # NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": diff --git a/examples/desktop/heatmap/heatmap.py b/examples/desktop/heatmap/heatmap.py index 08b284749..008686464 100644 --- a/examples/desktop/heatmap/heatmap.py +++ b/examples/desktop/heatmap/heatmap.py @@ -10,22 +10,20 @@ import fastplotlib as fpl import numpy as np -figure = fpl.Figure() +figure = fpl.Figure(size=(700, 560)) -xs = np.linspace(0, 1_000, 10_000, dtype=np.float32) +xs = np.linspace(0, 1_000, 9_000, dtype=np.float32) sine = np.sin(np.sqrt(xs)) -data = np.vstack([sine * i for i in range(20_000)]) +data = np.vstack([sine * i for i in range(15_000)]) # plot the image data img = figure[0, 0].add_image(data=data, name="heatmap") +del data figure.show() -figure.canvas.set_logical_size(700, 560) - -figure[0, 0].auto_scale() # NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively # please see our docs for using fastplotlib interactively in ipython and jupyter diff --git a/examples/desktop/heatmap/heatmap_cmap.py b/examples/desktop/heatmap/heatmap_cmap.py index f51981bed..8791741a7 100644 --- a/examples/desktop/heatmap/heatmap_cmap.py +++ b/examples/desktop/heatmap/heatmap_cmap.py @@ -11,7 +11,7 @@ import fastplotlib as fpl import numpy as np -figure = fpl.Figure() +figure = fpl.Figure(size=(700, 560)) xs = np.linspace(0, 1_000, 10_000, dtype=np.float32) @@ -24,10 +24,6 @@ figure.show() -figure.canvas.set_logical_size(700, 560) - -figure[0, 0].auto_scale() - img.cmap = "viridis" # NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively diff --git a/examples/desktop/heatmap/heatmap_data.py b/examples/desktop/heatmap/heatmap_data.py index 9334ea4d7..f524f5476 100644 --- a/examples/desktop/heatmap/heatmap_data.py +++ b/examples/desktop/heatmap/heatmap_data.py @@ -10,7 +10,7 @@ import fastplotlib as fpl import numpy as np -figure = fpl.Figure() +figure = fpl.Figure(size=(700, 560)) xs = np.linspace(0, 1_000, 9_000, dtype=np.float32) @@ -23,9 +23,6 @@ figure.show() -figure.canvas.set_logical_size(700, 560) - -figure[0, 0].auto_scale() cosine = np.cos(np.sqrt(xs)[:3000]) # change first 2,000 rows and 3,000 columns diff --git a/examples/desktop/heatmap/heatmap_square.py b/examples/desktop/heatmap/heatmap_square.py index 51e71695a..aee4f7d44 100644 --- a/examples/desktop/heatmap/heatmap_square.py +++ b/examples/desktop/heatmap/heatmap_square.py @@ -11,7 +11,7 @@ import numpy as np -figure = fpl.Figure() +figure = fpl.Figure(size=(700, 560)) xs = np.linspace(0, 1_000, 20_000, dtype=np.float32) @@ -25,9 +25,6 @@ del data # data no longer needed after given to graphic figure.show() -figure.canvas.set_logical_size(1500, 1500) - -figure[0, 0].auto_scale() if __name__ == "__main__": print(__doc__) diff --git a/examples/desktop/heatmap/heatmap_vmin_vmax.py b/examples/desktop/heatmap/heatmap_vmin_vmax.py index 45c960fd8..e7f9c758b 100644 --- a/examples/desktop/heatmap/heatmap_vmin_vmax.py +++ b/examples/desktop/heatmap/heatmap_vmin_vmax.py @@ -10,7 +10,7 @@ import fastplotlib as fpl import numpy as np -figure = fpl.Figure() +figure = fpl.Figure(size=(700, 560)) xs = np.linspace(0, 1_000, 10_000, dtype=np.float32) @@ -23,10 +23,6 @@ figure.show() -figure.canvas.set_logical_size(700, 560) - -figure[0, 0].auto_scale() - img.vmin = -5_000 img.vmax = 10_000 diff --git a/examples/desktop/heatmap/heatmap_wide.py b/examples/desktop/heatmap/heatmap_wide.py index dccf531e2..6bf3ff72d 100644 --- a/examples/desktop/heatmap/heatmap_wide.py +++ b/examples/desktop/heatmap/heatmap_wide.py @@ -11,7 +11,7 @@ import numpy as np -figure = fpl.Figure() +figure = fpl.Figure(size=(700, 560)) xs = np.linspace(0, 1_000, 20_000, dtype=np.float32) @@ -24,9 +24,6 @@ figure.show() -figure.canvas.set_logical_size(1500, 1500) - -figure[0, 0].auto_scale() if __name__ == "__main__": print(__doc__) diff --git a/examples/desktop/image/image_cmap.py b/examples/desktop/image/image_cmap.py index c70af7346..4aad934b2 100644 --- a/examples/desktop/image/image_cmap.py +++ b/examples/desktop/image/image_cmap.py @@ -13,17 +13,13 @@ im = iio.imread("imageio:camera.png") -figure = fpl.Figure() +figure = fpl.Figure(size=(700, 560)) # plot the image data image_graphic = figure[0, 0].add_image(data=im, name="random-image") figure.show() -figure.canvas.set_logical_size(700, 560) - -figure[0, 0].auto_scale() - image_graphic.cmap = "viridis" # NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively diff --git a/examples/desktop/image/image_rgb.py b/examples/desktop/image/image_rgb.py index 951142fd7..e89f3d192 100644 --- a/examples/desktop/image/image_rgb.py +++ b/examples/desktop/image/image_rgb.py @@ -13,16 +13,13 @@ im = iio.imread("imageio:astronaut.png") -figure = fpl.Figure() +figure = fpl.Figure(size=(700, 560)) # plot the image data image_graphic = figure[0, 0].add_image(data=im, name="iio astronaut") figure.show() -figure.canvas.set_logical_size(700, 560) - -figure[0, 0].auto_scale() # NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively # please see our docs for using fastplotlib interactively in ipython and jupyter diff --git a/examples/desktop/image/image_rgbvminvmax.py b/examples/desktop/image/image_rgbvminvmax.py index 25d3904e8..2263f1307 100644 --- a/examples/desktop/image/image_rgbvminvmax.py +++ b/examples/desktop/image/image_rgbvminvmax.py @@ -13,17 +13,13 @@ im = iio.imread("imageio:astronaut.png") -figure = fpl.Figure() +figure = fpl.Figure(size=(700, 560)) # plot the image data image_graphic = figure[0, 0].add_image(data=im, name="iio astronaut") figure.show() -figure.canvas.set_logical_size(700, 560) - -figure[0, 0].auto_scale() - image_graphic.vmin = 0.5 image_graphic.vmax = 0.75 diff --git a/examples/desktop/image/image_simple.py b/examples/desktop/image/image_simple.py index dab5188a1..cec8e3313 100644 --- a/examples/desktop/image/image_simple.py +++ b/examples/desktop/image/image_simple.py @@ -11,7 +11,7 @@ import fastplotlib as fpl import imageio.v3 as iio -figure = fpl.Figure() +figure = fpl.Figure(size=(700, 560)) data = iio.imread("imageio:camera.png") @@ -20,9 +20,6 @@ figure.show() -figure.canvas.set_logical_size(700, 560) - -figure[0, 0].auto_scale() # NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively # please see our docs for using fastplotlib interactively in ipython and jupyter diff --git a/examples/desktop/image/image_small.py b/examples/desktop/image/image_small.py index 95c263a28..937411ab1 100644 --- a/examples/desktop/image/image_small.py +++ b/examples/desktop/image/image_small.py @@ -12,7 +12,7 @@ import fastplotlib as fpl -figure = fpl.Figure() +figure = fpl.Figure(size=(700, 560)) data = np.array( [[0, 1, 2], @@ -22,9 +22,6 @@ figure.show() -figure.canvas.set_logical_size(700, 560) - -figure[0, 0].auto_scale() # NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively # please see our docs for using fastplotlib interactively in ipython and jupyter diff --git a/examples/desktop/image/image_vminvmax.py b/examples/desktop/image/image_vminvmax.py index d9e49b18e..0503c5ff2 100644 --- a/examples/desktop/image/image_vminvmax.py +++ b/examples/desktop/image/image_vminvmax.py @@ -11,7 +11,7 @@ import fastplotlib as fpl import imageio.v3 as iio -figure = fpl.Figure() +figure = fpl.Figure(size=(700, 560)) data = iio.imread("imageio:astronaut.png") @@ -20,10 +20,6 @@ figure.show() -figure.canvas.set_logical_size(700, 560) - -figure[0, 0].auto_scale() - image_graphic.vmin = 0.5 image_graphic.vmax = 0.75 diff --git a/examples/desktop/image/image_widget.py b/examples/desktop/image/image_widget.py index de1d27de1..131e02bd7 100644 --- a/examples/desktop/image/image_widget.py +++ b/examples/desktop/image/image_widget.py @@ -6,15 +6,17 @@ When run in a notebook, or with the Qt GUI backend, sliders are also shown. """ -# sphinx_gallery_pygfx_docs = 'hidden' +# sphinx_gallery_pygfx_docs = 'screenshot' import fastplotlib as fpl import imageio.v3 as iio # not a fastplotlib dependency, only used for examples a = iio.imread("imageio:camera.png") -iw = fpl.ImageWidget(data=a, cmap="viridis") +iw = fpl.ImageWidget(data=a, cmap="viridis", figure_kwargs={"size": (700, 560)}) iw.show() +figure = iw.figure + # NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": diff --git a/examples/desktop/line/line.py b/examples/desktop/line/line.py index cd661da1e..eb1afbe60 100644 --- a/examples/desktop/line/line.py +++ b/examples/desktop/line/line.py @@ -11,7 +11,7 @@ import fastplotlib as fpl import numpy as np -figure = fpl.Figure() +figure = fpl.Figure(size=(700, 560)) xs = np.linspace(-10, 10, 100) # sine wave @@ -36,11 +36,9 @@ colors = ["r"] * 25 + ["purple"] * 25 + ["y"] * 25 + ["b"] * 25 sinc_graphic = figure[0, 0].add_line(data=sinc, thickness=5, colors=colors) +figure[0, 0].axes.grids.xy.visible = True figure.show() -figure.canvas.set_logical_size(700, 560) - -figure[0, 0].auto_scale() # NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively # please see our docs for using fastplotlib interactively in ipython and jupyter diff --git a/examples/desktop/line/line_cmap.py b/examples/desktop/line/line_cmap.py index 5ffea6fef..b7dfe4424 100644 --- a/examples/desktop/line/line_cmap.py +++ b/examples/desktop/line/line_cmap.py @@ -11,7 +11,7 @@ import fastplotlib as fpl import numpy as np -figure = fpl.Figure() +figure = fpl.Figure(size=(700, 560)) xs = np.linspace(-10, 10, 100) # sine wave @@ -41,7 +41,6 @@ figure.show() -figure.canvas.set_logical_size(700, 560) # NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively # please see our docs for using fastplotlib interactively in ipython and jupyter diff --git a/examples/desktop/line/line_colorslice.py b/examples/desktop/line/line_colorslice.py index 3d18d74b7..0b71efc3d 100644 --- a/examples/desktop/line/line_colorslice.py +++ b/examples/desktop/line/line_colorslice.py @@ -11,7 +11,7 @@ import fastplotlib as fpl import numpy as np -figure = fpl.Figure() +figure = fpl.Figure(size=(700, 560)) xs = np.linspace(-10, 10, 100) # sine wave @@ -82,9 +82,6 @@ zeros_graphic.cmap[50:75] = "jet" zeros_graphic.cmap[75:] = "viridis" -figure.canvas.set_logical_size(700, 560) - -figure[0, 0].auto_scale() # NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively # please see our docs for using fastplotlib interactively in ipython and jupyter diff --git a/examples/desktop/line/line_dataslice.py b/examples/desktop/line/line_dataslice.py index eac765c68..83a9ae34a 100644 --- a/examples/desktop/line/line_dataslice.py +++ b/examples/desktop/line/line_dataslice.py @@ -11,7 +11,7 @@ import fastplotlib as fpl import numpy as np -figure = fpl.Figure() +figure = fpl.Figure(size=(700, 560)) xs = np.linspace(-10, 10, 100) # sine wave @@ -46,9 +46,6 @@ bool_key = [True, True, True, False, False] * 20 sinc_graphic.data[bool_key, 1] = 7 # y vals to 1 -figure.canvas.set_logical_size(700, 560) - -figure[0, 0].auto_scale() # NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively # please see our docs for using fastplotlib interactively in ipython and jupyter diff --git a/examples/desktop/line_collection/line_collection.py b/examples/desktop/line_collection/line_collection.py index 44b765319..67f3834d3 100644 --- a/examples/desktop/line_collection/line_collection.py +++ b/examples/desktop/line_collection/line_collection.py @@ -29,13 +29,15 @@ def make_circle(center, radius: float, n_points: int = 75) -> np.ndarray: pos_xy = np.vstack(circles) -figure = fpl.Figure() +figure = fpl.Figure(size=(700, 560)) figure[0, 0].add_line_collection(circles, cmap="jet", thickness=5) +# remove clutter +figure[0, 0].axes.visible = False + figure.show() -figure.canvas.set_logical_size(700, 560) # NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively # please see our docs for using fastplotlib interactively in ipython and jupyter diff --git a/examples/desktop/line_collection/line_collection_cmap_values.py b/examples/desktop/line_collection/line_collection_cmap_values.py index e94a161ad..e0b6f2507 100644 --- a/examples/desktop/line_collection/line_collection_cmap_values.py +++ b/examples/desktop/line_collection/line_collection_cmap_values.py @@ -34,15 +34,17 @@ def make_circle(center, radius: float, n_points: int = 75) -> np.ndarray: # highest values, lowest values, mid-high values, mid values cmap_values = [10] * 4 + [0] * 4 + [7] * 4 + [5] * 4 -figure = fpl.Figure() +figure = fpl.Figure(size=(700, 560)) figure[0, 0].add_line_collection( circles, cmap="bwr", cmap_transform=cmap_values, thickness=10 ) +# remove clutter +figure[0, 0].axes.visible = False + figure.show() -figure.canvas.set_logical_size(700, 560) # NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively # please see our docs for using fastplotlib interactively in ipython and jupyter diff --git a/examples/desktop/line_collection/line_collection_cmap_values_qualitative.py b/examples/desktop/line_collection/line_collection_cmap_values_qualitative.py index 5f9ea0000..bbb463c2f 100644 --- a/examples/desktop/line_collection/line_collection_cmap_values_qualitative.py +++ b/examples/desktop/line_collection/line_collection_cmap_values_qualitative.py @@ -12,6 +12,7 @@ import numpy as np import fastplotlib as fpl + def make_circle(center, radius: float, n_points: int = 75) -> np.ndarray: theta = np.linspace(0, 2 * np.pi, n_points) xs = radius * np.sin(theta) @@ -40,7 +41,7 @@ def make_circle(center, radius: float, n_points: int = 75) -> np.ndarray: 1, 1, 1, 5 ] -figure = fpl.Figure() +figure = fpl.Figure(size=(700, 560)) figure[0, 0].add_line_collection( circles, @@ -49,9 +50,11 @@ def make_circle(center, radius: float, n_points: int = 75) -> np.ndarray: thickness=10 ) +# remove clutter +figure[0, 0].axes.visible = False + figure.show() -figure.canvas.set_logical_size(700, 560) # NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively # please see our docs for using fastplotlib interactively in ipython and jupyter diff --git a/examples/desktop/line_collection/line_collection_colors.py b/examples/desktop/line_collection/line_collection_colors.py index bf3e818cd..23ca25b25 100644 --- a/examples/desktop/line_collection/line_collection_colors.py +++ b/examples/desktop/line_collection/line_collection_colors.py @@ -33,13 +33,15 @@ def make_circle(center, radius: float, n_points: int = 75) -> np.ndarray: # this will produce 16 circles so we will define 16 colors colors = ["blue"] * 4 + ["red"] * 4 + ["yellow"] * 4 + ["w"] * 4 -figure = fpl.Figure() +figure = fpl.Figure(size=(700, 560)) figure[0, 0].add_line_collection(circles, colors=colors, thickness=10) +# remove clutter +figure[0, 0].axes.visible = False + figure.show() -figure.canvas.set_logical_size(700, 560) # NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively # please see our docs for using fastplotlib interactively in ipython and jupyter diff --git a/examples/desktop/line_collection/line_collection_slicing.py b/examples/desktop/line_collection/line_collection_slicing.py index a7525f7ba..fbeab53c2 100644 --- a/examples/desktop/line_collection/line_collection_slicing.py +++ b/examples/desktop/line_collection/line_collection_slicing.py @@ -20,12 +20,12 @@ multi_data = np.stack([data] * 15) -figure = fpl.Figure() +figure = fpl.Figure(size=(700, 560)) lines = figure[0, 0].add_line_stack( multi_data, thickness=[2, 10, 2, 5, 5, 5, 8, 8, 8, 9, 3, 3, 3, 4, 4], - separation=1, + separation=4, metadatas=list(range(15)), # some metadata names=list("abcdefghijklmno"), # unique name for each line ) @@ -63,7 +63,15 @@ figure.show(maintain_aspect=False) -figure.canvas.set_logical_size(700, 580) +# individual y axis for each line +for line in lines: + line.add_axes() + line.axes.x.visible = False + line.axes.update_using_bbox(line.world_object.get_world_bounding_box()) + +# no y axis in subplot +figure[0, 0].axes.y.visible = False + if __name__ == "__main__": print(__doc__) diff --git a/examples/desktop/line_collection/line_stack.py b/examples/desktop/line_collection/line_stack.py index e7f7125e1..9ca2a937e 100644 --- a/examples/desktop/line_collection/line_stack.py +++ b/examples/desktop/line_collection/line_stack.py @@ -19,7 +19,7 @@ data = np.column_stack([xs, ys]) multi_data = np.stack([data] * 10) -figure = fpl.Figure() +figure = fpl.Figure(size=(700, 560)) line_stack = figure[0, 0].add_line_stack( multi_data, # shape: (10, 100, 2), i.e. [n_lines, n_points, xy] @@ -30,7 +30,6 @@ figure.show(maintain_aspect=False) -figure.canvas.set_logical_size(700, 560) # NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively # please see our docs for using fastplotlib interactively in ipython and jupyter diff --git a/examples/desktop/line_collection/line_stack_3d.py b/examples/desktop/line_collection/line_stack_3d.py index 314a97ff2..46a24ef75 100644 --- a/examples/desktop/line_collection/line_stack_3d.py +++ b/examples/desktop/line_collection/line_stack_3d.py @@ -21,7 +21,10 @@ multi_data = np.stack([data] * 10) # create figure to plot lines and use an orbit controller in 3D -figure = fpl.Figure(cameras="3d", controller_types="orbit") +figure = fpl.Figure(cameras="3d", controller_types="orbit", size=(700, 560)) + +# make grid invisible to remove clutter +figure[0, 0].axes.grids.visible = False line_stack = figure[0, 0].add_line_stack( multi_data, # shape: (10, 100, 2), i.e. [n_lines, n_points, xy] @@ -88,7 +91,7 @@ def animate_colors(subplot): "fov": 50.0, "width": 32, "height": 20, - "zoom": 1, + "zoom": 0.7, "maintain_aspect": True, "depth_range": None, } @@ -97,7 +100,6 @@ def animate_colors(subplot): figure[0, 0].camera.set_state(camera_state) -figure.canvas.set_logical_size(700, 560) # NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively # please see our docs for using fastplotlib interactively in ipython and jupyter diff --git a/examples/desktop/misc/cycle_animation.py b/examples/desktop/misc/cycle_animation.py index bb402a1f7..f866434a1 100644 --- a/examples/desktop/misc/cycle_animation.py +++ b/examples/desktop/misc/cycle_animation.py @@ -34,7 +34,7 @@ colors = ["yellow"] * n_points + ["cyan"] * n_points + ["magenta"] * n_points # create plot -figure = fpl.Figure() +figure = fpl.Figure(size=(700, 560)) subplot_scatter = figure[0, 0] # use an alpha value since this will be a lot of points scatter_graphic = subplot_scatter.add_scatter(data=cloud, sizes=3, colors=colors, alpha=0.6) @@ -53,10 +53,9 @@ def cycle_colors(subplot): figure.show() -subplot_scatter.canvas.set_logical_size(700, 560) # NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() \ No newline at end of file + fpl.run() diff --git a/examples/desktop/misc/em_wave_animation.py b/examples/desktop/misc/em_wave_animation.py index 50ab27ed6..bfccedf5f 100644 --- a/examples/desktop/misc/em_wave_animation.py +++ b/examples/desktop/misc/em_wave_animation.py @@ -6,7 +6,7 @@ """ # test_example = false -# sphinx_gallery_pygfx_docs = 'animate' +# sphinx_gallery_pygfx_docs = 'animate 8s' import fastplotlib as fpl import numpy as np @@ -14,7 +14,7 @@ figure = fpl.Figure( cameras="3d", controller_types="orbit", - size=(700, 400) + size=(700, 560) ) start, stop = 0, 4 * np.pi @@ -47,10 +47,6 @@ # it is the z-offset for where to place the *graphic*, by default with Orthographic cameras (i.e. 2D views) # it will increment by 1 for each line in the collection, we want to disable this so set z_position=0 -# axes are a WIP, just draw a white line along z for now -z_axis = np.array([[0, 0, 0], [0, 0, stop]]) -figure[0, 0].add_line(z_axis, colors="w", thickness=1) - # just a pre-saved camera state state = { 'position': np.array([-8.0 , 6.0, -2.0]), @@ -68,13 +64,15 @@ figure[0, 0].camera.set_state(state) +# make all grids except xz plane invisible to remove clutter +figure[0, 0].axes.grids.xz.visible = True + figure.show() figure[0, 0].camera.zoom = 1.5 increment = np.pi * 4 / 100 -figure.canvas.set_logical_size(700, 560) # moves the wave one step along the z-axis def tick(subplot): @@ -84,22 +82,34 @@ def tick(subplot): # just change the x-axis vals for the electric field subplot["e"].data[:, 0] = new_data + subplot["e"].data[:, 2] = new_zs # and y-axis vals for magnetic field subplot["m"].data[:, 1] = new_data + subplot["m"].data[:, 2] = new_zs # update the vector lines - for i, (value, z) in enumerate(zip(new_data[::10], zs[::10])): + for i, (value, z) in enumerate(zip(new_data[::10], new_zs[::10])): subplot["e-vec"].graphics[i].data = np.array([[0, 0, z], [value, 0, z]]) subplot["m-vec"].graphics[i].data = np.array([[0, 0, z], [0, value, z]]) + # update axes and center scene + subplot.axes.z.start_value = start + subplot.axes.z.update(subplot.camera, subplot.viewport.logical_size) + subplot.center_scene() + start += increment stop += increment +figure[0, 0].axes.x.visible = False +figure[0, 0].axes.y.visible = False +figure[0, 0].axes.auto_grid = False + figure[0, 0].add_animations(tick) +print(figure[0, 0]._fpl_graphics_scene.children) # NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() \ No newline at end of file + fpl.run() diff --git a/examples/desktop/misc/image_animation.py b/examples/desktop/misc/image_animation.py index df84f3c5a..8c323f464 100644 --- a/examples/desktop/misc/image_animation.py +++ b/examples/desktop/misc/image_animation.py @@ -13,7 +13,7 @@ data = np.random.rand(512, 512) -figure = fpl.Figure() +figure = fpl.Figure(size=(700, 560)) # plot the image data image_graphic = figure[0, 0].add_image(data=data, name="random-image") @@ -29,7 +29,6 @@ def update_data(figure_instance): figure.show() -figure.canvas.set_logical_size(700, 560) # NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively # please see our docs for using fastplotlib interactively in ipython and jupyter diff --git a/examples/desktop/misc/line3d_animation.py b/examples/desktop/misc/line3d_animation.py index 27d22c78a..4f2f089e6 100644 --- a/examples/desktop/misc/line3d_animation.py +++ b/examples/desktop/misc/line3d_animation.py @@ -6,7 +6,7 @@ """ # test_example = false -# sphinx_gallery_pygfx_docs = 'animate 5s' +# sphinx_gallery_pygfx_docs = 'animate 8s' import numpy as np import fastplotlib as fpl @@ -21,7 +21,7 @@ # make data 3d, with shape [, 3] spiral = np.dstack([xs, ys, zs])[0] -figure = fpl.Figure(cameras="3d") +figure = fpl.Figure(cameras="3d", size=(700, 560)) line_graphic = figure[0,0].add_line(data=spiral, thickness=3, cmap='jet') @@ -46,11 +46,13 @@ def move_marker(): # add `move_marker` to the animations figure.add_animations(move_marker) -figure.show() +# remove clutter +figure[0, 0].axes.grids.xy.visible = True +figure[0, 0].axes.grids.xz.visible = True + -figure.canvas.set_logical_size(700, 560) +figure.show() -figure[0,0].auto_scale(maintain_aspect=False) # NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively # please see our docs for using fastplotlib interactively in ipython and jupyter diff --git a/examples/desktop/misc/line_animation.py b/examples/desktop/misc/line_animation.py index 50faad5c7..a602a6e7d 100644 --- a/examples/desktop/misc/line_animation.py +++ b/examples/desktop/misc/line_animation.py @@ -19,7 +19,7 @@ xs = np.linspace(start, stop, 100) ys = np.sin(xs) -figure = fpl.Figure() +figure = fpl.Figure(size=(700, 560)) # plot the image data sine = figure[0, 0].add_line(ys, name="sine", colors="r") @@ -40,14 +40,11 @@ def update_line(subplot): figure[0, 0].add_animations(update_line) -figure.show() +figure.show(maintain_aspect=False) -figure.canvas.set_logical_size(700, 560) - -figure[0,0].auto_scale(maintain_aspect=False) # NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() \ No newline at end of file + fpl.run() diff --git a/examples/desktop/misc/multiplot_animation.py b/examples/desktop/misc/multiplot_animation.py index a712ce9ef..b0a942d0a 100644 --- a/examples/desktop/misc/multiplot_animation.py +++ b/examples/desktop/misc/multiplot_animation.py @@ -2,7 +2,7 @@ Multi-Subplot Image Update ========================== -Example showing updating a single plot with new random 512x512 data. +Example showing updating a multiple subplots with new random 512x512 data. """ # test_example = false @@ -12,7 +12,7 @@ import numpy as np # Figure of shape 2 x 3 with all controllers synced -figure = fpl.Figure(shape=(2, 3), controller_ids="sync") +figure = fpl.Figure(shape=(2, 3), controller_ids="sync", size=(700, 560)) # Make a random image graphic for each subplot for subplot in figure: @@ -40,10 +40,9 @@ def update_data(f): # show the gridplot figure.show() -figure.canvas.set_logical_size(700, 560) # NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() \ No newline at end of file + fpl.run() diff --git a/examples/desktop/misc/scatter_animation.py b/examples/desktop/misc/scatter_animation.py index aa1495dd9..de57292a5 100644 --- a/examples/desktop/misc/scatter_animation.py +++ b/examples/desktop/misc/scatter_animation.py @@ -34,7 +34,7 @@ colors = ["yellow"] * n_points + ["cyan"] * n_points + ["magenta"] * n_points # create plot -figure = fpl.Figure() +figure = fpl.Figure(size=(700, 560)) subplot_scatter = figure[0, 0] # use an alpha value since this will be a lot of points scatter_graphic = subplot_scatter.add_scatter(data=cloud, sizes=3, colors=colors, alpha=0.6) @@ -50,10 +50,9 @@ def update_points(subplot): figure.show() -subplot_scatter.canvas.set_logical_size(700, 560) # NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() \ No newline at end of file + fpl.run() diff --git a/examples/desktop/misc/simple_event.py b/examples/desktop/misc/simple_event.py index b6d408862..574b8ea5e 100644 --- a/examples/desktop/misc/simple_event.py +++ b/examples/desktop/misc/simple_event.py @@ -14,7 +14,7 @@ data = iio.imread("imageio:camera.png") # Create a figure -figure = fpl.Figure() +figure = fpl.Figure(size=(700, 560)) # plot sine wave, use a single color image_graphic = figure[0,0].add_image(data=data) @@ -47,8 +47,6 @@ def click_event(event_data): print(xy) -figure.canvas.set_logical_size(700, 560) - # NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": diff --git a/examples/desktop/scatter/scatter.py b/examples/desktop/scatter/scatter.py index 05dd7a99b..fe1f6ce6d 100644 --- a/examples/desktop/scatter/scatter.py +++ b/examples/desktop/scatter/scatter.py @@ -11,7 +11,7 @@ import fastplotlib as fpl import numpy as np -figure = fpl.Figure() +figure = fpl.Figure(size=(700, 560)) # create a random distribution of 10,000 xyz coordinates n_points = 5_000 @@ -35,17 +35,11 @@ # color each of them separately colors = ["yellow"] * n_points + ["cyan"] * n_points + ["magenta"] * n_points -# create plot -figure = fpl.Figure() - # use an alpha value since this will be a lot of points figure[0,0].add_scatter(data=cloud, sizes=3, colors=colors, alpha=0.6) figure.show() -figure.canvas.set_logical_size(700, 560) - -figure[0, 0].auto_scale() # NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively # please see our docs for using fastplotlib interactively in ipython and jupyter diff --git a/examples/desktop/scatter/scatter_cmap.py b/examples/desktop/scatter/scatter_cmap.py index 0adf72509..42ff572d8 100644 --- a/examples/desktop/scatter/scatter_cmap.py +++ b/examples/desktop/scatter/scatter_cmap.py @@ -11,7 +11,7 @@ import fastplotlib as fpl import numpy as np -figure = fpl.Figure() +figure = fpl.Figure(size=(700, 560)) # create a random distribution of 10,000 xyz coordinates n_points = 5_000 @@ -42,9 +42,6 @@ figure[0,0].graphics[0].cmap = "viridis" -figure.canvas.set_logical_size(700, 560) - -figure[0, 0].auto_scale() # NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively # please see our docs for using fastplotlib interactively in ipython and jupyter diff --git a/examples/desktop/scatter/scatter_cmap_iris.py b/examples/desktop/scatter/scatter_cmap_iris.py index 700f5c136..b25369c60 100644 --- a/examples/desktop/scatter/scatter_cmap_iris.py +++ b/examples/desktop/scatter/scatter_cmap_iris.py @@ -13,7 +13,7 @@ from sklearn import datasets -figure = fpl.Figure() +figure = fpl.Figure(size=(700, 560)) data = datasets.load_iris()["data"] @@ -30,10 +30,6 @@ figure.show() -figure.canvas.set_logical_size(700, 560) - -figure[0, 0].auto_scale() - scatter_graphic.cmap = "tab10" diff --git a/examples/desktop/scatter/scatter_colorslice.py b/examples/desktop/scatter/scatter_colorslice.py index 3d3a3fa26..839df3826 100644 --- a/examples/desktop/scatter/scatter_colorslice.py +++ b/examples/desktop/scatter/scatter_colorslice.py @@ -11,7 +11,7 @@ import fastplotlib as fpl import numpy as np -figure = fpl.Figure() +figure = fpl.Figure(size=(700, 560)) # create a random distribution of 10,000 xyz coordinates n_points = 5_000 @@ -35,20 +35,13 @@ # color each of them separately colors = ["yellow"] * n_points + ["cyan"] * n_points + ["magenta"] * n_points -# create plot -figure = fpl.Figure() - # use an alpha value since this will be a lot of points figure[0,0].add_scatter(data=cloud, sizes=3, colors=colors, alpha=0.6) figure.show() -figure.canvas.set_logical_size(700, 560) - scatter_graphic = figure[0, 0].graphics[0] -figure[0, 0].auto_scale() - scatter_graphic.colors[0:75] = "red" scatter_graphic.colors[75:150] = "white" scatter_graphic.colors[::2] = "blue" diff --git a/examples/desktop/scatter/scatter_colorslice_iris.py b/examples/desktop/scatter/scatter_colorslice_iris.py index a1e6d5318..92df1f66c 100644 --- a/examples/desktop/scatter/scatter_colorslice_iris.py +++ b/examples/desktop/scatter/scatter_colorslice_iris.py @@ -12,7 +12,7 @@ from sklearn import datasets -figure = fpl.Figure() +figure = fpl.Figure(size=(700, 560)) data = datasets.load_iris()["data"] @@ -28,10 +28,6 @@ figure.show() -figure.canvas.set_logical_size(700, 560) - -figure[0, 0].auto_scale() - scatter_graphic.colors[0:75] = "red" scatter_graphic.colors[75:150] = "white" scatter_graphic.colors[::2] = "blue" diff --git a/examples/desktop/scatter/scatter_dataslice.py b/examples/desktop/scatter/scatter_dataslice.py index af2fffebd..715959e06 100644 --- a/examples/desktop/scatter/scatter_dataslice.py +++ b/examples/desktop/scatter/scatter_dataslice.py @@ -12,7 +12,7 @@ import numpy as np -figure = fpl.Figure() +figure = fpl.Figure(size=(700, 560)) # create a gaussian cloud of 5_000 points n_points = 1_000 @@ -23,19 +23,12 @@ gaussian_cloud = np.random.multivariate_normal(mean, covariance, n_points) gaussian_cloud2 = np.random.multivariate_normal(mean, covariance, n_points) -# create plot -figure = fpl.Figure() - # use an alpha value since this will be a lot of points scatter1 = figure[0,0].add_scatter(data=gaussian_cloud, sizes=3) scatter2 = figure[0,0].add_scatter(data=gaussian_cloud2, colors="r", sizes=3) figure.show() -figure.canvas.set_logical_size(700, 560) - -figure[0, 0].auto_scale() - scatter1.data[:500] = np.array([0 , 0, 0]) scatter2.data[500:] = np.array([0 , 0, 0]) diff --git a/examples/desktop/scatter/scatter_dataslice_iris.py b/examples/desktop/scatter/scatter_dataslice_iris.py index 0d47c6efd..04ac4b85f 100644 --- a/examples/desktop/scatter/scatter_dataslice_iris.py +++ b/examples/desktop/scatter/scatter_dataslice_iris.py @@ -13,7 +13,7 @@ from sklearn import datasets -figure = fpl.Figure() +figure = fpl.Figure(size=(700, 560)) data = datasets.load_iris()["data"] @@ -24,10 +24,6 @@ figure.show() -figure.canvas.set_logical_size(700, 560) - -figure[0, 0].auto_scale() - scatter_graphic.data[0] = np.array([[5, 3, 1.5]]) scatter_graphic.data[1] = np.array([[4.3, 3.2, 1.3]]) scatter_graphic.data[2] = np.array([[5.2, 2.7, 1.7]]) diff --git a/examples/desktop/scatter/scatter_iris.py b/examples/desktop/scatter/scatter_iris.py index c16a4b135..6937ffe4b 100644 --- a/examples/desktop/scatter/scatter_iris.py +++ b/examples/desktop/scatter/scatter_iris.py @@ -13,7 +13,7 @@ from pathlib import Path import sys -figure = fpl.Figure() +figure = fpl.Figure(size=(700, 560)) current_file = Path(sys.argv[0]).resolve() @@ -27,12 +27,9 @@ figure.show() -figure.canvas.set_logical_size(700, 560) - -figure[0, 0].auto_scale() # NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() \ No newline at end of file + fpl.run() diff --git a/examples/desktop/scatter/scatter_size.py b/examples/desktop/scatter/scatter_size.py index bd4e2db2b..0cecb6dad 100644 --- a/examples/desktop/scatter/scatter_size.py +++ b/examples/desktop/scatter/scatter_size.py @@ -18,7 +18,7 @@ names = [["scalar_size"], ["array_size"]] # Create the grid plot -figure = fpl.Figure(shape=shape, names=names, size=(1000, 1000)) +figure = fpl.Figure(shape=shape, names=names, size=(700, 560)) # get y_values using sin function angles = np.arange(0, 20 * np.pi + 0.001, np.pi / 20) @@ -39,7 +39,6 @@ figure.show() -figure.canvas.set_logical_size(700, 560) # NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively # please see our docs for using fastplotlib interactively in ipython and jupyter diff --git a/examples/desktop/screenshots/gridplot.png b/examples/desktop/screenshots/gridplot.png index 315958673..99ba70155 100644 --- a/examples/desktop/screenshots/gridplot.png +++ b/examples/desktop/screenshots/gridplot.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d43e6972bf76aa2de400616bde4275cd05d3a945475742ec7f63f7658628292b -size 264437 +oid sha256:a0da6067ecd930fb0add52124dfd97f7d73b27ab7696df681c75e333c749975a +size 328971 diff --git a/examples/desktop/screenshots/gridplot_non_square.png b/examples/desktop/screenshots/gridplot_non_square.png index 689585b40..6db1c3f2a 100644 --- a/examples/desktop/screenshots/gridplot_non_square.png +++ b/examples/desktop/screenshots/gridplot_non_square.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:703285790dc96500a7a376f6e78953c943643f4ecf3102182072c2bd0bf8190c -size 173753 +oid sha256:2763431048efa1642a276bc3e659ed93a2f787ff6db700bcd29acc619d542f3f +size 236206 diff --git a/examples/desktop/screenshots/heatmap.png b/examples/desktop/screenshots/heatmap.png index 0514daf94..a8f91765e 100644 --- a/examples/desktop/screenshots/heatmap.png +++ b/examples/desktop/screenshots/heatmap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:03b3ab1fc8aa602eb94beed1f5fa5712452ee802bb3230c4fd066d073bdd4ad2 -size 40100 +oid sha256:d40c5e47f686dc498f003684efeefc16e6962d6ce1e2edc4c2cd8537b3ff3387 +size 82267 diff --git a/examples/desktop/screenshots/image_cmap.png b/examples/desktop/screenshots/image_cmap.png index 91124db6a..837d6765f 100644 --- a/examples/desktop/screenshots/image_cmap.png +++ b/examples/desktop/screenshots/image_cmap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f18a55da8cede25dbb77b18e8cf374d158a66b823d029714983218e55ee68249 -size 187688 +oid sha256:95ed35b1ab7d5e56ff81e883d5c56419ddede3481f1a0c77f5af01dba83d03ea +size 236774 diff --git a/examples/desktop/screenshots/image_rgb.png b/examples/desktop/screenshots/image_rgb.png index 8ae39eaad..2ca946c15 100644 --- a/examples/desktop/screenshots/image_rgb.png +++ b/examples/desktop/screenshots/image_rgb.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3851bea9ee908a460750b40a0a5709aff1b28afa6adf11c9ad2ed8239958caa4 -size 216343 +oid sha256:86e421deb8e013f25737b9a752409890ba14f794a1a01fbed728d474490292bb +size 269316 diff --git a/examples/desktop/screenshots/image_rgbvminvmax.png b/examples/desktop/screenshots/image_rgbvminvmax.png index 478ce40fe..c31263344 100644 --- a/examples/desktop/screenshots/image_rgbvminvmax.png +++ b/examples/desktop/screenshots/image_rgbvminvmax.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2ec8ddd362197ba802f8381d5baea226dc30689eee5e5dc744c2da710f0b3482 -size 33860 +oid sha256:fc5983f07d840320bf6866896d221845f59eecedbc6d89a7a0bc5dd1f6472c7b +size 49999 diff --git a/examples/desktop/screenshots/image_simple.png b/examples/desktop/screenshots/image_simple.png index c60293498..194e5afe4 100644 --- a/examples/desktop/screenshots/image_simple.png +++ b/examples/desktop/screenshots/image_simple.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:216791f48cee8ddb9979ecc8b7b7435c0fe22c2734148c25314f1827a5c9ad66 -size 187868 +oid sha256:ec0770ff5671a9f83f43f8ece18e45b74137244ff578b8035eace3fd98291595 +size 237699 diff --git a/examples/desktop/screenshots/image_small.png b/examples/desktop/screenshots/image_small.png index cda3a2584..5ed8f615d 100644 --- a/examples/desktop/screenshots/image_small.png +++ b/examples/desktop/screenshots/image_small.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3f2af0ed16ec82842ad9d45d5a8b6189e77a2f2f8adb21dd82bc1636979cd2c7 -size 2325 +oid sha256:3818b137bcfce829ea6a8670ca52a20122b2489f536ca5ff38e0ed6288043113 +size 12824 diff --git a/examples/desktop/screenshots/image_vminvmax.png b/examples/desktop/screenshots/image_vminvmax.png index 478ce40fe..c31263344 100644 --- a/examples/desktop/screenshots/image_vminvmax.png +++ b/examples/desktop/screenshots/image_vminvmax.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2ec8ddd362197ba802f8381d5baea226dc30689eee5e5dc744c2da710f0b3482 -size 33860 +oid sha256:fc5983f07d840320bf6866896d221845f59eecedbc6d89a7a0bc5dd1f6472c7b +size 49999 diff --git a/examples/desktop/screenshots/line.png b/examples/desktop/screenshots/line.png index 605540225..3cf15db2d 100644 --- a/examples/desktop/screenshots/line.png +++ b/examples/desktop/screenshots/line.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d7f3736d4464cfd942e87d21be1a18d09f5d0d239a7e1c7679e918dcc5c9331c -size 26701 +oid sha256:e0ea3004cc871f54d1f12f6e5a39afbda568748ca907468a0533268949c67916 +size 173435 diff --git a/examples/desktop/screenshots/line_cmap.png b/examples/desktop/screenshots/line_cmap.png index cab91220f..6ec5a4998 100644 --- a/examples/desktop/screenshots/line_cmap.png +++ b/examples/desktop/screenshots/line_cmap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1f154346cffbaa0957a9986d8b7beef417b66ef0cec7dbed3c20780d91425567 -size 29231 +oid sha256:cbf54efd9999593043c48a53f189c675ef6544a962c44297ce76df4fbe75ad42 +size 47804 diff --git a/examples/desktop/screenshots/line_collection.png b/examples/desktop/screenshots/line_collection.png index f3fb5052b..ffe8cc96e 100644 --- a/examples/desktop/screenshots/line_collection.png +++ b/examples/desktop/screenshots/line_collection.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ca08ce57a1cf57c334add1c41351f3b823f06ad8da463017d0815cf7cfea03b3 -size 91085 +oid sha256:b373c63989b4d3d3c9b5ea1607ef1602fa7d45753cdc0895a6e6d1d4a2c5420b +size 106504 diff --git a/examples/desktop/screenshots/line_collection_cmap_values.png b/examples/desktop/screenshots/line_collection_cmap_values.png index 33af5b917..66d36dec3 100644 --- a/examples/desktop/screenshots/line_collection_cmap_values.png +++ b/examples/desktop/screenshots/line_collection_cmap_values.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:12ddca084dc83478c6b3d263f11f456f8b81e7a8a291d6b9024dbcecbfb049c0 -size 57107 +oid sha256:dff530c128132f26aded7c2ad9e202cc98e7486fbad84146a9055b6514c99453 +size 67561 diff --git a/examples/desktop/screenshots/line_collection_cmap_values_qualitative.png b/examples/desktop/screenshots/line_collection_cmap_values_qualitative.png index 57f45605b..b144dbdcb 100644 --- a/examples/desktop/screenshots/line_collection_cmap_values_qualitative.png +++ b/examples/desktop/screenshots/line_collection_cmap_values_qualitative.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:74d5999cdd0b992f73bafb1bd74c318fd9cf058aed232068ab7dcb76d86df556 -size 60881 +oid sha256:ce6e25567214539b296248a4dc665552f47687cda03d412f715db7f72138c341 +size 69992 diff --git a/examples/desktop/screenshots/line_collection_colors.png b/examples/desktop/screenshots/line_collection_colors.png index 9c27854ed..90948c126 100644 --- a/examples/desktop/screenshots/line_collection_colors.png +++ b/examples/desktop/screenshots/line_collection_colors.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a152331c51ed5440c5faf2a59439d90832521fbb1498d9635ddae088219ca353 -size 46941 +oid sha256:9aeb3ef27fd7a393b4884749e7988e8cde3906c9f19b573e51bd78bf31fc7a45 +size 60514 diff --git a/examples/desktop/screenshots/line_collection_slicing.png b/examples/desktop/screenshots/line_collection_slicing.png index 1145e84dc..26933c5cc 100644 --- a/examples/desktop/screenshots/line_collection_slicing.png +++ b/examples/desktop/screenshots/line_collection_slicing.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bdfdc2b2c5799e814ef5a1e32748a2a6d2dd88005f6fa0d9c456b8dadfada5db -size 124609 +oid sha256:beb5193965530c490324edeb253ed429237e44289c5239079743a71d2aece797 +size 132171 diff --git a/examples/desktop/screenshots/line_colorslice.png b/examples/desktop/screenshots/line_colorslice.png index 825ce8e3f..34ff56c4f 100644 --- a/examples/desktop/screenshots/line_colorslice.png +++ b/examples/desktop/screenshots/line_colorslice.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:de5a56c96a062ed0ec154ae21f3a3a67087e0c8aef6d8e4681c67a016424144a -size 31971 +oid sha256:c8afbeb5a79192eb1805c7c8478b26f6aabc534f3ac58fc7190f108ebb8640fe +size 56462 diff --git a/examples/desktop/screenshots/line_dataslice.png b/examples/desktop/screenshots/line_dataslice.png index 71c3d1918..c135997bb 100644 --- a/examples/desktop/screenshots/line_dataslice.png +++ b/examples/desktop/screenshots/line_dataslice.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e4dece6f721068a1ae37c6830110f97df64ea57c467ef4d7f42b73575d2ee476 -size 43995 +oid sha256:e6c5c4ef3aaeca5597c11e5db3764599c8c41b191c692db5fda54f525d8079da +size 68033 diff --git a/examples/desktop/screenshots/line_stack.png b/examples/desktop/screenshots/line_stack.png index 026b1f61e..ea5a3a330 100644 --- a/examples/desktop/screenshots/line_stack.png +++ b/examples/desktop/screenshots/line_stack.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1384f1030e81fc05b24db040ac47a3bd62663358dcbdd0e77b3d675d5edd4357 -size 86938 +oid sha256:cdb26c1460583f8f605ffe6751c926c0e84463b10d68343169660593b82a9078 +size 130495 diff --git a/examples/desktop/screenshots/linear_selector.png b/examples/desktop/screenshots/linear_selector.png new file mode 100644 index 000000000..2db42319d --- /dev/null +++ b/examples/desktop/screenshots/linear_selector.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:09f60f24e702dd6b17ba525604c1a04f23682eb08c8c2100d45a34b2626bebc6 +size 153115 diff --git a/examples/desktop/screenshots/scatter_cmap_iris.png b/examples/desktop/screenshots/scatter_cmap_iris.png index 2a6ae7016..96acbec6c 100644 --- a/examples/desktop/screenshots/scatter_cmap_iris.png +++ b/examples/desktop/screenshots/scatter_cmap_iris.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b781b387476028a5eaf2083c40d57306afbcbc2a6754dce6fb66cf71ddd689d1 -size 31719 +oid sha256:79f7d22b575c3a68dfdcd4bf806f79f1896a784ecbb6a2d3ba01da5731fa78dd +size 59731 diff --git a/examples/desktop/screenshots/scatter_colorslice_iris.png b/examples/desktop/screenshots/scatter_colorslice_iris.png index 45c5d940c..73fcddebf 100644 --- a/examples/desktop/screenshots/scatter_colorslice_iris.png +++ b/examples/desktop/screenshots/scatter_colorslice_iris.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:68f93c08d361232c9be2220a68db8659c9c3c81c3cdb4e1a1ce9b366fb28b4f5 -size 13215 +oid sha256:3c778cf9c51c9636d4f4ff13e4a1c841795a4dba327eb7118de2a0fb60c7e3f3 +size 35810 diff --git a/examples/desktop/screenshots/scatter_dataslice_iris.png b/examples/desktop/screenshots/scatter_dataslice_iris.png index 1121d032c..32f797c67 100644 --- a/examples/desktop/screenshots/scatter_dataslice_iris.png +++ b/examples/desktop/screenshots/scatter_dataslice_iris.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5d662e151062a136a17dac1f8693ba13f41daac05e91e32ee9c7053715f9ee17 -size 14437 +oid sha256:444f0bd81459a4977df2eb9aa5645c0f7745fce97baa0c9e39c254bd32cdb1e6 +size 38351 diff --git a/examples/desktop/screenshots/scatter_iris.png b/examples/desktop/screenshots/scatter_iris.png index 7d107d964..dc53d97b0 100644 --- a/examples/desktop/screenshots/scatter_iris.png +++ b/examples/desktop/screenshots/scatter_iris.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4fc88e52cc4ede6d1453746461da645f8b3df0a3099155caf639768a5ad4424c -size 14148 +oid sha256:153db7a803709978a1a997d7c94db37ebc0504ec9a7eebce80977d4c90d48f61 +size 37365 diff --git a/examples/desktop/screenshots/scatter_size.png b/examples/desktop/screenshots/scatter_size.png index 66b31cab9..74c1b6e56 100644 --- a/examples/desktop/screenshots/scatter_size.png +++ b/examples/desktop/screenshots/scatter_size.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9d1eeb96dc1f52c4d48889a8b00387387cccb7b83d479c1c4b47789b281a1cd5 -size 34222 +oid sha256:381877c06882f40a8b46bbe07e1e1ca41a74ff9cda84544cca4ee92a4b522cda +size 62476 diff --git a/examples/desktop/selectors/README.rst b/examples/desktop/selectors/README.rst new file mode 100644 index 000000000..0f7e412a7 --- /dev/null +++ b/examples/desktop/selectors/README.rst @@ -0,0 +1,2 @@ +Selection Tools +=============== \ No newline at end of file diff --git a/examples/desktop/selectors/linear_selector.py b/examples/desktop/selectors/linear_selector.py new file mode 100644 index 000000000..b224c197f --- /dev/null +++ b/examples/desktop/selectors/linear_selector.py @@ -0,0 +1,157 @@ +""" +Linear Selectors +================ + +Example showing how to use a `LinearSelector` with lines, line collections, and images +""" + +# test_example = false +# sphinx_gallery_pygfx_docs = 'screenshot' + +import fastplotlib as fpl +import numpy as np + +# create some data +xs = np.linspace(0, 10 * np.pi, 100) +sine = np.column_stack([xs, np.sin(xs)]) +cosine = np.column_stack([xs, np.cos(xs)]) + +# a varying sine-cosine quilted pattern +image_xs, image_ys = np.meshgrid(xs, xs) +multiplier = np.linspace(0, 10, 100) +image_data = multiplier * np.sin(image_xs) + multiplier * np.cos(image_ys) + +# create a figure +figure = fpl.Figure( + shape=(2, 2), + size=(700, 560) +) + +# line of a single sine wave from 0 - 10π +line = figure[0, 0].add_line(sine, cmap="jet") + +# add a linear selector to the line +line_selector = line.add_linear_selector() + +line_selector_text = (f"x value: {line_selector.selection / np.pi:.2f}π\n" + f"y value: {line.data[0, 1]:.2f}\n" + f"index: {line_selector.get_selected_index()}") + +# a label that will change to display line data based on the linear selector +line_selection_label = figure[0, 0].add_text( + line_selector_text, + offset=(0., 1.75, 0.), + anchor="middle-left", + font_size=22, + face_color=line.colors[0], + outline_color="w", + outline_thickness=0.1, +) + + +# add an event handler using a decorator, selectors are just like other graphics +@line_selector.add_event_handler("selection") +def line_selector_changed(ev): + selection = ev.info["value"] + index = ev.get_selected_index() + + # set text to display selection data + line_selection_label.text = \ + (f"x value: {selection / np.pi:.2f}π\n" + f"y value: {line.data[index, 1]:.2f}\n" + f"index: {index}") + + # set text color based on line color at selection index + line_selection_label.face_color = line.colors[index] + + +# line stack, sine and cosine wave +line_stack = figure[0, 1].add_line_stack([sine, cosine], colors=["magenta", "cyan"], separation=1) +line_stack_selector = line_stack.add_linear_selector() + +line_stack_selector_text = (f"x value: {line_stack_selector.selection / np.pi:.2f}π\n" + f"index: {line_selector.get_selected_index()}\n" + f"sine y value: {line_stack[0].data[0, 1]:.2f}\n" + f"cosine y value: {line_stack[1].data[0, 1]:.2f}\n") + +# a label that will change to display line_stack data based on the linear selector +line_stack_selector_label = figure[0, 1].add_text( + line_stack_selector_text, + offset=(0., 7.0, 0.), + anchor="middle-left", + font_size=18, + face_color="w", +) + + +# add an event handler using a decorator +@line_stack_selector.add_event_handler("selection") +def line_stack_selector_changed(ev): + selection = ev.info["value"] + + # a linear selectors one a line collection returns a + # list of selected indices for each graphic in the collection + index = ev.get_selected_index()[0] + + # set text to display selection data + line_stack_selector_label.text = \ + (f"x value: {selection / np.pi:.2f}π\n" + f"index: {index}\n" + f"sine y value: {line_stack[0].data[index, 1]:.2f}\n" + f"cosine y value: {line_stack[1].data[index, 1]:.2f}\n") + + +# create an image +image = figure[1, 0].add_image(image_data) + +# add a row selector +image_row_selector = image.add_linear_selector(axis="y") + +# add column selector +image_col_selector = image.add_linear_selector() + +# make a line to indicate row data +line_image_row = figure[1, 1].add_line(image.data[0]) + +# make a line to indicate column data +line_image_col_data = np.column_stack([image.data[:, 0], np.arange(100)]) +line_image_col = figure[1, 1].add_line(line_image_col_data) + + +# callbacks to change the line data in subplot [1, 1] +# to display selected row and selected column data +def image_row_selector_changed(ev): + ix = ev.get_selected_index() + new_data = image.data[ix] + # set y values of line + line_image_row.data[:, 1] = new_data + + +def image_col_selector_changed(ev): + ix = ev.get_selected_index() + new_data = image.data[:, ix] + # set x values of line + line_image_col.data[:, 0] = new_data + + +# add event handlers, you can also use a decorator +image_row_selector.add_event_handler(image_row_selector_changed, "selection") +image_col_selector.add_event_handler(image_col_selector_changed, "selection") + +figure.show(maintain_aspect=False) + +# some axes and camera zoom settings +for subplot in [figure[0, 0], figure[0, 1]]: + subplot.axes.auto_grid = False + subplot.axes.grids.xy.major_step = (np.pi, 1) + subplot.axes.grids.xy.minor_step = (0, 0) + subplot.camera.zoom = 0.6 + +figure[1, 1].camera.zoom = 0.5 + + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.run() \ No newline at end of file diff --git a/examples/notebooks/image_widget_test.ipynb b/examples/notebooks/image_widget_test.ipynb index 321f7b84f..aaf41f3e3 100644 --- a/examples/notebooks/image_widget_test.ipynb +++ b/examples/notebooks/image_widget_test.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "id": "07019035-83f2-4753-9e7c-628ae439b441", "metadata": { "tags": [] @@ -18,14 +18,12 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "id": "10b8ab40-944d-472c-9b7e-cae8a129e7ce", "metadata": {}, "outputs": [], "source": [ - "from nb_test_utils import plot_test, notebook_finished\n", - "import nb_test_utils\n", - "nb_test_utils.TOLERANCE = 0.035" + "from nb_test_utils import plot_test, notebook_finished" ] }, { @@ -450,7 +448,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": null, "id": "ed783360-992d-40f8-bb6f-152a59edff43", "metadata": {}, "outputs": [], @@ -465,7 +463,7 @@ " rgb=[False, True],\n", " histogram_widget=True,\n", " cmap=\"gnuplot2\", \n", - " figure_kwargs = {\"controller_ids\": None},\n", + " figure_kwargs={\"controller_ids\": None, \"size\": (900, 400)},\n", ")\n", "\n", "iw_mixed_shapes.show()" @@ -473,7 +471,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": null, "id": "274c67b4-aa07-4fcf-a094-1b1e70d0378a", "metadata": {}, "outputs": [], @@ -481,11 +479,11 @@ "iw_mixed_shapes.sliders[\"t\"].value = 50\n", "plot_test(\"image-widget-zfish-mixed-rgb-cockatoo-frame-50\", iw_mixed_shapes.figure)\n", "\n", - "#Set the data, changing the first array and also the size of the \"T\" slider\n", + "# Set the data, changing the first array and also the size of the \"T\" slider\n", "iw_mixed_shapes.set_data([zfish_frame_2, movie[:200, :, :, :]], reset_indices=True)\n", "plot_test(\"image-widget-zfish-mixed-rgb-cockatoo-set-data\", iw_mixed_shapes.figure)\n", "\n", - "#Check how a window function might work on the RGB data\n", + "# Check how a window function might work on the RGB data\n", "iw_mixed_shapes.window_funcs = {\"t\": (np.mean, 4)}\n", "iw_mixed_shapes.sliders[\"t\"].value = 20\n", "plot_test(\"image-widget-zfish-mixed-rgb-cockatoo-windowrgb\", iw_mixed_shapes.figure)" @@ -518,7 +516,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.8" + "version": "3.11.3" } }, "nbformat": 4, diff --git a/examples/notebooks/lineplot.ipynb b/examples/notebooks/lineplot.ipynb index 85ebb60f5..e700c866a 100644 --- a/examples/notebooks/lineplot.ipynb +++ b/examples/notebooks/lineplot.ipynb @@ -69,11 +69,6 @@ "for i, subplot in enumerate(fig):\n", " # create and add the LineGraphic\n", " line_graphic = subplot.add_line(data=spiral, thickness=3, cmap='jet')\n", - " \n", - " # make axes visible\n", - " subplot.set_axes_visibility(True)\n", - " subplot.set_grid_visibility(True)\n", - " \n", " marker = subplot.add_scatter(data=spiral[0], sizes=10, name=\"marker\")\n", " \n", "marker_index = 0\n", @@ -121,7 +116,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.3" + "version": "3.11.2" } }, "nbformat": 4, diff --git a/examples/notebooks/nb_test_utils.py b/examples/notebooks/nb_test_utils.py index 791640fe2..3d9e50d34 100644 --- a/examples/notebooks/nb_test_utils.py +++ b/examples/notebooks/nb_test_utils.py @@ -16,7 +16,7 @@ os.makedirs(SCREENSHOTS_DIR, exist_ok=True) os.makedirs(DIFFS_DIR, exist_ok=True) -TOLERANCE = 0.025 +TOLERANCE = 0.05 # store all the failures to allow the nb to proceed to test other examples FAILURES = list() diff --git a/examples/notebooks/scatter.ipynb b/examples/notebooks/scatter.ipynb index b78521064..0389b462b 100644 --- a/examples/notebooks/scatter.ipynb +++ b/examples/notebooks/scatter.ipynb @@ -92,9 +92,6 @@ "\n", "for subplot in fig:\n", " subplot.add_scatter(data=cloud, colors=colors, alpha=0.7, sizes=5)\n", - " \n", - " subplot.set_axes_visibility(True)\n", - " subplot.set_grid_visibility(True)\n", "\n", "\n", "fig.show()" @@ -185,7 +182,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.3" + "version": "3.11.2" } }, "nbformat": 4, diff --git a/examples/notebooks/screenshots/nb-astronaut.png b/examples/notebooks/screenshots/nb-astronaut.png index 9c28b6cfa..70c1a95a7 100644 --- a/examples/notebooks/screenshots/nb-astronaut.png +++ b/examples/notebooks/screenshots/nb-astronaut.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:afb405dfcd90d9165b4be8c2b79a82b45964debb119d25851835b8a6e2f18785 -size 111986 +oid sha256:c1491279a44125be3fc51678a2662b0632d8618a7425b7894677a7eba919eae9 +size 84735 diff --git a/examples/notebooks/screenshots/nb-astronaut_RGB.png b/examples/notebooks/screenshots/nb-astronaut_RGB.png index 1939c12d7..0443de1c4 100644 --- a/examples/notebooks/screenshots/nb-astronaut_RGB.png +++ b/examples/notebooks/screenshots/nb-astronaut_RGB.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2f86ef886266279ace4672904860bdaeee49dd23498998c8f68ae0b36cecc529 -size 110588 +oid sha256:716e19f1f9d16443602de327716daee8663731e1afccfa4a9b16c68ffd3b0c11 +size 76074 diff --git a/examples/notebooks/screenshots/nb-camera.png b/examples/notebooks/screenshots/nb-camera.png index cfdf2673e..e71803ade 100644 --- a/examples/notebooks/screenshots/nb-camera.png +++ b/examples/notebooks/screenshots/nb-camera.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:124e52fdb8c200be3295f79331f25a51d423d159a7f8cde1863daa00e54c0894 -size 77665 +oid sha256:b84ffb87948cfd523941041a3c9c6827ccac51bb5648faddd810d15a4bd0912c +size 52034 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-set_data.png b/examples/notebooks/screenshots/nb-image-widget-movie-set_data.png index e49ad3c38..e8e74e817 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-set_data.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-set_data.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5acd7eeccbf47af45aa8306befb040f9b53d21f1727e7366b536d73261b407ce -size 43494 +oid sha256:d9421323aac16e9e8d3489332b7db7b2381effc4b10a132e2c58dc86544720ae +size 45797 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-0-reset.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-0-reset.png index dfcb98736..4fce1c96a 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-0-reset.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-0-reset.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9ca702fffc4eebea5ba31b77062b60f848c2e5d689568d16b39a62561a0b8b73 -size 134201 +oid sha256:949885c0eab52bbb5293aa74ded4d3dedfd5172d1217934fa8963b7c74f176e8 +size 118713 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-0.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-0.png index dfcb98736..ffb80c4ec 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-0.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9ca702fffc4eebea5ba31b77062b60f848c2e5d689568d16b39a62561a0b8b73 -size 134201 +oid sha256:f05522f502bc848086c01ba86f17036b310617e8bfb239d83505ef31c2ad23a7 +size 106685 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-279.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-279.png index 787e2757e..0063b3fa2 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-279.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-279.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:73bdd6a91ab679dcf237626bc7d3edd267d402ea8de2b6e2c3db7bba9b9418ac -size 169211 +oid sha256:9cb358df1f9dcb67f26818cad619c0740f25602cdbb634b737112d6d43c89fc8 +size 142265 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-max-33.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-max-33.png index ca2357ddd..9c48d5258 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-max-33.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-max-33.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:afb9c5bfbfbc2ce800d613f779021b0a93d098f415d89157f994cc9b1632361b -size 149454 +oid sha256:62c303c87a6fbc2f2874b817ca0d938b8a6f83042e81a659d9feb7d7fe7442a6 +size 127805 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-13.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-13.png index ac3f4cb61..388a280e1 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c3c07d75cd4673e411d814c1dab1e62d6543b26c89f208eed15ccb941bbe3ab2 -size 124795 +oid sha256:b22f9823bab849de025b256558f008efdfadcb181c66510f32293d2fea53c6f0 +size 110339 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-33.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-33.png index 3a77efced..1d0802226 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-33.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-33.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5f39d68bbc2c7d52cc13609ff60274dbfe49bea4d4a03cfbf1d1c15cf7fb8e8c -size 114013 +oid sha256:5baf57418ed6f36278715187250ac69307cd88eb4232df586a3c929ffbc40d4b +size 102774 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-reset.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-reset.png index e34f9deb3..6534b9907 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-reset.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-reset.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2825af49b1964fb76dcf2ccd494bb61623df4d5fffad7be30cf389b9b7e6d4bf -size 146186 +oid sha256:e009147472683c8d207a23d7c64575465f936ee48250dfa9fe15654ed7d34403 +size 126018 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50.png index e34f9deb3..6534b9907 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2825af49b1964fb76dcf2ccd494bb61623df4d5fffad7be30cf389b9b7e6d4bf -size 146186 +oid sha256:e009147472683c8d207a23d7c64575465f936ee48250dfa9fe15654ed7d34403 +size 126018 diff --git a/examples/notebooks/screenshots/nb-image-widget-single-gnuplot2.png b/examples/notebooks/screenshots/nb-image-widget-single-gnuplot2.png index 4cd3248a0..f157e63c2 100644 --- a/examples/notebooks/screenshots/nb-image-widget-single-gnuplot2.png +++ b/examples/notebooks/screenshots/nb-image-widget-single-gnuplot2.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aff55757a29cac06c1c158599681e8c10e27fd772425c6b3137a06d5d604f95e -size 435106 +oid sha256:4d77e42683f74dbd311aa56e5c66b4bb90449e5e52a5a9d4ae3a04cf774ca4df +size 306329 diff --git a/examples/notebooks/screenshots/nb-image-widget-single.png b/examples/notebooks/screenshots/nb-image-widget-single.png index dd37a74db..c262e74ce 100644 --- a/examples/notebooks/screenshots/nb-image-widget-single.png +++ b/examples/notebooks/screenshots/nb-image-widget-single.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1e70812decf8d1c591b1d97c24346159255e8b5cba5722f9c4d67c5b5aa92a8a -size 403368 +oid sha256:a0f6a4eea4dcf0100b6cdd89892fb02bc2d2c5396445ef0694a0810b9d4465e8 +size 274170 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-gaussian.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-gaussian.png index 9be76e5bd..a78761846 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-gaussian.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-gaussian.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8d6b97c351f51ee8b0429e7001ba16cb3862c9cfc4f4e0f0227524b8c20d5906 -size 157300 +oid sha256:de65879e7ad15cd85740c989d62bd27e4e2fdbe61de3585238caaa52b554fa95 +size 129651 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-reset.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-reset.png index c877ac887..f5989caa9 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-reset.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-reset.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d74649c5ca7b0401a8e42ffe9b73cebeebdce80953c4790f44a99bfe6624902b -size 71618 +oid sha256:828e3e104d21f0cc743f16b72152642aa276d5727258b4454a3f5dcc6514ac7e +size 81188 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-max-window-13.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-max-window-13.png index 7613ae2a9..3e3cdc025 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-max-window-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-max-window-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e9c99c189dbfffbc3fa24fb6f48015518a2e1c3e681191abb45cf4e29185dcff -size 196855 +oid sha256:e615d9dbcbc09d14eb1ab2aea14c609e996e0f96bafdf3c4513acd0613260509 +size 205824 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-13.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-13.png index e803cdc68..22fe4e54d 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:916800ae449d12e875f14be3d13a75db85339524dbd594f9963074b9fc5316ae -size 177769 +oid sha256:d81c351726c3eb1cbef662b28454747e6074374bdd2203d0e71835c6611cda11 +size 151657 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-5.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-5.png index 5b5ef1009..e6a877eec 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-5.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3006a07bfbf6276967ca55809788f61a1852db9851f959cc1db00016a9b3747f -size 140019 +oid sha256:71676028d9a29fb4dbb4e3aaa3dd9173dff585fe08300a5d8884e6d8e413952e +size 131857 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50.png index 4e8803a7b..023cb947c 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3e55ffde023955d00804a7272a4e963b4d2a74b74fb401962d32c1a29d76bc24 -size 80880 +oid sha256:dc841d0a37938319d177ecd74662de486c4fe2bc8be35de456ad7a3abd4ca329 +size 90997 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-99.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-99.png index 061195a98..c1fa94056 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-99.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-99.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:405495c384aa52d6e3c8a65237876682f4be62967dce1b6af526e4d069fa44d3 -size 62621 +oid sha256:42a51e116e1455fcea00dd9ea9f387633df31e185706cd0fd18e8101947718be +size 74817 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-gaussian.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-gaussian.png index 0da3abb21..f79d956b0 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-gaussian.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-gaussian.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7b30ef1dca9711bd72eb28f0035552f93e143a683f818c3f2aec8df4323306e4 -size 178459 +oid sha256:f9900ac2df273c48d163538c6cbda2d4b43245bbcc55f759a50883aaf51cf876 +size 160362 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-reset.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-reset.png index 21ea17c27..572e1c2a7 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-reset.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-reset.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b3e8fc84f5ea2d5a93bc02e19965781fbe9ec697b660430a5203cb1c91803974 -size 142748 +oid sha256:35d948baddc0c069e65d63d04fb3f22276dd936deee281fdf0bf31c2203c0e01 +size 156432 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-max-window-13.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-max-window-13.png index ece0fee5f..8f083da9b 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-max-window-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-max-window-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7b01f2385991f4941f35d1b913fe54c72cbe42c62522ab181ddb2466b2f2be8d -size 372324 +oid sha256:371727d51121efa1f6250f9aebdb1a3847e2ef79cf1a9137d5c07b8738114b9b +size 307668 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-13.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-13.png index 93dd3b254..e59f9020f 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4bac6aedfebab2bf97497dbecd17f59b36cb78b27dcdb1547c6d78f902d5f89b -size 213579 +oid sha256:d179a1279dcd12c10495e3022fdf8ae3b5ee8ed50ca0ba512f0dbd6b1fd325f8 +size 184162 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-5.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-5.png index b6392f095..3d133063f 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-5.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5458f9488a19207c7d4f8a971de06a028dfb22e4a2847c3a0b1e1f45c41109f0 -size 200566 +oid sha256:dae80d58e60532eb151354de65574b70a73bc6ef8dcaba8c65d39da6cc388eda +size 184497 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50.png index 8165824cb..e79a20bbd 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8588b720e7d970a0c5d0b9e43c68ee0695d7ced8c51797d50143b0737d3ae2c1 -size 160340 +oid sha256:ba9a962dfdc0bcfd033dff87e970554b3d480226c904a3fb2428c427312c7e42 +size 176697 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-99.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-99.png index f46e58b4f..9f8791bcb 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-99.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-99.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b86bc324f13ca3a958d0db80251874478e0191b0c30c301f3022913e7b1f62d5 -size 147084 +oid sha256:b3cbdc194f63da1e33a7e367175e93e49bf5f69c988bb8ac03c137bd1505adc5 +size 166434 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-init-mean-window-5.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-init-mean-window-5.png index 8e3e7e2de..fcd0b1382 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-init-mean-window-5.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-init-mean-window-5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9993fe8f8d3e6d6e48d863b251fdd7b37926ba7b97b2d70683cbc3ab45910c99 -size 184668 +oid sha256:051f4e6dc5a6a9824da12165bf6475baf83886ca97c6ba10c0ea2f615dc4e0ee +size 162378 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-false.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-false.png index aae5c9066..9d45ca1aa 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-false.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-false.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f4cdb28c8aa72b1cd968f4f78f3c2413d2338b6a2b5c200df02ecdd2bce1568b -size 126337 +oid sha256:6adc3973283da26ad475351af58d91892251c53fe0e09f714cf21cfdab7502c6 +size 140885 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-true.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-true.png index 346495cfc..190025d6d 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-true.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-true.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:19000f2cc6d78e2cc18dd5213778e595ee6710ca3fcd71cb4cbe6286b42b1e8b -size 130255 +oid sha256:bab10f413eaac26d69848ada9528fa1e36e616eab37c5244d7f1c9c3ab85e7d6 +size 143505 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-init-mean-window-5.png b/examples/notebooks/screenshots/nb-image-widget-zfish-init-mean-window-5.png index 2298f904e..e97c2ffd0 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-init-mean-window-5.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-init-mean-window-5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4a141cd3e0d3647accb18c55d84026d16ca2280611b80682737a61151dd9c377 -size 99397 +oid sha256:10042f15d067049e05418f1e82eb3428f56f53a2c159ac4eaa135d42dfc3d057 +size 88268 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-frame-50.png b/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-frame-50.png index 58f4fd87e..de9822952 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-frame-50.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-frame-50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cbd3cb8399c32cc611a86bb482782bfe55393ec73f2c2a3f4eb0d4e8af2442d6 -size 58842 +oid sha256:1e031e6712bb7a9601f627e32347c05ed2669363ee1ffe428d10797081c32ef0 +size 113064 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-set-data.png b/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-set-data.png index 0eff22834..2e47302a8 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-set-data.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-set-data.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e6e201ecce9db938796d1fc710a154ae8bc49e0a7e1f51d9af586f29f4ee63de -size 57116 +oid sha256:0aaa7782c20f209e07a7259d676b4fc993d4f25ba1a52150d5512d8ef16b82bc +size 130999 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-windowrgb.png b/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-windowrgb.png index 03a1fc30c..9104fb9ea 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-windowrgb.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-windowrgb.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:608c9a0b1466886652299887a4f0f16a77dfb400fc46200a453df25c5a0e7016 -size 55903 +oid sha256:5b478e4cd25c96e2c08b3f595193d019a0cfcac69f8ea3e3a8330cf6c0ffabbf +size 131188 diff --git a/examples/notebooks/screenshots/nb-lines-3d.png b/examples/notebooks/screenshots/nb-lines-3d.png index d1e46a618..65310e7f1 100644 --- a/examples/notebooks/screenshots/nb-lines-3d.png +++ b/examples/notebooks/screenshots/nb-lines-3d.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:91f74b1ad6d4eeb08da8a33bfccfc0e9e80d48fc33b2a783cb94890f3c603a94 -size 14131 +oid sha256:fc7a8caabb59ff2f2fd9811678b974542c6a3dddfd0d005109777264056d458a +size 23430 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-jet-values-cosine.png b/examples/notebooks/screenshots/nb-lines-cmap-jet-values-cosine.png index db1a0e658..9f3a156b9 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-jet-values-cosine.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-jet-values-cosine.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:58af931da3307204f2699b2ac04d8546b93aa0b4d3c058ab6d181656fd79fae8 -size 11674 +oid sha256:80d318cb1daf701e682e600c0058cf3e5c336062dd459618eac60f92ec2399ad +size 17362 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-jet-values.png b/examples/notebooks/screenshots/nb-lines-cmap-jet-values.png index 9bb734365..677906685 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-jet-values.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-jet-values.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9949949767455061caa08b96dfdf0948d511d604d39ded4a028a9a50deca9797 -size 12990 +oid sha256:996d29cdf767f3863615ebc0d5b91f4ec07af898350c07b2fd78426c239cb452 +size 18817 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-jet.png b/examples/notebooks/screenshots/nb-lines-cmap-jet.png index 10f9252f3..5195c617d 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-jet.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-jet.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c04746bb9c6e168644981e808b83b878d5d72e2101f441979765c74bb36c087a -size 10979 +oid sha256:74da9cc1bac480b5d72693ea7e074d5192e382a989816912757bd307a1e04faf +size 17367 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-tab-10.png b/examples/notebooks/screenshots/nb-lines-cmap-tab-10.png index a769ff769..d766bcda0 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-tab-10.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-tab-10.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:704cddf180de18dfc02cccced26dc57a7d8bff3938ceaf5ca9b6db7ccaed5928 -size 9582 +oid sha256:04052da5609011c7df22e4688bfe1100ad90fa757099d7b315d0b2bcaeb8c3d0 +size 15876 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-viridis-values.png b/examples/notebooks/screenshots/nb-lines-cmap-viridis-values.png index 861efcef5..723beb580 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-viridis-values.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-viridis-values.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:67310ed0deb418bf0d6d10e1184e902f928f0e914518b91c23e948f3bb9e7b25 -size 9850 +oid sha256:1ea8423ccba04a9a137640b28ff8f84e167d46735d87fd38c48c29373a9601ac +size 16223 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-viridis.png b/examples/notebooks/screenshots/nb-lines-cmap-viridis.png index 2d71b4428..e6493053c 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-viridis.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-viridis.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6295649505902ac1f37ae6453e278dbbcdacb64426f1c51e27e16ef38650f8a8 -size 13725 +oid sha256:9fd9697d7df47491c6b9e73424dd07088c7e22758d5672a99ddbce56e4ff3b02 +size 19316 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-white.png b/examples/notebooks/screenshots/nb-lines-cmap-white.png index b450a8ea4..cbbbef0bc 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-white.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-white.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a1abc26476bbabf31094bd70929afc918e4064a1996d7742adb716ed6e9c2617 -size 7532 +oid sha256:54242cbcd3f196e5f39fc3a27a98b42f9515f04625d37d3b210afd37721078dc +size 8967 diff --git a/examples/notebooks/screenshots/nb-lines-colors.png b/examples/notebooks/screenshots/nb-lines-colors.png index 88fef4e39..60792f453 100644 --- a/examples/notebooks/screenshots/nb-lines-colors.png +++ b/examples/notebooks/screenshots/nb-lines-colors.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bbbb1b63c69ef4061f0b64fc2360e0c613ee4732d581929657068f55141d6fd9 -size 27274 +oid sha256:9ae3f1bae2ea0fe146c7096af3551e5e58704416bff49a6f0bdd5415cfc1533b +size 37095 diff --git a/examples/notebooks/screenshots/nb-lines-data.png b/examples/notebooks/screenshots/nb-lines-data.png index b8c5bf582..86ce4362b 100644 --- a/examples/notebooks/screenshots/nb-lines-data.png +++ b/examples/notebooks/screenshots/nb-lines-data.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6f677a3c0a1b2fb57771af6118d45d23b1d86f88d3431ca06ef89b79a48dad06 -size 38880 +oid sha256:17ec845345cb97088de4c18c8eebc54c9a27c5770724f8f582b4144f3e70d139 +size 46868 diff --git a/examples/notebooks/screenshots/nb-lines-underlay.png b/examples/notebooks/screenshots/nb-lines-underlay.png index 93edd81d6..7d1280db4 100644 --- a/examples/notebooks/screenshots/nb-lines-underlay.png +++ b/examples/notebooks/screenshots/nb-lines-underlay.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:35e0ea48cac0242e79da491629bda9fccedb94814e8d3d1188323c7d9668e513 -size 49940 +oid sha256:467788d8588fa1794c0cd705e03564537ff49f79762a5e8f092700516d503391 +size 52447 diff --git a/examples/notebooks/screenshots/nb-lines.png b/examples/notebooks/screenshots/nb-lines.png index e28486bf4..4fd64a56d 100644 --- a/examples/notebooks/screenshots/nb-lines.png +++ b/examples/notebooks/screenshots/nb-lines.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:17ee8c3de59b9e80d66c30d61287a38ac06ee996833f32648506a6bf1ebb0da8 -size 23317 +oid sha256:da1e28036caa8077885f52aa3a6ba4dbe1ee4f8cfa79a7b604614483150cd7b7 +size 24798 diff --git a/examples/tests/test_examples.py b/examples/tests/test_examples.py index c08df9005..9562a4357 100644 --- a/examples/tests/test_examples.py +++ b/examples/tests/test_examples.py @@ -68,6 +68,10 @@ def test_example_screenshots(module, force_offscreen): # import the example module example = importlib.import_module(module_name) + for subplot in example.figure: + subplot.viewport.render(subplot.scene, subplot.camera) + example.figure.renderer.flush() + # render a frame img = np.asarray(example.figure.renderer.target.draw()) @@ -104,7 +108,7 @@ def test_example_screenshots(module, force_offscreen): rgb = normalize_image(rgb) ref_img = normalize_image(ref_img) - similar, rmse = image_similarity(rgb, ref_img, threshold=0.025) + similar, rmse = image_similarity(rgb, ref_img, threshold=0.05) update_diffs(module.stem, similar, rgb, ref_img) assert similar, ( diff --git a/examples/tests/testutils.py b/examples/tests/testutils.py index 0e4cd2e1b..2ea4742ea 100644 --- a/examples/tests/testutils.py +++ b/examples/tests/testutils.py @@ -23,7 +23,8 @@ "line/*.py", "line_collection/*.py", "gridplot/*.py", - "misc/*.py" + "misc/*.py", + "selectors/*.py" ] diff --git a/fastplotlib/__init__.py b/fastplotlib/__init__.py index 8b46dcc0b..19dfb1903 100644 --- a/fastplotlib/__init__.py +++ b/fastplotlib/__init__.py @@ -3,6 +3,7 @@ from .utils.gui import run # noqa from .graphics import * from .graphics.selectors import * +from .graphics.utils import pause_events from .legends import * from .layouts import Figure diff --git a/fastplotlib/graphics/_axes.py b/fastplotlib/graphics/_axes.py new file mode 100644 index 000000000..2a63183d5 --- /dev/null +++ b/fastplotlib/graphics/_axes.py @@ -0,0 +1,598 @@ +import numpy as np + +import pygfx + + +GRID_PLANES = ["xy", "xz", "yz"] + + +# very thin subclass that just adds GridMaterial properties to this world object for easier user control +class Grid(pygfx.Grid): + @property + def major_step(self) -> tuple[float, float]: + """The step distance between the major grid lines.""" + return self.material.major_step + + @major_step.setter + def major_step(self, step: tuple[float, float]): + self.material.major_step = step + + @property + def minor_step(self) -> tuple[float, float]: + """The step distance between the minor grid lines.""" + return self.material.minor_step + + @minor_step.setter + def minor_step(self, step: tuple[float, float]): + self.material.minor_step = step + + @property + def axis_thickness(self) -> float: + """The thickness of the axis lines.""" + return self.material.axis_thickness + + @axis_thickness.setter + def axis_thickness(self, thickness: float): + self.material.axis_thickness = thickness + + @property + def major_thickness(self) -> float: + """The thickness of the major grid lines.""" + return self.material.major_thickness + + @major_thickness.setter + def major_thickness(self, thickness: float): + self.material.major_thickness = thickness + + @property + def minor_thickness(self) -> float: + """The thickness of the minor grid lines.""" + return self.material.minor_thickness + + @minor_thickness.setter + def minor_thickness(self, thickness: float): + self.material.minor_thickness = thickness + + @property + def thickness_space(self) -> str: + """The coordinate space in which the thicknesses are expressed. + + See :obj:`pygfx.utils.enums.CoordSpace`: + """ + return self.material.thickness_space + + @thickness_space.setter + def thickness_space(self, value: str): + self.material.thickness_space = value + + @property + def axis_color(self) -> str: + """The color of the axis lines.""" + return self.material.axis_color + + @axis_color.setter + def axis_color(self, color: str): + self.material.axis_color = color + + @property + def major_color(self) -> str: + """The color of the major grid lines.""" + return self.material.major_color + + @major_color.setter + def major_color(self, color: str): + self.material.major_color = color + + @property + def minor_color(self) -> str: + """The color of the minor grid lines.""" + return self.material.minor_color + + @minor_color.setter + def minor_color(self, color: str): + self.material.minor_color = color + + @property + def infinite(self) -> bool: + """Whether the grid is infinite. + + If not infinite, the grid is 1x1 in world space, scaled, rotated, and + positioned with the object's transform. + + (Infinite grids are not actually infinite. Rather they move along with + the camera, and are sized based on the distance between the camera and + the grid.) + """ + return self.material.infinite + + @infinite.setter + def infinite(self, value: str): + self.material.infinite = value + + +class Grids(pygfx.Group): + """Just a class to make accessing the grids easier""" + + def __init__(self, *, xy, xz, yz): + super().__init__() + + self._xy = xy + self._xz = xz + self._yz = yz + + self.add(xy, xz, yz) + + @property + def xy(self) -> Grid: + """xy grid""" + return self._xy + + @property + def xz(self) -> Grid: + """xz grid""" + return self._xz + + @property + def yz(self) -> Grid: + """yz grid""" + return self._yz + + +class Ruler(pygfx.Ruler): + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.tick_text_mapper = None + self.font_size = 14 + + def _update_sub_objects(self, ticks, tick_auto_step): + """Update the sub-objects to show the given ticks.""" + assert isinstance(ticks, dict) + + tick_size = 5 + min_n_slots = 8 # todo: can be (much) higher when we use a single text object! + + # Load config + start_pos = self._start_pos + end_pos = self._end_pos + start_value = self._start_value + end_value = self.end_value + + # Derive some more variables + length = end_value - start_value + vec = end_pos - start_pos + if length: + vec /= length + + # Get array to store positions + n_slots = self.points.geometry.positions.nitems + n_positions = len(ticks) + 2 + if n_positions <= n_slots <= max(min_n_slots, 2 * n_positions): + # Re-use existing buffers + positions = self.points.geometry.positions.data + sizes = self.points.geometry.sizes.data + self.points.geometry.positions.update_range() + self.points.geometry.sizes.update_range() + else: + # Allocate new buffers + new_n_slots = max(min_n_slots, int(n_positions * 1.2)) + positions = np.zeros((new_n_slots, 3), np.float32) + sizes = np.zeros((new_n_slots,), np.float32) + self.points.geometry.positions = pygfx.Buffer(positions) + self.points.geometry.sizes = pygfx.Buffer(sizes) + # Allocate text objects + while len(self._text_object_pool) < new_n_slots: + ob = pygfx.Text( + pygfx.TextGeometry("", screen_space=True, font_size=self.font_size), + pygfx.TextMaterial(aa=False), + ) + self._text_object_pool.append(ob) + self._text_object_pool[new_n_slots:] = [] + # Reset children + self.clear() + self.add(self._line, self._points, *self._text_object_pool) + + def define_text(pos, text): + if self.tick_text_mapper is not None and text != "": + text = self.tick_text_mapper(text) + + ob = self._text_object_pool[index] + ob.geometry.anchor = self._text_anchor + ob.geometry.anchor_offset = self._text_anchor_offset + ob.geometry.set_text(text) + ob.local.position = pos + + # Apply start point + index = 0 + positions[0] = start_pos + if self._ticks_at_end_points: + sizes[0] = tick_size + define_text(start_pos, f"{self._start_value:0.4g}") + else: + sizes[0] = 0 + define_text(start_pos, f"") + + # Collect ticks + index += 1 + for value, text in ticks.items(): + pos = start_pos + vec * (value - start_value) + positions[index] = pos + sizes[index] = tick_size + define_text(pos, text) + index += 1 + + # Handle end point, and nullify remaining slots + positions[index:] = end_pos + sizes[index:] = 0 + for ob in self._text_object_pool[index:]: + ob.geometry.set_text("") + + # Show last tick? + if self._ticks_at_end_points: + sizes[index] = tick_size + define_text(end_pos, f"{end_value:0.4g}") + + # Hide the ticks close to the ends? + if self._ticks_at_end_points and ticks: + tick_values = list(ticks.keys()) + if abs(tick_values[0] - start_value) < 0.5 * tick_auto_step: + self._text_object_pool[1].geometry.set_text("") + if abs(tick_values[-1] - end_value) < 0.5 * tick_auto_step: + self._text_object_pool[index - 1].geometry.set_text("") + + +class Axes: + def __init__( + self, + plot_area, + intersection: tuple[int, int, int] | None = None, + x_kwargs: dict = None, + y_kwargs: dict = None, + z_kwargs: dict = None, + grids: bool = True, + grid_kwargs: dict = None, + auto_grid: bool = True, + offset: np.ndarray = np.array([0.0, 0.0, 0.0]), + ): + self._plot_area = plot_area + + if x_kwargs is None: + x_kwargs = dict() + + if y_kwargs is None: + y_kwargs = dict() + + if z_kwargs is None: + z_kwargs = dict() + + x_kwargs = { + "tick_side": "right", + **x_kwargs, + } + + y_kwargs = {"tick_side": "left", **y_kwargs} + + z_kwargs = { + "tick_side": "left", + **z_kwargs, + } + + # create ruler for each dim + self._x = Ruler(**x_kwargs) + self._y = Ruler(**y_kwargs) + self._z = Ruler(**z_kwargs) + + self._offset = offset + + # *MUST* instantiate some start and end positions for the rulers else kernel crashes immediately + # probably a WGPU rust panic + self.x.start_pos = 0, 0, 0 + self.x.end_pos = 100, 0, 0 + self.x.start_value = self.x.start_pos[0] - offset[0] + statsx = self.x.update( + self._plot_area.camera, self._plot_area.viewport.logical_size + ) + + self.y.start_pos = 0, 0, 0 + self.y.end_pos = 0, 100, 0 + self.y.start_value = self.y.start_pos[1] - offset[1] + statsy = self.y.update( + self._plot_area.camera, self._plot_area.viewport.logical_size + ) + + self.z.start_pos = 0, 0, 0 + self.z.end_pos = 0, 0, 100 + self.z.start_value = self.z.start_pos[1] - offset[2] + self.z.update(self._plot_area.camera, self._plot_area.viewport.logical_size) + + # world object for the rulers + grids + self._world_object = pygfx.Group() + + # add rulers + self.world_object.add( + self.x, + self.y, + self.z, + ) + + # set z ruler invisible for orthographic projections for now + if self._plot_area.camera.fov == 0: + # TODO: allow any orientation in the future even for orthographic projections + self.z.visible = False + + if grid_kwargs is None: + grid_kwargs = dict() + + grid_kwargs = { + "major_step": 10, + "minor_step": 1, + "thickness_space": "screen", + "major_thickness": 2, + "minor_thickness": 0.5, + "infinite": True, + **grid_kwargs, + } + + if grids: + _grids = dict() + for plane in GRID_PLANES: + grid = Grid( + geometry=None, + material=pygfx.GridMaterial(**grid_kwargs), + orientation=plane, + visible=False, + ) + + _grids[plane] = grid + + self._grids = Grids(**_grids) + self.world_object.add(self._grids) + + if self._plot_area.camera.fov == 0: + # orthographic projection, place grids far away + self._grids.local.z = -1000 + + major_step_x, major_step_y = statsx["tick_step"], statsy["tick_step"] + + self.grids.xy.material.major_step = major_step_x, major_step_y + self.grids.xy.material.minor_step = 0.2 * major_step_x, 0.2 * major_step_y + + else: + self._grids = False + + self._intersection = intersection + self._auto_grid = auto_grid + + @property + def world_object(self) -> pygfx.WorldObject: + return self._world_object + + @property + def offset(self) -> np.ndarray: + """offset of the axes""" + return self._offset + + @offset.setter + def offset(self, value: np.ndarray): + self._offset = value + + @property + def x(self) -> Ruler: + """x axis ruler""" + return self._x + + @property + def y(self) -> Ruler: + """y axis ruler""" + return self._y + + @property + def z(self) -> Ruler: + """z axis ruler""" + return self._z + + @property + def grids(self) -> Grids | bool: + """grids for each plane: xy, xz, yz""" + return self._grids + + @property + def auto_grid(self) -> bool: + """auto adjust the grid on each render cycle""" + return self._auto_grid + + @auto_grid.setter + def auto_grid(self, value: bool): + self._auto_grid = value + + @property + def visible(self) -> bool: + """set visibility of all axes elements, rulers and grids""" + return self._world_object.visible + + @visible.setter + def visible(self, value: bool): + self._world_object.visible = value + + @property + def intersection(self) -> tuple[float, float, float] | None: + return self._intersection + + @intersection.setter + def intersection(self, intersection: tuple[float, float, float] | None): + """ + intersection point of [x, y, z] rulers. + Set (0, 0, 0) for origin + Set to `None` to follow when panning through the scene with orthographic projection + """ + if intersection is None: + self._intersection = None + return + + if len(intersection) != 3: + raise ValueError( + "intersection must be a float of 3 elements for [x, y, z] or `None`" + ) + + self._intersection = tuple(float(v) for v in intersection) + + def update_using_bbox(self, bbox): + """ + Update the w.r.t. the given bbox + + Parameters + ---------- + bbox: np.ndarray + array of shape [2, 3], [[xmin, ymin, zmin], [xmax, ymax, zmax]] + + """ + + # flip axes if camera scale is flipped + if self._plot_area.camera.local.scale_x < 0: + bbox[0, 0], bbox[1, 0] = bbox[1, 0], bbox[0, 0] + + if self._plot_area.camera.local.scale_y < 0: + bbox[0, 1], bbox[1, 1] = bbox[1, 1], bbox[0, 1] + + if self._plot_area.camera.local.scale_z < 0: + bbox[0, 2], bbox[1, 2] = bbox[1, 2], bbox[0, 2] + + if self.intersection is None: + intersection = (0, 0, 0) + else: + intersection = self.intersection + + self.update(bbox, intersection) + + def update_using_camera(self): + """ + Update the axes w.r.t the current camera state + + For orthographic projections of the xy plane, it will calculate the inverse projection + of the screen space onto world space to determine the current range of the world space + to set the rulers and ticks + + For perspective projections it will just use the bbox of the scene to set the rulers + + """ + + if not self.visible: + return + + if self._plot_area.camera.fov == 0: + xpos, ypos, width, height = self._plot_area.get_rect() + # orthographic projection, get ranges using inverse + + # get range of screen space + xmin, xmax = xpos, xpos + width + ymin, ymax = ypos + height, ypos + + min_vals = self._plot_area.map_screen_to_world((xmin, ymin)) + max_vals = self._plot_area.map_screen_to_world((xmax, ymax)) + + if min_vals is None or max_vals is None: + return + + world_xmin, world_ymin, _ = min_vals + world_xmax, world_ymax, _ = max_vals + + world_zmin, world_zmax = 0, 0 + + bbox = np.array( + [ + [world_xmin, world_ymin, world_zmin], + [world_xmax, world_ymax, world_zmax], + ] + ) + + else: + # set ruler start and end positions based on scene bbox + bbox = self._plot_area._fpl_graphics_scene.get_world_bounding_box() + + if self.intersection is None: + if self._plot_area.camera.fov == 0: + # place the ruler close to the left and bottom edges of the viewport + # TODO: determine this for perspective projections + xscreen_10, yscreen_10 = xpos + (width * 0.1), ypos + (height * 0.9) + intersection = self._plot_area.map_screen_to_world( + (xscreen_10, yscreen_10) + ) + else: + # force origin since None is not supported for Persepctive projections + self._intersection = (0, 0, 0) + intersection = self._intersection + + else: + # axes intersect at the origin + intersection = self.intersection + + self.update(bbox, intersection) + + def update(self, bbox, intersection): + """ + Update the axes using the given bbox and ruler intersection point + + Parameters + ---------- + bbox: np.ndarray + array of shape [2, 3], [[xmin, ymin, zmin], [xmax, ymax, zmax]] + + intersection: float, float, float + intersection point of the x, y, z ruler + + """ + + world_xmin, world_ymin, world_zmin = bbox[0] + world_xmax, world_ymax, world_zmax = bbox[1] + world_x_10, world_y_10, world_z_10 = intersection + + # swap min and max for each dimension if necessary + if self._plot_area.camera.local.scale_y < 0: + world_ymin, world_ymax = world_ymax, world_ymin + self.y.tick_side = "right" # swap tick side + self.x.tick_side = "right" + else: + self.y.tick_side = "left" + self.x.tick_side = "right" + + if self._plot_area.camera.local.scale_x < 0: + world_xmin, world_xmax = world_xmax, world_xmin + self.x.tick_side = "left" + + self.x.start_pos = world_xmin, world_y_10, world_z_10 + self.x.end_pos = world_xmax, world_y_10, world_z_10 + + self.x.start_value = self.x.start_pos[0] - self.offset[0] + statsx = self.x.update( + self._plot_area.camera, self._plot_area.viewport.logical_size + ) + + self.y.start_pos = world_x_10, world_ymin, world_z_10 + self.y.end_pos = world_x_10, world_ymax, world_z_10 + + self.y.start_value = self.y.start_pos[1] - self.offset[1] + statsy = self.y.update( + self._plot_area.camera, self._plot_area.viewport.logical_size + ) + + if self._plot_area.camera.fov != 0: + self.z.start_pos = world_x_10, world_y_10, world_zmin + self.z.end_pos = world_x_10, world_y_10, world_zmax + + self.z.start_value = self.z.start_pos[1] - self.offset[2] + statsz = self.z.update( + self._plot_area.camera, self._plot_area.viewport.logical_size + ) + major_step_z = statsz["tick_step"] + + if self.grids: + if self.auto_grid: + major_step_x, major_step_y = statsx["tick_step"], statsy["tick_step"] + self.grids.xy.major_step = major_step_x, major_step_y + self.grids.xy.minor_step = 0.2 * major_step_x, 0.2 * major_step_y + + if self._plot_area.camera.fov != 0: + self.grids.xz.major_step = major_step_x, major_step_z + self.grids.xz.minor_step = 0.2 * major_step_x, 0.2 * major_step_z + + self.grids.yz.material.major_step = major_step_y, major_step_z + self.grids.yz.minor_step = 0.2 * major_step_y, 0.2 * major_step_z diff --git a/fastplotlib/graphics/_base.py b/fastplotlib/graphics/_base.py index 01482ae6e..27bfbc149 100644 --- a/fastplotlib/graphics/_base.py +++ b/fastplotlib/graphics/_base.py @@ -17,6 +17,7 @@ Rotation, Visible, ) +from ._axes import Axes HexStr: TypeAlias = str @@ -114,6 +115,8 @@ def __init__( self._visible = Visible(visible) self._block_events = False + self._axes: Axes = None + @property def supported_events(self) -> tuple[str]: """events supported by this graphic""" @@ -355,6 +358,12 @@ def _fpl_prepare_del(self): Optionally implemented in subclasses """ + # remove axes if added to this graphic + if self._axes is not None: + self._plot_area.scene.remove(self._axes) + self._plot_area.remove_animation(self._update_axes) + self._axes.world_object.clear() + # signal that a deletion has been requested self.deleted = True @@ -413,3 +422,18 @@ def rotate(self, alpha: float, axis: Literal["x", "y", "z"] = "y"): f"`axis` must be either `x`, `y`, or `z`. `{axis}` provided instead!" ) self.rotation = la.quat_mul(rot, self.rotation) + + @property + def axes(self) -> Axes: + return self._axes + + def add_axes(self): + """Add axes onto this Graphic""" + if self._axes is not None: + raise AttributeError("Axes already added onto this graphic") + + self._axes = Axes(self._plot_area, offset=self.offset, grids=False) + self._axes.world_object.local.rotation = self.world_object.local.rotation + + self._plot_area.scene.add(self.axes.world_object) + self._axes.update_using_bbox(self.world_object.get_world_bounding_box()) diff --git a/fastplotlib/graphics/image.py b/fastplotlib/graphics/image.py index 866ed2486..6730e86cb 100644 --- a/fastplotlib/graphics/image.py +++ b/fastplotlib/graphics/image.py @@ -132,7 +132,7 @@ def __init__( self._vmax = ImageVmax(vmax) # set cmap to None for RGB images - if self._data.value.ndim == 3: + if self._data.value.ndim > 2: self._cmap = None else: self._cmap = ImageCmap(cmap) @@ -192,14 +192,14 @@ def data(self, data): @property def cmap(self) -> str: """colormap name""" - if self.data.value.ndim == 3: - raise AttributeError("RGB images do not have a colormap property") + if self.data.value.ndim > 2: + raise AttributeError("RGB(A) images do not have a colormap property") return self._cmap.value @cmap.setter def cmap(self, name: str): - if self.data.value.ndim == 3: - raise AttributeError("RGB images do not have a colormap property") + if self.data.value.ndim > 2: + raise AttributeError("RGB(A) images do not have a colormap property") self._cmap.set_value(self, name) @property diff --git a/fastplotlib/graphics/selectors/_linear.py b/fastplotlib/graphics/selectors/_linear.py index e7b0032f9..eec4cc910 100644 --- a/fastplotlib/graphics/selectors/_linear.py +++ b/fastplotlib/graphics/selectors/_linear.py @@ -372,7 +372,16 @@ def _get_selected_index(self, graphic): if "Image" in graphic.__class__.__name__: # indices map directly to grid geometry for image data buffer index = self.selection - return round(index) + shape = graphic.data[:].shape + + if self.axis == "x": + # assume selecting columns + upper_bound = shape[1] - 1 + elif self.axis == "y": + # assume selecting rows + upper_bound = shape[0] - 1 + + return min(round(index), upper_bound) def _move_graphic(self, delta: np.ndarray): """ diff --git a/fastplotlib/graphics/utils.py b/fastplotlib/graphics/utils.py new file mode 100644 index 000000000..6be5aefc4 --- /dev/null +++ b/fastplotlib/graphics/utils.py @@ -0,0 +1,37 @@ +from contextlib import contextmanager + +from ._base import Graphic + + +@contextmanager +def pause_events(*graphics: Graphic): + """ + Context manager for pausing Graphic events. + + Examples + -------- + + .. code-block:: + + # pass in any number of graphics + with fpl.pause_events(graphic1, graphic2, graphic3): + # enter context manager + # all events are blocked from graphic1, graphic2, graphic3 + + # context manager exited, event states restored. + + """ + if not all([isinstance(g, Graphic) for g in graphics]): + raise TypeError( + f"`pause_events` only takes Graphic instances as arguments, " + f"you have passed the following types:\n{[type(g) for g in graphics]}" + ) + + original_vals = [g.block_events for g in graphics] + + for g in graphics: + g.block_events = True + yield + + for g, value in zip(graphics, original_vals): + g.block_events = value diff --git a/fastplotlib/layouts/_figure.py b/fastplotlib/layouts/_figure.py index d330c6928..3ad5231c7 100644 --- a/fastplotlib/layouts/_figure.py +++ b/fastplotlib/layouts/_figure.py @@ -474,7 +474,7 @@ def show( _maintain_aspect = subplot.camera.maintain_aspect else: _maintain_aspect = maintain_aspect - subplot.auto_scale(maintain_aspect=_maintain_aspect, zoom=0.95) + subplot.auto_scale(maintain_aspect=maintain_aspect) # return the appropriate OutputContext based on the current canvas if self.canvas.__class__.__name__ == "JupyterWgpuCanvas": @@ -497,6 +497,20 @@ def show( frame=self, make_toolbar=toolbar, add_widgets=add_widgets ) + elif self.canvas.__class__.__name__ == "WgpuManualOffscreenCanvas": + # for test and docs gallery screenshots + for subplot in self: + subplot.set_viewport_rect() + subplot.axes.update_using_camera() + + # render call is blocking only on github actions for some reason, + # but not for rtd build, this is a workaround + # for CI tests, the render call works if it's in test_examples + # but it is necessary for the gallery images too so that's why this check is here + if "RTD_BUILD" in os.environ.keys(): + if os.environ["RTD_BUILD"] == "1": + subplot.viewport.render(subplot.scene, subplot.camera) + else: # assume GLFW, the output context is just the canvas self._output = self.canvas diff --git a/fastplotlib/layouts/_plot_area.py b/fastplotlib/layouts/_plot_area.py index 36d9c4019..e5cf1a74b 100644 --- a/fastplotlib/layouts/_plot_area.py +++ b/fastplotlib/layouts/_plot_area.py @@ -99,11 +99,25 @@ def __init__( # legends, managed just like other graphics as explained above self._legends: list[Legend] = list() + # keep all graphics in a separate group, makes bbox calculations etc. easier + # this is the "real scene" excluding axes, selection tools etc. + self._fpl_graphics_scene = pygfx.Group() + self.scene.add(self._fpl_graphics_scene) + self._name = name # need to think about how to deal with children better self.children = list() + self._background_material = pygfx.BackgroundMaterial( + (0.0, 0.0, 0.0, 1.0), + (0.0, 0.0, 0.0, 1.0), + (0.0, 0.0, 0.0, 1.0), + (0.0, 0.0, 0.0, 1.0), + ) + self._background = pygfx.Background(None, self._background_material) + self.scene.add(self._background) + self.set_viewport_rect() # several read-only properties @@ -240,6 +254,21 @@ def name(self, name: str): raise TypeError("PlotArea `name` must be of type ") self._name = name + @property + def background_color(self) -> tuple[pygfx.Color, ...]: + """background colors, (top left, top right, bottom right, bottom left)""" + return ( + self._background_material.color_top_left, + self._background_material.color_top_right, + self._background_material.color_bottom_right, + self._background_material.color_bottom_left, + ) + + @background_color.setter + def background_color(self, colors: str | tuple[float]): + """1, 2, or 4 colors, each color must be acceptable by pygfx.Color""" + self._background_material.set_colors(*colors) + def get_rect(self) -> tuple[float, float, float, float]: """ Returns the viewport rect to define the rectangle @@ -394,7 +423,7 @@ def add_graphic(self, graphic: Graphic, center: bool = True): if graphic in self: # graphic is already in this plot but was removed from the scene, add it back - self.scene.add(graphic.world_object) + self._fpl_graphics_scene.add(graphic.world_object) return self._add_or_insert_graphic(graphic=graphic, center=center, action="add") @@ -465,12 +494,15 @@ def _add_or_insert_graphic( if isinstance(graphic, BaseSelector): obj_list = self._selectors + self.scene.add(graphic.world_object) elif isinstance(graphic, Legend): obj_list = self._legends + self.scene.add(graphic.world_object) elif isinstance(graphic, Graphic): obj_list = self._graphics + self._fpl_graphics_scene.add(graphic.world_object) else: raise TypeError("graphic must be of type Graphic | BaseSelector | Legend") @@ -482,9 +514,6 @@ def _add_or_insert_graphic( else: raise ValueError("valid actions are 'insert' | 'add'") - # add world object to scene - self.scene.add(graphic.world_object) - if center: self.center_graphic(graphic) @@ -518,7 +547,7 @@ def center_graphic(self, graphic: Graphic, zoom: float = 1.35): # probably because camera.show_object uses bounding sphere self.camera.zoom = zoom - def center_scene(self, *, zoom: float = 1.35): + def center_scene(self, *, zoom: float = 1.0): """ Auto-center the scene, does not scale. @@ -528,13 +557,13 @@ def center_scene(self, *, zoom: float = 1.35): apply a zoom after centering the scene """ - if not len(self.scene.children) > 0: + if not len(self._fpl_graphics_scene.children) > 0: return # scale all cameras associated with this controller # else it looks wonky for camera in self.controller.cameras: - camera.show_object(self.scene) + camera.show_object(self._fpl_graphics_scene) # camera.show_object can cause the camera width and height to increase so apply a zoom to compensate # probably because camera.show_object uses bounding sphere @@ -544,7 +573,7 @@ def auto_scale( self, *, # since this is often used as an event handler, don't want to coerce maintain_aspect = True maintain_aspect: None | bool = None, - zoom: float = 0.8, + zoom: float = 0.75, ): """ Auto-scale the camera w.r.t to the scene @@ -559,12 +588,8 @@ def auto_scale( zoom value for the camera after auto-scaling, if zoom = 1.0 then the graphics in the scene will fill the entire canvas. """ - if not len(self.scene.children) > 0: + if not len(self._fpl_graphics_scene.children) > 0: return - # hacky workaround for now until we decide if we want to put selectors in their own scene - # remove all selectors from a scene to calculate scene bbox - for selector in self.selectors: - self.scene.remove(selector.world_object) self.center_scene() @@ -575,8 +600,10 @@ def auto_scale( for camera in self.controller.cameras: camera.maintain_aspect = maintain_aspect - if len(self.scene.children) > 0: - width, height, depth = np.ptp(self.scene.get_world_bounding_box(), axis=0) + if len(self._fpl_graphics_scene.children) > 0: + width, height, depth = np.ptp( + self._fpl_graphics_scene.get_world_bounding_box(), axis=0 + ) else: width, height, depth = (1, 1, 1) @@ -586,9 +613,6 @@ def auto_scale( if height < 0.01: height = 1 - for selector in self.selectors: - self.scene.add(selector.world_object) - # scale all cameras associated with this controller else it looks wonky for camera in self.controller.cameras: camera.width = width @@ -609,7 +633,11 @@ def remove_graphic(self, graphic: Graphic): """ - self.scene.remove(graphic.world_object) + if isinstance(graphic, (BaseSelector, Legend)): + self.scene.remove(graphic.world_object) + + elif isinstance(graphic, Graphic): + self._fpl_graphics_scene.remove(graphic.world_object) def delete_graphic(self, graphic: Graphic): """ @@ -626,6 +654,7 @@ def delete_graphic(self, graphic: Graphic): if isinstance(graphic, BaseSelector): self._selectors.remove(graphic) + elif isinstance(graphic, Legend): self._legends.remove(graphic) @@ -636,6 +665,9 @@ def delete_graphic(self, graphic: Graphic): if graphic.world_object in self.scene.children: self.scene.remove(graphic.world_object) + elif graphic.world_object in self._fpl_graphics_scene.children: + self._fpl_graphics_scene.remove(graphic.world_object) + # cleanup graphic._fpl_prepare_del() diff --git a/fastplotlib/layouts/_subplot.py b/fastplotlib/layouts/_subplot.py index 059307e6b..293cea00c 100644 --- a/fastplotlib/layouts/_subplot.py +++ b/fastplotlib/layouts/_subplot.py @@ -10,6 +10,7 @@ from ._utils import make_canvas_and_renderer, create_camera, create_controller from ._plot_area import PlotArea from ._graphic_methods_mixin import GraphicMethodsMixin +from ..graphics._axes import Axes class Subplot(PlotArea, GraphicMethodsMixin): @@ -88,12 +89,6 @@ def __init__( self.spacing = 2 - self._axes: pygfx.AxesHelper = pygfx.AxesHelper(size=100) - for arrow in self._axes.children: - self._axes.remove(arrow) - - self._grid: pygfx.GridHelper = pygfx.GridHelper(size=100, thickness=1) - self._title_graphic: TextGraphic = None super(Subplot, self).__init__( @@ -116,6 +111,13 @@ def __init__( if self.name is not None: self.set_title(self.name) + self._axes = Axes(self) + self.scene.add(self.axes.world_object) + + @property + def axes(self) -> Axes: + return self._axes + @property def name(self) -> str: return self._name @@ -140,6 +142,10 @@ def docks(self) -> dict: """ return self._docks + def render(self): + self.axes.update_using_camera() + super().render() + def set_title(self, text: str): """Sets the plot title, stored as a ``TextGraphic`` in the "top" dock area""" if text is None: @@ -169,7 +175,7 @@ def center_title(self): def get_rect(self): """Returns the bounding box that defines the Subplot within the canvas.""" row_ix, col_ix = self.position - width_canvas, height_canvas = self.renderer.logical_size + width_canvas, height_canvas = self.canvas.get_logical_size() x_pos = ( (width_canvas / self.ncols) + ((col_ix - 1) * (width_canvas / self.ncols)) @@ -187,20 +193,6 @@ def get_rect(self): return rect - def set_axes_visibility(self, visible: bool): - """Toggles axes visibility.""" - if visible: - self.scene.add(self._axes) - else: - self.scene.remove(self._axes) - - def set_grid_visibility(self, visible: bool): - """Toggles grid visibility.""" - if visible: - self.scene.add(self._grid) - else: - self.scene.remove(self._grid) - class Dock(PlotArea): _valid_positions = ["right", "left", "top", "bottom"] diff --git a/fastplotlib/widgets/histogram_lut.py b/fastplotlib/widgets/histogram_lut.py index a3b36eb45..0f63eb8f4 100644 --- a/fastplotlib/widgets/histogram_lut.py +++ b/fastplotlib/widgets/histogram_lut.py @@ -1,14 +1,28 @@ +from math import ceil import weakref import numpy as np -from pygfx import Group +import pygfx from ..graphics import LineGraphic, ImageGraphic, TextGraphic +from ..graphics.utils import pause_events from ..graphics._base import Graphic from ..graphics.selectors import LinearRegionSelector +def _get_image_graphic_events(image_graphic: ImageGraphic) -> list[str]: + """Small helper function to return the relevant events for an ImageGraphic""" + events = ["vmin", "vmax"] + + if not image_graphic.data.value.ndim > 2: + events.append("cmap") + + # if RGB(A), do not add cmap + + return events + + # TODO: This is a widget, we can think about a BaseWidget class later if necessary class HistogramLUT(Graphic): def __init__( @@ -47,7 +61,7 @@ def __init__( self._histogram_line = LineGraphic(line_data) - bounds = (edges[0], edges[-1]) + bounds = (edges[0] * self._scale_factor, edges[-1] * self._scale_factor) limits = (edges_flanked[0], edges_flanked[-1]) size = 120 # since it's scaled to 100 origin = (hist_scaled.max() / 2, 0) @@ -64,8 +78,8 @@ def __init__( # there will be a small difference with the histogram edges so this makes them both line up exactly self._linear_region_selector.selection = ( - self._image_graphic.vmin, - self._image_graphic.vmax, + self._image_graphic.vmin * self._scale_factor, + self._image_graphic.vmax * self._scale_factor, ) self._vmin = self.image_graphic.vmin @@ -95,7 +109,7 @@ def __init__( self._text_vmax.world_object.material.pick_write = False - widget_wo = Group() + widget_wo = pygfx.Group() widget_wo.add( self._histogram_line.world_object, self._linear_region_selector.world_object, @@ -115,7 +129,46 @@ def __init__( self._linear_region_handler, "selection" ) - self.image_graphic.add_event_handler(self._image_cmap_handler, "vmin", "vmax") + ig_events = _get_image_graphic_events(self.image_graphic) + + self.image_graphic.add_event_handler(self._image_cmap_handler, *ig_events) + + # colorbar for grayscale images + if self.image_graphic.data.value.ndim != 3: + self._colorbar: ImageGraphic = self._make_colorbar(edges_flanked) + + self.world_object.add(self._colorbar.world_object) + else: + self._colorbar = None + self._cmap = None + + def _make_colorbar(self, edges_flanked) -> ImageGraphic: + # use the histogram edge values as data for an + # image with 2 columns, this will be our colorbar! + colorbar_data = np.column_stack( + [ + np.linspace( + edges_flanked[0], edges_flanked[-1], ceil(np.ptp(edges_flanked)) + ) + ] + * 2 + ).astype(np.float32) + + colorbar_data /= self._scale_factor + + cbar = ImageGraphic( + data=colorbar_data, + vmin=self.vmin, + vmax=self.vmax, + cmap=self.image_graphic.cmap, + interpolation="linear", + offset=(-55, edges_flanked[0], -1), + ) + + cbar.world_object.world.scale_x = 20 + self._cmap = self.image_graphic.cmap + + return cbar def _get_vmin_vmax_str(self) -> tuple[str, str]: if self.vmin < 0.001 or self.vmin > 99_999: @@ -136,6 +189,7 @@ def _fpl_add_plot_area_hook(self, plot_area): self._histogram_line._fpl_add_plot_area_hook(plot_area) self._plot_area.auto_scale() + self._plot_area.controller.enabled = True def _calculate_histogram(self, data): if data.ndim > 2: @@ -209,27 +263,39 @@ def _linear_region_handler(self, ev): def _image_cmap_handler(self, ev): setattr(self, ev.type, ev.info["value"]) + @property + def cmap(self) -> str: + return self._cmap + + @cmap.setter + def cmap(self, name: str): + if self._colorbar is None: + return + + with pause_events(self.image_graphic): + self.image_graphic.cmap = name + + self._cmap = name + self._colorbar.cmap = name + @property def vmin(self) -> float: return self._vmin @vmin.setter def vmin(self, value: float): - self.image_graphic.block_events = True - self._linear_region_selector.block_events = True - - # must use world coordinate values directly from selection() - # otherwise the linear region bounds jump to the closest bin edges - self._linear_region_selector.selection = ( - value * self._scale_factor, - self._linear_region_selector.selection[1], - ) - self.image_graphic.vmin = value - - self.image_graphic.block_events = False - self._linear_region_selector.block_events = False + with pause_events(self.image_graphic, self._linear_region_selector): + # must use world coordinate values directly from selection() + # otherwise the linear region bounds jump to the closest bin edges + self._linear_region_selector.selection = ( + value * self._scale_factor, + self._linear_region_selector.selection[1], + ) + self.image_graphic.vmin = value self._vmin = value + if self._colorbar is not None: + self._colorbar.vmin = value vmin_str, vmax_str = self._get_vmin_vmax_str() self._text_vmin.offset = (-120, self._linear_region_selector.selection[0], 0) @@ -241,22 +307,19 @@ def vmax(self) -> float: @vmax.setter def vmax(self, value: float): - self.image_graphic.block_events = True - self._linear_region_selector.block_events = True - - # must use world coordinate values directly from selection() - # otherwise the linear region bounds jump to the closest bin edges - self._linear_region_selector.selection = ( - self._linear_region_selector.selection[0], - value * self._scale_factor, - ) - - self.image_graphic.vmax = value + with pause_events(self.image_graphic, self._linear_region_selector): + # must use world coordinate values directly from selection() + # otherwise the linear region bounds jump to the closest bin edges + self._linear_region_selector.selection = ( + self._linear_region_selector.selection[0], + value * self._scale_factor, + ) - self.image_graphic.block_events = False - self._linear_region_selector.block_events = False + self.image_graphic.vmax = value self._vmax = value + if self._colorbar is not None: + self._colorbar.vmax = value vmin_str, vmax_str = self._get_vmin_vmax_str() self._text_vmax.offset = (-120, self._linear_region_selector.selection[1], 0) @@ -279,15 +342,23 @@ def set_data(self, data, reset_vmin_vmax: bool = True): self._linear_region_selector.limits = limits self._linear_region_selector.selection = bounds else: - # don't change the current selection - self.image_graphic.block_events = True - self._linear_region_selector.block_events = True - self._linear_region_selector.limits = limits - self.image_graphic.block_events = False - self._linear_region_selector.block_events = False + with pause_events(self.image_graphic, self._linear_region_selector): + # don't change the current selection + self._linear_region_selector.limits = limits self._data = weakref.proxy(data) + if self._colorbar is not None: + self.world_object.remove(self._colorbar.world_object) + + if self.image_graphic.data.value.ndim != 3: + self._colorbar: ImageGraphic = self._make_colorbar(edges_flanked) + + self.world_object.add(self._colorbar.world_object) + else: + self._colorbar = None + self._cmap = None + # reset plotarea dims self._plot_area.auto_scale() @@ -304,14 +375,20 @@ def image_graphic(self, graphic): if self._image_graphic is not None: # cleanup events from current image graphic - self._image_graphic.remove_event_handler(self._image_cmap_handler) + ig_events = _get_image_graphic_events(self._image_graphic) + self._image_graphic.remove_event_handler( + self._image_cmap_handler, *ig_events + ) self._image_graphic = graphic - self.image_graphic.add_event_handler(self._image_cmap_handler) + ig_events = _get_image_graphic_events(self._image_graphic) + + self.image_graphic.add_event_handler(self._image_cmap_handler, *ig_events) def disconnect_image_graphic(self): - self._image_graphic.remove_event_handler(self._image_cmap_handler) + ig_events = _get_image_graphic_events(self._image_graphic) + self._image_graphic.remove_event_handler(self._image_cmap_handler, *ig_events) del self._image_graphic # self._image_graphic = None diff --git a/fastplotlib/widgets/image.py b/fastplotlib/widgets/image.py index 749403781..1819f8742 100644 --- a/fastplotlib/widgets/image.py +++ b/fastplotlib/widgets/image.py @@ -126,7 +126,7 @@ def managed_graphics(self) -> list[ImageGraphic]: def cmap(self) -> list[str]: cmaps = list() for g in self.managed_graphics: - cmaps.append(g.cmap.name) + cmaps.append(g.cmap) return cmaps From 85337ba235210f16ec55822869bd8dacdd5a0447 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Wed, 31 Jul 2024 17:23:13 -0400 Subject: [PATCH 098/185] use cmap library for colormaps (#390) (#574) * use cmap library for colormaps (#390) * example with cmap * add to deps * fix shape * use full map * remove break * undo changes to screenshots * fix parse * remove diffs * remove x.py * minimize diff * add docstring * remove colormaps --------- Co-authored-by: Kushal Kolar * fix pygfx docs move * black --------- Co-authored-by: Talley Lambert --- docs/source/conf.py | 2 +- fastplotlib/utils/colormaps/Accent | 8 - fastplotlib/utils/colormaps/Blues | 256 ------------------- fastplotlib/utils/colormaps/BrBG | 256 ------------------- fastplotlib/utils/colormaps/BuGn | 256 ------------------- fastplotlib/utils/colormaps/BuPu | 256 ------------------- fastplotlib/utils/colormaps/CMRmap | 256 ------------------- fastplotlib/utils/colormaps/Dark2 | 8 - fastplotlib/utils/colormaps/GnBu | 256 ------------------- fastplotlib/utils/colormaps/Greens | 256 ------------------- fastplotlib/utils/colormaps/Greys | 256 ------------------- fastplotlib/utils/colormaps/OrRd | 256 ------------------- fastplotlib/utils/colormaps/Oranges | 256 ------------------- fastplotlib/utils/colormaps/PRGn | 256 ------------------- fastplotlib/utils/colormaps/Paired | 12 - fastplotlib/utils/colormaps/Pastel1 | 9 - fastplotlib/utils/colormaps/Pastel2 | 8 - fastplotlib/utils/colormaps/PiYG | 256 ------------------- fastplotlib/utils/colormaps/PuBu | 256 ------------------- fastplotlib/utils/colormaps/PuBuGn | 256 ------------------- fastplotlib/utils/colormaps/PuOr | 256 ------------------- fastplotlib/utils/colormaps/PuRd | 256 ------------------- fastplotlib/utils/colormaps/Purples | 256 ------------------- fastplotlib/utils/colormaps/RdBu | 256 ------------------- fastplotlib/utils/colormaps/RdGy | 256 ------------------- fastplotlib/utils/colormaps/RdPu | 256 ------------------- fastplotlib/utils/colormaps/RdYlBu | 256 ------------------- fastplotlib/utils/colormaps/RdYlGn | 256 ------------------- fastplotlib/utils/colormaps/Reds | 256 ------------------- fastplotlib/utils/colormaps/Set1 | 9 - fastplotlib/utils/colormaps/Set2 | 8 - fastplotlib/utils/colormaps/Set3 | 12 - fastplotlib/utils/colormaps/Spectral | 256 ------------------- fastplotlib/utils/colormaps/Wistia | 256 ------------------- fastplotlib/utils/colormaps/YlGn | 256 ------------------- fastplotlib/utils/colormaps/YlGnBu | 256 ------------------- fastplotlib/utils/colormaps/YlOrBr | 256 ------------------- fastplotlib/utils/colormaps/YlOrRd | 256 ------------------- fastplotlib/utils/colormaps/__init__.py | 0 fastplotlib/utils/colormaps/afmhot | 256 ------------------- fastplotlib/utils/colormaps/autumn | 256 ------------------- fastplotlib/utils/colormaps/binary | 256 ------------------- fastplotlib/utils/colormaps/bone | 256 ------------------- fastplotlib/utils/colormaps/brg | 256 ------------------- fastplotlib/utils/colormaps/bwr | 256 ------------------- fastplotlib/utils/colormaps/cividis | 256 ------------------- fastplotlib/utils/colormaps/cool | 256 ------------------- fastplotlib/utils/colormaps/coolwarm | 256 ------------------- fastplotlib/utils/colormaps/copper | 256 ------------------- fastplotlib/utils/colormaps/cubehelix | 256 ------------------- fastplotlib/utils/colormaps/flag | 256 ------------------- fastplotlib/utils/colormaps/gist_earth | 256 ------------------- fastplotlib/utils/colormaps/gist_gray | 256 ------------------- fastplotlib/utils/colormaps/gist_heat | 256 ------------------- fastplotlib/utils/colormaps/gist_ncar | 256 ------------------- fastplotlib/utils/colormaps/gist_rainbow | 256 ------------------- fastplotlib/utils/colormaps/gist_stern | 256 ------------------- fastplotlib/utils/colormaps/gist_yarg | 256 ------------------- fastplotlib/utils/colormaps/gnuplot | 256 ------------------- fastplotlib/utils/colormaps/gnuplot2 | 256 ------------------- fastplotlib/utils/colormaps/gray | 256 ------------------- fastplotlib/utils/colormaps/hot | 256 ------------------- fastplotlib/utils/colormaps/hsv | 256 ------------------- fastplotlib/utils/colormaps/inferno | 256 ------------------- fastplotlib/utils/colormaps/jet | 256 ------------------- fastplotlib/utils/colormaps/magma | 256 ------------------- fastplotlib/utils/colormaps/nipy_spectral | 256 ------------------- fastplotlib/utils/colormaps/ocean | 256 ------------------- fastplotlib/utils/colormaps/pink | 256 ------------------- fastplotlib/utils/colormaps/plasma | 256 ------------------- fastplotlib/utils/colormaps/prism | 256 ------------------- fastplotlib/utils/colormaps/rainbow | 256 ------------------- fastplotlib/utils/colormaps/seismic | 256 ------------------- fastplotlib/utils/colormaps/spring | 256 ------------------- fastplotlib/utils/colormaps/summer | 256 ------------------- fastplotlib/utils/colormaps/tab10 | 10 - fastplotlib/utils/colormaps/tab20 | 20 -- fastplotlib/utils/colormaps/tab20b | 20 -- fastplotlib/utils/colormaps/tab20c | 20 -- fastplotlib/utils/colormaps/terrain | 256 ------------------- fastplotlib/utils/colormaps/turbo | 256 ------------------- fastplotlib/utils/colormaps/twilight | 256 ------------------- fastplotlib/utils/colormaps/twilight_shifted | 256 ------------------- fastplotlib/utils/colormaps/viridis | 256 ------------------- fastplotlib/utils/colormaps/winter | 256 ------------------- fastplotlib/utils/functions.py | 74 ++---- fastplotlib/utils/generate_colormaps.py | 126 --------- setup.py | 1 + 88 files changed, 25 insertions(+), 18498 deletions(-) delete mode 100644 fastplotlib/utils/colormaps/Accent delete mode 100644 fastplotlib/utils/colormaps/Blues delete mode 100644 fastplotlib/utils/colormaps/BrBG delete mode 100644 fastplotlib/utils/colormaps/BuGn delete mode 100644 fastplotlib/utils/colormaps/BuPu delete mode 100644 fastplotlib/utils/colormaps/CMRmap delete mode 100644 fastplotlib/utils/colormaps/Dark2 delete mode 100644 fastplotlib/utils/colormaps/GnBu delete mode 100644 fastplotlib/utils/colormaps/Greens delete mode 100644 fastplotlib/utils/colormaps/Greys delete mode 100644 fastplotlib/utils/colormaps/OrRd delete mode 100644 fastplotlib/utils/colormaps/Oranges delete mode 100644 fastplotlib/utils/colormaps/PRGn delete mode 100644 fastplotlib/utils/colormaps/Paired delete mode 100644 fastplotlib/utils/colormaps/Pastel1 delete mode 100644 fastplotlib/utils/colormaps/Pastel2 delete mode 100644 fastplotlib/utils/colormaps/PiYG delete mode 100644 fastplotlib/utils/colormaps/PuBu delete mode 100644 fastplotlib/utils/colormaps/PuBuGn delete mode 100644 fastplotlib/utils/colormaps/PuOr delete mode 100644 fastplotlib/utils/colormaps/PuRd delete mode 100644 fastplotlib/utils/colormaps/Purples delete mode 100644 fastplotlib/utils/colormaps/RdBu delete mode 100644 fastplotlib/utils/colormaps/RdGy delete mode 100644 fastplotlib/utils/colormaps/RdPu delete mode 100644 fastplotlib/utils/colormaps/RdYlBu delete mode 100644 fastplotlib/utils/colormaps/RdYlGn delete mode 100644 fastplotlib/utils/colormaps/Reds delete mode 100644 fastplotlib/utils/colormaps/Set1 delete mode 100644 fastplotlib/utils/colormaps/Set2 delete mode 100644 fastplotlib/utils/colormaps/Set3 delete mode 100644 fastplotlib/utils/colormaps/Spectral delete mode 100644 fastplotlib/utils/colormaps/Wistia delete mode 100644 fastplotlib/utils/colormaps/YlGn delete mode 100644 fastplotlib/utils/colormaps/YlGnBu delete mode 100644 fastplotlib/utils/colormaps/YlOrBr delete mode 100644 fastplotlib/utils/colormaps/YlOrRd delete mode 100644 fastplotlib/utils/colormaps/__init__.py delete mode 100644 fastplotlib/utils/colormaps/afmhot delete mode 100644 fastplotlib/utils/colormaps/autumn delete mode 100644 fastplotlib/utils/colormaps/binary delete mode 100644 fastplotlib/utils/colormaps/bone delete mode 100644 fastplotlib/utils/colormaps/brg delete mode 100644 fastplotlib/utils/colormaps/bwr delete mode 100644 fastplotlib/utils/colormaps/cividis delete mode 100644 fastplotlib/utils/colormaps/cool delete mode 100644 fastplotlib/utils/colormaps/coolwarm delete mode 100644 fastplotlib/utils/colormaps/copper delete mode 100644 fastplotlib/utils/colormaps/cubehelix delete mode 100644 fastplotlib/utils/colormaps/flag delete mode 100644 fastplotlib/utils/colormaps/gist_earth delete mode 100644 fastplotlib/utils/colormaps/gist_gray delete mode 100644 fastplotlib/utils/colormaps/gist_heat delete mode 100644 fastplotlib/utils/colormaps/gist_ncar delete mode 100644 fastplotlib/utils/colormaps/gist_rainbow delete mode 100644 fastplotlib/utils/colormaps/gist_stern delete mode 100644 fastplotlib/utils/colormaps/gist_yarg delete mode 100644 fastplotlib/utils/colormaps/gnuplot delete mode 100644 fastplotlib/utils/colormaps/gnuplot2 delete mode 100644 fastplotlib/utils/colormaps/gray delete mode 100644 fastplotlib/utils/colormaps/hot delete mode 100644 fastplotlib/utils/colormaps/hsv delete mode 100644 fastplotlib/utils/colormaps/inferno delete mode 100644 fastplotlib/utils/colormaps/jet delete mode 100644 fastplotlib/utils/colormaps/magma delete mode 100644 fastplotlib/utils/colormaps/nipy_spectral delete mode 100644 fastplotlib/utils/colormaps/ocean delete mode 100644 fastplotlib/utils/colormaps/pink delete mode 100644 fastplotlib/utils/colormaps/plasma delete mode 100644 fastplotlib/utils/colormaps/prism delete mode 100644 fastplotlib/utils/colormaps/rainbow delete mode 100644 fastplotlib/utils/colormaps/seismic delete mode 100644 fastplotlib/utils/colormaps/spring delete mode 100644 fastplotlib/utils/colormaps/summer delete mode 100644 fastplotlib/utils/colormaps/tab10 delete mode 100644 fastplotlib/utils/colormaps/tab20 delete mode 100644 fastplotlib/utils/colormaps/tab20b delete mode 100644 fastplotlib/utils/colormaps/tab20c delete mode 100644 fastplotlib/utils/colormaps/terrain delete mode 100644 fastplotlib/utils/colormaps/turbo delete mode 100644 fastplotlib/utils/colormaps/twilight delete mode 100644 fastplotlib/utils/colormaps/twilight_shifted delete mode 100644 fastplotlib/utils/colormaps/viridis delete mode 100644 fastplotlib/utils/colormaps/winter delete mode 100644 fastplotlib/utils/generate_colormaps.py diff --git a/docs/source/conf.py b/docs/source/conf.py index 68eb728a3..64c05b82c 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -101,7 +101,7 @@ intersphinx_mapping = { "python": ("https://docs.python.org/3", None), "numpy": ("https://numpy.org/doc/stable/", None), - "pygfx": ("https://pygfx.com/stable", None), + "pygfx": ("https://pygfx.org/stable", None), "wgpu": ("https://wgpu-py.readthedocs.io/en/latest", None), "fastplotlib": ("https://fastplotlib.readthedocs.io/en/latest/", None), } diff --git a/fastplotlib/utils/colormaps/Accent b/fastplotlib/utils/colormaps/Accent deleted file mode 100644 index 3bc12ed62..000000000 --- a/fastplotlib/utils/colormaps/Accent +++ /dev/null @@ -1,8 +0,0 @@ -4.980392158031463623e-01 7.882353067398071289e-01 4.980392158031463623e-01 1.000000000000000000e+00 -7.450980544090270996e-01 6.823529601097106934e-01 8.313725590705871582e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.529411911964416504e-01 5.254902243614196777e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 6.000000238418579102e-01 1.000000000000000000e+00 -2.196078449487686157e-01 4.235294163227081299e-01 6.901960968971252441e-01 1.000000000000000000e+00 -9.411764740943908691e-01 7.843137718737125397e-03 4.980392158031463623e-01 1.000000000000000000e+00 -7.490196228027343750e-01 3.568627536296844482e-01 9.019608050584793091e-02 1.000000000000000000e+00 -4.000000059604644775e-01 4.000000059604644775e-01 4.000000059604644775e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/Blues b/fastplotlib/utils/colormaps/Blues deleted file mode 100644 index e1684d87a..000000000 --- a/fastplotlib/utils/colormaps/Blues +++ /dev/null @@ -1,256 +0,0 @@ -9.686274528503417969e-01 9.843137264251708984e-01 1.000000000000000000e+00 1.000000000000000000e+00 -9.655517339706420898e-01 9.823452234268188477e-01 9.990157485008239746e-01 1.000000000000000000e+00 -9.624759554862976074e-01 9.803767800331115723e-01 9.980314970016479492e-01 1.000000000000000000e+00 -9.594002366065979004e-01 9.784082770347595215e-01 9.970473051071166992e-01 1.000000000000000000e+00 -9.563245177268981934e-01 9.764398336410522461e-01 9.960630536079406738e-01 1.000000000000000000e+00 -9.532487392425537109e-01 9.744713306427001953e-01 9.950788021087646484e-01 1.000000000000000000e+00 -9.501730203628540039e-01 9.725028872489929199e-01 9.940945506095886230e-01 1.000000000000000000e+00 -9.470972418785095215e-01 9.705343842506408691e-01 9.931103587150573730e-01 1.000000000000000000e+00 -9.440215229988098145e-01 9.685659408569335938e-01 9.921261072158813477e-01 1.000000000000000000e+00 -9.409458041191101074e-01 9.665974378585815430e-01 9.911418557167053223e-01 1.000000000000000000e+00 -9.378700256347656250e-01 9.646289944648742676e-01 9.901576042175292969e-01 1.000000000000000000e+00 -9.347943067550659180e-01 9.626604914665222168e-01 9.891734123229980469e-01 1.000000000000000000e+00 -9.317185878753662109e-01 9.606920480728149414e-01 9.881891608238220215e-01 1.000000000000000000e+00 -9.286428093910217285e-01 9.587235450744628906e-01 9.872049093246459961e-01 1.000000000000000000e+00 -9.255670905113220215e-01 9.567551016807556152e-01 9.862206578254699707e-01 1.000000000000000000e+00 -9.224913716316223145e-01 9.547865986824035645e-01 9.852364659309387207e-01 1.000000000000000000e+00 -9.194155931472778320e-01 9.528181552886962891e-01 9.842522144317626953e-01 1.000000000000000000e+00 -9.163398742675781250e-01 9.508496522903442383e-01 9.832679629325866699e-01 1.000000000000000000e+00 -9.132641553878784180e-01 9.488812088966369629e-01 9.822837114334106445e-01 1.000000000000000000e+00 -9.101883769035339355e-01 9.469127058982849121e-01 9.812995195388793945e-01 1.000000000000000000e+00 -9.071126580238342285e-01 9.449442625045776367e-01 9.803152680397033691e-01 1.000000000000000000e+00 -9.040368795394897461e-01 9.429757595062255859e-01 9.793310165405273438e-01 1.000000000000000000e+00 -9.009611606597900391e-01 9.410073161125183105e-01 9.783467650413513184e-01 1.000000000000000000e+00 -8.978854417800903320e-01 9.390388131141662598e-01 9.773625731468200684e-01 1.000000000000000000e+00 -8.948096632957458496e-01 9.370703697204589844e-01 9.763783216476440430e-01 1.000000000000000000e+00 -8.917339444160461426e-01 9.351018667221069336e-01 9.753940701484680176e-01 1.000000000000000000e+00 -8.886582255363464355e-01 9.331334233283996582e-01 9.744098186492919922e-01 1.000000000000000000e+00 -8.855824470520019531e-01 9.311649203300476074e-01 9.734256267547607422e-01 1.000000000000000000e+00 -8.825067281723022461e-01 9.291964769363403320e-01 9.724413752555847168e-01 1.000000000000000000e+00 -8.794310092926025391e-01 9.272279739379882812e-01 9.714571237564086914e-01 1.000000000000000000e+00 -8.763552308082580566e-01 9.252595305442810059e-01 9.704728722572326660e-01 1.000000000000000000e+00 -8.732795119285583496e-01 9.232910275459289551e-01 9.694886803627014160e-01 1.000000000000000000e+00 -8.702191710472106934e-01 9.213225841522216797e-01 9.685044288635253906e-01 1.000000000000000000e+00 -8.672664165496826172e-01 9.193540811538696289e-01 9.675201773643493652e-01 1.000000000000000000e+00 -8.643137216567993164e-01 9.173856377601623535e-01 9.665359258651733398e-01 1.000000000000000000e+00 -8.613610267639160156e-01 9.154171347618103027e-01 9.655517339706420898e-01 1.000000000000000000e+00 -8.584083318710327148e-01 9.134486913681030273e-01 9.645674824714660645e-01 1.000000000000000000e+00 -8.554555773735046387e-01 9.114801883697509766e-01 9.635832309722900391e-01 1.000000000000000000e+00 -8.525028824806213379e-01 9.095117449760437012e-01 9.625989794731140137e-01 1.000000000000000000e+00 -8.495501875877380371e-01 9.075432419776916504e-01 9.616147875785827637e-01 1.000000000000000000e+00 -8.465974330902099609e-01 9.055747985839843750e-01 9.606305360794067383e-01 1.000000000000000000e+00 -8.436447381973266602e-01 9.036062955856323242e-01 9.596462845802307129e-01 1.000000000000000000e+00 -8.406920433044433594e-01 9.016378521919250488e-01 9.586620330810546875e-01 1.000000000000000000e+00 -8.377393484115600586e-01 8.996693491935729980e-01 9.576778411865234375e-01 1.000000000000000000e+00 -8.347865939140319824e-01 8.977009057998657227e-01 9.566935896873474121e-01 1.000000000000000000e+00 -8.318338990211486816e-01 8.957324028015136719e-01 9.557093381881713867e-01 1.000000000000000000e+00 -8.288812041282653809e-01 8.937639594078063965e-01 9.547250866889953613e-01 1.000000000000000000e+00 -8.259285092353820801e-01 8.917954564094543457e-01 9.537408947944641113e-01 1.000000000000000000e+00 -8.229757547378540039e-01 8.898270130157470703e-01 9.527566432952880859e-01 1.000000000000000000e+00 -8.200230598449707031e-01 8.878585100173950195e-01 9.517723917961120605e-01 1.000000000000000000e+00 -8.170703649520874023e-01 8.858900666236877441e-01 9.507881402969360352e-01 1.000000000000000000e+00 -8.141176700592041016e-01 8.839215636253356934e-01 9.498039484024047852e-01 1.000000000000000000e+00 -8.111649155616760254e-01 8.819531202316284180e-01 9.488196969032287598e-01 1.000000000000000000e+00 -8.082122206687927246e-01 8.799846172332763672e-01 9.478354454040527344e-01 1.000000000000000000e+00 -8.052595257759094238e-01 8.780161738395690918e-01 9.468511939048767090e-01 1.000000000000000000e+00 -8.023068308830261230e-01 8.760476708412170410e-01 9.458670020103454590e-01 1.000000000000000000e+00 -7.993540763854980469e-01 8.740792274475097656e-01 9.448827505111694336e-01 1.000000000000000000e+00 -7.964013814926147461e-01 8.721107244491577148e-01 9.438984990119934082e-01 1.000000000000000000e+00 -7.934486865997314453e-01 8.701422810554504395e-01 9.429142475128173828e-01 1.000000000000000000e+00 -7.904959917068481445e-01 8.681737780570983887e-01 9.419300556182861328e-01 1.000000000000000000e+00 -7.875432372093200684e-01 8.662053346633911133e-01 9.409458041191101074e-01 1.000000000000000000e+00 -7.845905423164367676e-01 8.642368316650390625e-01 9.399615526199340820e-01 1.000000000000000000e+00 -7.816378474235534668e-01 8.622683286666870117e-01 9.389773011207580566e-01 1.000000000000000000e+00 -7.786850929260253906e-01 8.602998852729797363e-01 9.379931092262268066e-01 1.000000000000000000e+00 -7.752403020858764648e-01 8.583006262779235840e-01 9.368243217468261719e-01 1.000000000000000000e+00 -7.703191041946411133e-01 8.562091588973999023e-01 9.351018667221069336e-01 1.000000000000000000e+00 -7.653979063034057617e-01 8.541176319122314453e-01 9.333794713020324707e-01 1.000000000000000000e+00 -7.604767680168151855e-01 8.520261645317077637e-01 9.316570758819580078e-01 1.000000000000000000e+00 -7.555555701255798340e-01 8.499346375465393066e-01 9.299346208572387695e-01 1.000000000000000000e+00 -7.506343722343444824e-01 8.478431105613708496e-01 9.282122254371643066e-01 1.000000000000000000e+00 -7.457131743431091309e-01 8.457516431808471680e-01 9.264898300170898438e-01 1.000000000000000000e+00 -7.407919764518737793e-01 8.436601161956787109e-01 9.247673749923706055e-01 1.000000000000000000e+00 -7.358708381652832031e-01 8.415686488151550293e-01 9.230449795722961426e-01 1.000000000000000000e+00 -7.309496402740478516e-01 8.394771218299865723e-01 9.213225841522216797e-01 1.000000000000000000e+00 -7.260284423828125000e-01 8.373855948448181152e-01 9.196001291275024414e-01 1.000000000000000000e+00 -7.211072444915771484e-01 8.352941274642944336e-01 9.178777337074279785e-01 1.000000000000000000e+00 -7.161861062049865723e-01 8.332026004791259766e-01 9.161553382873535156e-01 1.000000000000000000e+00 -7.112649083137512207e-01 8.311111330986022949e-01 9.144328832626342773e-01 1.000000000000000000e+00 -7.063437104225158691e-01 8.290196061134338379e-01 9.127104878425598145e-01 1.000000000000000000e+00 -7.014225125312805176e-01 8.269280791282653809e-01 9.109880924224853516e-01 1.000000000000000000e+00 -6.965013742446899414e-01 8.248366117477416992e-01 9.092656373977661133e-01 1.000000000000000000e+00 -6.915801763534545898e-01 8.227450847625732422e-01 9.075432419776916504e-01 1.000000000000000000e+00 -6.866589784622192383e-01 8.206536173820495605e-01 9.058208465576171875e-01 1.000000000000000000e+00 -6.817377805709838867e-01 8.185620903968811035e-01 9.040984511375427246e-01 1.000000000000000000e+00 -6.768165826797485352e-01 8.164705634117126465e-01 9.023759961128234863e-01 1.000000000000000000e+00 -6.718954443931579590e-01 8.143790960311889648e-01 9.006536006927490234e-01 1.000000000000000000e+00 -6.669742465019226074e-01 8.122875690460205078e-01 8.989312052726745605e-01 1.000000000000000000e+00 -6.620530486106872559e-01 8.101961016654968262e-01 8.972087502479553223e-01 1.000000000000000000e+00 -6.571318507194519043e-01 8.081045746803283691e-01 8.954863548278808594e-01 1.000000000000000000e+00 -6.522107124328613281e-01 8.060130476951599121e-01 8.937639594078063965e-01 1.000000000000000000e+00 -6.472895145416259766e-01 8.039215803146362305e-01 8.920415043830871582e-01 1.000000000000000000e+00 -6.423683166503906250e-01 8.018300533294677734e-01 8.903191089630126953e-01 1.000000000000000000e+00 -6.374471187591552734e-01 7.997385859489440918e-01 8.885967135429382324e-01 1.000000000000000000e+00 -6.325259804725646973e-01 7.976470589637756348e-01 8.868742585182189941e-01 1.000000000000000000e+00 -6.276047825813293457e-01 7.955555319786071777e-01 8.851518630981445312e-01 1.000000000000000000e+00 -6.226835846900939941e-01 7.934640645980834961e-01 8.834294676780700684e-01 1.000000000000000000e+00 -6.172549128532409668e-01 7.908650636672973633e-01 8.818454146385192871e-01 1.000000000000000000e+00 -6.109803915023803711e-01 7.874202132225036621e-01 8.804920911788940430e-01 1.000000000000000000e+00 -6.047058701515197754e-01 7.839754223823547363e-01 8.791387677192687988e-01 1.000000000000000000e+00 -5.984313488006591797e-01 7.805305719375610352e-01 8.777854442596435547e-01 1.000000000000000000e+00 -5.921568870544433594e-01 7.770857214927673340e-01 8.764321208000183105e-01 1.000000000000000000e+00 -5.858823657035827637e-01 7.736409306526184082e-01 8.750787973403930664e-01 1.000000000000000000e+00 -5.796078443527221680e-01 7.701960802078247070e-01 8.737254738807678223e-01 1.000000000000000000e+00 -5.733333230018615723e-01 7.667512297630310059e-01 8.723721504211425781e-01 1.000000000000000000e+00 -5.670588016510009766e-01 7.633064389228820801e-01 8.710188269615173340e-01 1.000000000000000000e+00 -5.607843399047851562e-01 7.598615884780883789e-01 8.696655035018920898e-01 1.000000000000000000e+00 -5.545098185539245605e-01 7.564167380332946777e-01 8.683121800422668457e-01 1.000000000000000000e+00 -5.482352972030639648e-01 7.529719471931457520e-01 8.669588565826416016e-01 1.000000000000000000e+00 -5.419607758522033691e-01 7.495270967483520508e-01 8.656055331230163574e-01 1.000000000000000000e+00 -5.356862545013427734e-01 7.460822463035583496e-01 8.642522096633911133e-01 1.000000000000000000e+00 -5.294117927551269531e-01 7.426374554634094238e-01 8.628988862037658691e-01 1.000000000000000000e+00 -5.231372714042663574e-01 7.391926050186157227e-01 8.615455627441406250e-01 1.000000000000000000e+00 -5.168627500534057617e-01 7.357478141784667969e-01 8.601922392845153809e-01 1.000000000000000000e+00 -5.105882287025451660e-01 7.323029637336730957e-01 8.588389158248901367e-01 1.000000000000000000e+00 -5.043137073516845703e-01 7.288581132888793945e-01 8.574855923652648926e-01 1.000000000000000000e+00 -4.980392158031463623e-01 7.254133224487304688e-01 8.561322689056396484e-01 1.000000000000000000e+00 -4.917646944522857666e-01 7.219684720039367676e-01 8.547789454460144043e-01 1.000000000000000000e+00 -4.854902029037475586e-01 7.185236215591430664e-01 8.534256219863891602e-01 1.000000000000000000e+00 -4.792156815528869629e-01 7.150788307189941406e-01 8.520722985267639160e-01 1.000000000000000000e+00 -4.729411900043487549e-01 7.116339802742004395e-01 8.507189750671386719e-01 1.000000000000000000e+00 -4.666666686534881592e-01 7.081891298294067383e-01 8.493656516075134277e-01 1.000000000000000000e+00 -4.603921473026275635e-01 7.047443389892578125e-01 8.480123281478881836e-01 1.000000000000000000e+00 -4.541176557540893555e-01 7.012994885444641113e-01 8.466590046882629395e-01 1.000000000000000000e+00 -4.478431344032287598e-01 6.978546977043151855e-01 8.453056812286376953e-01 1.000000000000000000e+00 -4.415686130523681641e-01 6.944098472595214844e-01 8.439522981643676758e-01 1.000000000000000000e+00 -4.352941215038299561e-01 6.909649968147277832e-01 8.425989747047424316e-01 1.000000000000000000e+00 -4.290196001529693604e-01 6.875202059745788574e-01 8.412456512451171875e-01 1.000000000000000000e+00 -4.227451086044311523e-01 6.840753555297851562e-01 8.398923277854919434e-01 1.000000000000000000e+00 -4.170857369899749756e-01 6.806305050849914551e-01 8.382314443588256836e-01 1.000000000000000000e+00 -4.120415151119232178e-01 6.771857142448425293e-01 8.362630009651184082e-01 1.000000000000000000e+00 -4.069973230361938477e-01 6.737408638000488281e-01 8.342944979667663574e-01 1.000000000000000000e+00 -4.019531011581420898e-01 6.702960133552551270e-01 8.323260545730590820e-01 1.000000000000000000e+00 -3.969088792800903320e-01 6.668512225151062012e-01 8.303575515747070312e-01 1.000000000000000000e+00 -3.918646574020385742e-01 6.634063720703125000e-01 8.283891081809997559e-01 1.000000000000000000e+00 -3.868204653263092041e-01 6.599615812301635742e-01 8.264206051826477051e-01 1.000000000000000000e+00 -3.817762434482574463e-01 6.565167307853698730e-01 8.244521617889404297e-01 1.000000000000000000e+00 -3.767320215702056885e-01 6.530718803405761719e-01 8.224836587905883789e-01 1.000000000000000000e+00 -3.716877996921539307e-01 6.496270895004272461e-01 8.205152153968811035e-01 1.000000000000000000e+00 -3.666436076164245605e-01 6.461822390556335449e-01 8.185467123985290527e-01 1.000000000000000000e+00 -3.615993857383728027e-01 6.427373886108398438e-01 8.165782094001770020e-01 1.000000000000000000e+00 -3.565551638603210449e-01 6.392925977706909180e-01 8.146097660064697266e-01 1.000000000000000000e+00 -3.515109717845916748e-01 6.358477473258972168e-01 8.126412630081176758e-01 1.000000000000000000e+00 -3.464667499065399170e-01 6.324028968811035156e-01 8.106728196144104004e-01 1.000000000000000000e+00 -3.414225280284881592e-01 6.289581060409545898e-01 8.087043166160583496e-01 1.000000000000000000e+00 -3.363783061504364014e-01 6.255132555961608887e-01 8.067358732223510742e-01 1.000000000000000000e+00 -3.313341140747070312e-01 6.220684647560119629e-01 8.047673702239990234e-01 1.000000000000000000e+00 -3.262898921966552734e-01 6.186236143112182617e-01 8.027989268302917480e-01 1.000000000000000000e+00 -3.212456703186035156e-01 6.151787638664245605e-01 8.008304238319396973e-01 1.000000000000000000e+00 -3.162014484405517578e-01 6.117339730262756348e-01 7.988619804382324219e-01 1.000000000000000000e+00 -3.111572563648223877e-01 6.082891225814819336e-01 7.968934774398803711e-01 1.000000000000000000e+00 -3.061130344867706299e-01 6.048442721366882324e-01 7.949250340461730957e-01 1.000000000000000000e+00 -3.010688126087188721e-01 6.013994812965393066e-01 7.929565310478210449e-01 1.000000000000000000e+00 -2.960246205329895020e-01 5.979546308517456055e-01 7.909880876541137695e-01 1.000000000000000000e+00 -2.909803986549377441e-01 5.945097804069519043e-01 7.890195846557617188e-01 1.000000000000000000e+00 -2.859361767768859863e-01 5.910649895668029785e-01 7.870511412620544434e-01 1.000000000000000000e+00 -2.808919548988342285e-01 5.876201391220092773e-01 7.850826382637023926e-01 1.000000000000000000e+00 -2.758477628231048584e-01 5.841752886772155762e-01 7.831141948699951172e-01 1.000000000000000000e+00 -2.708035409450531006e-01 5.807304978370666504e-01 7.811456918716430664e-01 1.000000000000000000e+00 -2.657593190670013428e-01 5.772856473922729492e-01 7.791772484779357910e-01 1.000000000000000000e+00 -2.607150971889495850e-01 5.738408565521240234e-01 7.772087454795837402e-01 1.000000000000000000e+00 -2.562860548496246338e-01 5.700115561485290527e-01 7.751634120941162109e-01 1.000000000000000000e+00 -2.522260546684265137e-01 5.659515857696533203e-01 7.730718851089477539e-01 1.000000000000000000e+00 -2.481660842895507812e-01 5.618915557861328125e-01 7.709804177284240723e-01 1.000000000000000000e+00 -2.441061139106750488e-01 5.578315854072570801e-01 7.688888907432556152e-01 1.000000000000000000e+00 -2.400461435317993164e-01 5.537716150283813477e-01 7.667973637580871582e-01 1.000000000000000000e+00 -2.359861582517623901e-01 5.497116446495056152e-01 7.647058963775634766e-01 1.000000000000000000e+00 -2.319261878728866577e-01 5.456516742706298828e-01 7.626143693923950195e-01 1.000000000000000000e+00 -2.278662025928497314e-01 5.415917038917541504e-01 7.605229020118713379e-01 1.000000000000000000e+00 -2.238062322139739990e-01 5.375317335128784180e-01 7.584313750267028809e-01 1.000000000000000000e+00 -2.197462469339370728e-01 5.334717631340026855e-01 7.563398480415344238e-01 1.000000000000000000e+00 -2.156862765550613403e-01 5.294117927551269531e-01 7.542483806610107422e-01 1.000000000000000000e+00 -2.116262912750244141e-01 5.253517627716064453e-01 7.521568536758422852e-01 1.000000000000000000e+00 -2.075663208961486816e-01 5.212917923927307129e-01 7.500653862953186035e-01 1.000000000000000000e+00 -2.035063505172729492e-01 5.172318220138549805e-01 7.479738593101501465e-01 1.000000000000000000e+00 -1.994463652372360229e-01 5.131718516349792480e-01 7.458823323249816895e-01 1.000000000000000000e+00 -1.953863948583602905e-01 5.091118812561035156e-01 7.437908649444580078e-01 1.000000000000000000e+00 -1.913264095783233643e-01 5.050519108772277832e-01 7.416993379592895508e-01 1.000000000000000000e+00 -1.872664391994476318e-01 5.009919404983520508e-01 7.396078705787658691e-01 1.000000000000000000e+00 -1.832064539194107056e-01 4.969319403171539307e-01 7.375163435935974121e-01 1.000000000000000000e+00 -1.791464835405349731e-01 4.928719699382781982e-01 7.354248166084289551e-01 1.000000000000000000e+00 -1.750864982604980469e-01 4.888119995594024658e-01 7.333333492279052734e-01 1.000000000000000000e+00 -1.710265278816223145e-01 4.847520291805267334e-01 7.312418222427368164e-01 1.000000000000000000e+00 -1.669665575027465820e-01 4.806920289993286133e-01 7.291503548622131348e-01 1.000000000000000000e+00 -1.629065722227096558e-01 4.766320586204528809e-01 7.270588278770446777e-01 1.000000000000000000e+00 -1.588466018438339233e-01 4.725720882415771484e-01 7.249673008918762207e-01 1.000000000000000000e+00 -1.547866165637969971e-01 4.685121178627014160e-01 7.228758335113525391e-01 1.000000000000000000e+00 -1.507266461849212646e-01 4.644521474838256836e-01 7.207843065261840820e-01 1.000000000000000000e+00 -1.466666609048843384e-01 4.603921473026275635e-01 7.186928391456604004e-01 1.000000000000000000e+00 -1.426066905260086060e-01 4.563321769237518311e-01 7.166013121604919434e-01 1.000000000000000000e+00 -1.385467201471328735e-01 4.522722065448760986e-01 7.145097851753234863e-01 1.000000000000000000e+00 -1.344867348670959473e-01 4.482122361660003662e-01 7.124183177947998047e-01 1.000000000000000000e+00 -1.304267644882202148e-01 4.441522359848022461e-01 7.103267908096313477e-01 1.000000000000000000e+00 -1.271049529314041138e-01 4.401845335960388184e-01 7.074971199035644531e-01 1.000000000000000000e+00 -1.240292191505432129e-01 4.362475872039794922e-01 7.044214010238647461e-01 1.000000000000000000e+00 -1.209534779191017151e-01 4.323106408119201660e-01 7.013456225395202637e-01 1.000000000000000000e+00 -1.178777366876602173e-01 4.283736944198608398e-01 6.982699036598205566e-01 1.000000000000000000e+00 -1.148020029067993164e-01 4.244367480278015137e-01 6.951941847801208496e-01 1.000000000000000000e+00 -1.117262616753578186e-01 4.204998016357421875e-01 6.921184062957763672e-01 1.000000000000000000e+00 -1.086505204439163208e-01 4.165628552436828613e-01 6.890426874160766602e-01 1.000000000000000000e+00 -1.055747792124748230e-01 4.126259088516235352e-01 6.859669089317321777e-01 1.000000000000000000e+00 -1.024990379810333252e-01 4.086889624595642090e-01 6.828911900520324707e-01 1.000000000000000000e+00 -9.942329674959182739e-02 4.047520160675048828e-01 6.798154711723327637e-01 1.000000000000000000e+00 -9.634755551815032959e-02 4.008150696754455566e-01 6.767396926879882812e-01 1.000000000000000000e+00 -9.327182173728942871e-02 3.968781232833862305e-01 6.736639738082885742e-01 1.000000000000000000e+00 -9.019608050584793091e-02 3.929411768913269043e-01 6.705882549285888672e-01 1.000000000000000000e+00 -8.712033927440643311e-02 3.890042304992675781e-01 6.675124764442443848e-01 1.000000000000000000e+00 -8.404459804296493530e-02 3.850672841072082520e-01 6.644367575645446777e-01 1.000000000000000000e+00 -8.096885681152343750e-02 3.811303377151489258e-01 6.613610386848449707e-01 1.000000000000000000e+00 -7.789311558008193970e-02 3.771933913230895996e-01 6.582852602005004883e-01 1.000000000000000000e+00 -7.481737434864044189e-02 3.732564449310302734e-01 6.552095413208007812e-01 1.000000000000000000e+00 -7.174164056777954102e-02 3.693194985389709473e-01 6.521338224411010742e-01 1.000000000000000000e+00 -6.866589933633804321e-02 3.653825521469116211e-01 6.490580439567565918e-01 1.000000000000000000e+00 -6.559015810489654541e-02 3.614456057548522949e-01 6.459823250770568848e-01 1.000000000000000000e+00 -6.251441687345504761e-02 3.575086593627929688e-01 6.429065465927124023e-01 1.000000000000000000e+00 -5.943867564201354980e-02 3.535717129707336426e-01 6.398308277130126953e-01 1.000000000000000000e+00 -5.636293813586235046e-02 3.496347665786743164e-01 6.367551088333129883e-01 1.000000000000000000e+00 -5.328719690442085266e-02 3.456978201866149902e-01 6.336793303489685059e-01 1.000000000000000000e+00 -5.021145567297935486e-02 3.417608737945556641e-01 6.306036114692687988e-01 1.000000000000000000e+00 -4.713571816682815552e-02 3.378239274024963379e-01 6.275278925895690918e-01 1.000000000000000000e+00 -4.405997693538665771e-02 3.338869810104370117e-01 6.244521141052246094e-01 1.000000000000000000e+00 -4.098423570394515991e-02 3.299500048160552979e-01 6.213763952255249023e-01 1.000000000000000000e+00 -3.790849819779396057e-02 3.260130584239959717e-01 6.183006763458251953e-01 1.000000000000000000e+00 -3.483275696635246277e-02 3.220761120319366455e-01 6.152248978614807129e-01 1.000000000000000000e+00 -3.175701573491096497e-02 3.181391656398773193e-01 6.121491789817810059e-01 1.000000000000000000e+00 -3.137255087494850159e-02 3.140945732593536377e-01 6.064898371696472168e-01 1.000000000000000000e+00 -3.137255087494850159e-02 3.100346028804779053e-01 6.004613637924194336e-01 1.000000000000000000e+00 -3.137255087494850159e-02 3.059746325016021729e-01 5.944328904151916504e-01 1.000000000000000000e+00 -3.137255087494850159e-02 3.019146621227264404e-01 5.884044766426086426e-01 1.000000000000000000e+00 -3.137255087494850159e-02 2.978546619415283203e-01 5.823760032653808594e-01 1.000000000000000000e+00 -3.137255087494850159e-02 2.937946915626525879e-01 5.763475298881530762e-01 1.000000000000000000e+00 -3.137255087494850159e-02 2.897347211837768555e-01 5.703191161155700684e-01 1.000000000000000000e+00 -3.137255087494850159e-02 2.856747508049011230e-01 5.642906427383422852e-01 1.000000000000000000e+00 -3.137255087494850159e-02 2.816147506237030029e-01 5.582622289657592773e-01 1.000000000000000000e+00 -3.137255087494850159e-02 2.775547802448272705e-01 5.522337555885314941e-01 1.000000000000000000e+00 -3.137255087494850159e-02 2.734948098659515381e-01 5.462052822113037109e-01 1.000000000000000000e+00 -3.137255087494850159e-02 2.694348394870758057e-01 5.401768684387207031e-01 1.000000000000000000e+00 -3.137255087494850159e-02 2.653748691082000732e-01 5.341483950614929199e-01 1.000000000000000000e+00 -3.137255087494850159e-02 2.613148689270019531e-01 5.281199812889099121e-01 1.000000000000000000e+00 -3.137255087494850159e-02 2.572548985481262207e-01 5.220915079116821289e-01 1.000000000000000000e+00 -3.137255087494850159e-02 2.531949281692504883e-01 5.160630345344543457e-01 1.000000000000000000e+00 -3.137255087494850159e-02 2.491349428892135620e-01 5.100346207618713379e-01 1.000000000000000000e+00 -3.137255087494850159e-02 2.450749725103378296e-01 5.040061473846435547e-01 1.000000000000000000e+00 -3.137255087494850159e-02 2.410149872303009033e-01 4.979777038097381592e-01 1.000000000000000000e+00 -3.137255087494850159e-02 2.369550168514251709e-01 4.919492602348327637e-01 1.000000000000000000e+00 -3.137255087494850159e-02 2.328950464725494385e-01 4.859207868576049805e-01 1.000000000000000000e+00 -3.137255087494850159e-02 2.288350611925125122e-01 4.798923432826995850e-01 1.000000000000000000e+00 -3.137255087494850159e-02 2.247750908136367798e-01 4.738638997077941895e-01 1.000000000000000000e+00 -3.137255087494850159e-02 2.207151055335998535e-01 4.678354561328887939e-01 1.000000000000000000e+00 -3.137255087494850159e-02 2.166551351547241211e-01 4.618069827556610107e-01 1.000000000000000000e+00 -3.137255087494850159e-02 2.125951498746871948e-01 4.557785391807556152e-01 1.000000000000000000e+00 -3.137255087494850159e-02 2.085351794958114624e-01 4.497500956058502197e-01 1.000000000000000000e+00 -3.137255087494850159e-02 2.044752091169357300e-01 4.437216520309448242e-01 1.000000000000000000e+00 -3.137255087494850159e-02 2.004152238368988037e-01 4.376932084560394287e-01 1.000000000000000000e+00 -3.137255087494850159e-02 1.963552534580230713e-01 4.316647350788116455e-01 1.000000000000000000e+00 -3.137255087494850159e-02 1.922952681779861450e-01 4.256362915039062500e-01 1.000000000000000000e+00 -3.137255087494850159e-02 1.882352977991104126e-01 4.196078479290008545e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/BrBG b/fastplotlib/utils/colormaps/BrBG deleted file mode 100644 index b737a5d04..000000000 --- a/fastplotlib/utils/colormaps/BrBG +++ /dev/null @@ -1,256 +0,0 @@ -3.294117748737335205e-01 1.882352977991104126e-01 1.960784383118152618e-02 1.000000000000000000e+00 -3.380238413810729980e-01 1.933102607727050781e-01 2.037677727639675140e-02 1.000000000000000000e+00 -3.466359078884124756e-01 1.983852386474609375e-01 2.114571258425712585e-02 1.000000000000000000e+00 -3.552479743957519531e-01 2.034602016210556030e-01 2.191464789211750031e-02 1.000000000000000000e+00 -3.638600409030914307e-01 2.085351794958114624e-01 2.268358319997787476e-02 1.000000000000000000e+00 -3.724721372127532959e-01 2.136101573705673218e-01 2.345251850783824921e-02 1.000000000000000000e+00 -3.810842037200927734e-01 2.186851203441619873e-01 2.422145381569862366e-02 1.000000000000000000e+00 -3.896962702274322510e-01 2.237600982189178467e-01 2.499038912355899811e-02 1.000000000000000000e+00 -3.983083367347717285e-01 2.288350611925125122e-01 2.575932256877422333e-02 1.000000000000000000e+00 -4.069204032421112061e-01 2.339100390672683716e-01 2.652825787663459778e-02 1.000000000000000000e+00 -4.155324995517730713e-01 2.389850020408630371e-01 2.729719318449497223e-02 1.000000000000000000e+00 -4.241445660591125488e-01 2.440599799156188965e-01 2.806612849235534668e-02 1.000000000000000000e+00 -4.327566325664520264e-01 2.491349428892135620e-01 2.883506380021572113e-02 1.000000000000000000e+00 -4.413686990737915039e-01 2.542099058628082275e-01 2.960399910807609558e-02 1.000000000000000000e+00 -4.499807655811309814e-01 2.592848837375640869e-01 3.037293441593647003e-02 1.000000000000000000e+00 -4.585928618907928467e-01 2.643598616123199463e-01 3.114186786115169525e-02 1.000000000000000000e+00 -4.672049283981323242e-01 2.694348394870758057e-01 3.191080316901206970e-02 1.000000000000000000e+00 -4.758169949054718018e-01 2.745098173618316650e-01 3.267974033951759338e-02 1.000000000000000000e+00 -4.844290614128112793e-01 2.795847654342651367e-01 3.344867378473281860e-02 1.000000000000000000e+00 -4.930411279201507568e-01 2.846597433090209961e-01 3.421760722994804382e-02 1.000000000000000000e+00 -5.016531944274902344e-01 2.897347211837768555e-01 3.498654440045356750e-02 1.000000000000000000e+00 -5.102652907371520996e-01 2.948096990585327148e-01 3.575547784566879272e-02 1.000000000000000000e+00 -5.188773274421691895e-01 2.998846471309661865e-01 3.652441501617431641e-02 1.000000000000000000e+00 -5.274894237518310547e-01 3.049596250057220459e-01 3.729334846138954163e-02 1.000000000000000000e+00 -5.361015200614929199e-01 3.100346028804779053e-01 3.806228190660476685e-02 1.000000000000000000e+00 -5.447135567665100098e-01 3.151095807552337646e-01 3.883121907711029053e-02 1.000000000000000000e+00 -5.529412031173706055e-01 3.213379383087158203e-01 4.190696030855178833e-02 1.000000000000000000e+00 -5.607843399047851562e-01 3.287197351455688477e-01 4.728950560092926025e-02 1.000000000000000000e+00 -5.686274766921997070e-01 3.361015021800994873e-01 5.267205089330673218e-02 1.000000000000000000e+00 -5.764706134796142578e-01 3.434832692146301270e-01 5.805459618568420410e-02 1.000000000000000000e+00 -5.843137502670288086e-01 3.508650660514831543e-01 6.343714147806167603e-02 1.000000000000000000e+00 -5.921568870544433594e-01 3.582468330860137939e-01 6.881968677043914795e-02 1.000000000000000000e+00 -6.000000238418579102e-01 3.656286001205444336e-01 7.420223206281661987e-02 1.000000000000000000e+00 -6.078431606292724609e-01 3.730103671550750732e-01 7.958477735519409180e-02 1.000000000000000000e+00 -6.156862974166870117e-01 3.803921639919281006e-01 8.496732264757156372e-02 1.000000000000000000e+00 -6.235294342041015625e-01 3.877739310264587402e-01 9.034986793994903564e-02 1.000000000000000000e+00 -6.313725709915161133e-01 3.951556980609893799e-01 9.573241323232650757e-02 1.000000000000000000e+00 -6.392157077789306641e-01 4.025374948978424072e-01 1.011149585247039795e-01 1.000000000000000000e+00 -6.470588445663452148e-01 4.099192619323730469e-01 1.064975038170814514e-01 1.000000000000000000e+00 -6.549019813537597656e-01 4.173010289669036865e-01 1.118800491094589233e-01 1.000000000000000000e+00 -6.627451181411743164e-01 4.246828258037567139e-01 1.172625944018363953e-01 1.000000000000000000e+00 -6.705882549285888672e-01 4.320645928382873535e-01 1.226451396942138672e-01 1.000000000000000000e+00 -6.784313917160034180e-01 4.394463598728179932e-01 1.280276775360107422e-01 1.000000000000000000e+00 -6.862745285034179688e-01 4.468281567096710205e-01 1.334102302789688110e-01 1.000000000000000000e+00 -6.941176652908325195e-01 4.542099237442016602e-01 1.387927681207656860e-01 1.000000000000000000e+00 -7.019608020782470703e-01 4.615916907787322998e-01 1.441753208637237549e-01 1.000000000000000000e+00 -7.098039388656616211e-01 4.689734578132629395e-01 1.495578587055206299e-01 1.000000000000000000e+00 -7.176470756530761719e-01 4.763552546501159668e-01 1.549404114484786987e-01 1.000000000000000000e+00 -7.254902124404907227e-01 4.837370216846466064e-01 1.603229492902755737e-01 1.000000000000000000e+00 -7.333333492279052734e-01 4.911187887191772461e-01 1.657055020332336426e-01 1.000000000000000000e+00 -7.411764860153198242e-01 4.985005855560302734e-01 1.710880398750305176e-01 1.000000000000000000e+00 -7.490196228027343750e-01 5.058823823928833008e-01 1.764705926179885864e-01 1.000000000000000000e+00 -7.539408206939697266e-01 5.158784985542297363e-01 1.887735426425933838e-01 1.000000000000000000e+00 -7.588619589805603027e-01 5.258746743202209473e-01 2.010765075683593750e-01 1.000000000000000000e+00 -7.637831568717956543e-01 5.358707904815673828e-01 2.133794724941253662e-01 1.000000000000000000e+00 -7.687043547630310059e-01 5.458669662475585938e-01 2.256824225187301636e-01 1.000000000000000000e+00 -7.736255526542663574e-01 5.558631420135498047e-01 2.379853874444961548e-01 1.000000000000000000e+00 -7.785466909408569336e-01 5.658592581748962402e-01 2.502883374691009521e-01 1.000000000000000000e+00 -7.834678888320922852e-01 5.758554339408874512e-01 2.625913023948669434e-01 1.000000000000000000e+00 -7.883890867233276367e-01 5.858516097068786621e-01 2.748942673206329346e-01 1.000000000000000000e+00 -7.933102846145629883e-01 5.958477258682250977e-01 2.871972322463989258e-01 1.000000000000000000e+00 -7.982314229011535645e-01 6.058439016342163086e-01 2.995001971721649170e-01 1.000000000000000000e+00 -8.031526207923889160e-01 6.158400774002075195e-01 3.118031620979309082e-01 1.000000000000000000e+00 -8.080738186836242676e-01 6.258361935615539551e-01 3.241061270236968994e-01 1.000000000000000000e+00 -8.129950165748596191e-01 6.358323693275451660e-01 3.364090621471405029e-01 1.000000000000000000e+00 -8.179162144660949707e-01 6.458285450935363770e-01 3.487120270729064941e-01 1.000000000000000000e+00 -8.228373527526855469e-01 6.558246612548828125e-01 3.610149919986724854e-01 1.000000000000000000e+00 -8.277585506439208984e-01 6.658208370208740234e-01 3.733179569244384766e-01 1.000000000000000000e+00 -8.326797485351562500e-01 6.758170127868652344e-01 3.856209218502044678e-01 1.000000000000000000e+00 -8.376009464263916016e-01 6.858131289482116699e-01 3.979238867759704590e-01 1.000000000000000000e+00 -8.425220847129821777e-01 6.958093047142028809e-01 4.102268218994140625e-01 1.000000000000000000e+00 -8.474432826042175293e-01 7.058054804801940918e-01 4.225297868251800537e-01 1.000000000000000000e+00 -8.523644804954528809e-01 7.158015966415405273e-01 4.348327517509460449e-01 1.000000000000000000e+00 -8.572856783866882324e-01 7.257977724075317383e-01 4.471357166767120361e-01 1.000000000000000000e+00 -8.622068166732788086e-01 7.357939481735229492e-01 4.594386816024780273e-01 1.000000000000000000e+00 -8.671280145645141602e-01 7.457900643348693848e-01 4.717416465282440186e-01 1.000000000000000000e+00 -8.720492124557495117e-01 7.557862401008605957e-01 4.840446114540100098e-01 1.000000000000000000e+00 -8.762783408164978027e-01 7.637062668800354004e-01 4.955786168575286865e-01 1.000000000000000000e+00 -8.798154592514038086e-01 7.695501446723937988e-01 5.063437223434448242e-01 1.000000000000000000e+00 -8.833525776863098145e-01 7.753940820693969727e-01 5.171087980270385742e-01 1.000000000000000000e+00 -8.868896365165710449e-01 7.812379598617553711e-01 5.278738737106323242e-01 1.000000000000000000e+00 -8.904267549514770508e-01 7.870818972587585449e-01 5.386390089988708496e-01 1.000000000000000000e+00 -8.939638733863830566e-01 7.929257750511169434e-01 5.494040846824645996e-01 1.000000000000000000e+00 -8.975009322166442871e-01 7.987697124481201172e-01 5.601691603660583496e-01 1.000000000000000000e+00 -9.010380506515502930e-01 8.046135902404785156e-01 5.709342360496520996e-01 1.000000000000000000e+00 -9.045751690864562988e-01 8.104575276374816895e-01 5.816993713378906250e-01 1.000000000000000000e+00 -9.081122875213623047e-01 8.163014054298400879e-01 5.924644470214843750e-01 1.000000000000000000e+00 -9.116493463516235352e-01 8.221453428268432617e-01 6.032295227050781250e-01 1.000000000000000000e+00 -9.151864647865295410e-01 8.279892206192016602e-01 6.139945983886718750e-01 1.000000000000000000e+00 -9.187235832214355469e-01 8.338331580162048340e-01 6.247597336769104004e-01 1.000000000000000000e+00 -9.222606420516967773e-01 8.396770358085632324e-01 6.355248093605041504e-01 1.000000000000000000e+00 -9.257977604866027832e-01 8.455209732055664062e-01 6.462898850440979004e-01 1.000000000000000000e+00 -9.293348789215087891e-01 8.513648509979248047e-01 6.570549607276916504e-01 1.000000000000000000e+00 -9.328719973564147949e-01 8.572087883949279785e-01 6.678200960159301758e-01 1.000000000000000000e+00 -9.364090561866760254e-01 8.630526661872863770e-01 6.785851716995239258e-01 1.000000000000000000e+00 -9.399461746215820312e-01 8.688966035842895508e-01 6.893502473831176758e-01 1.000000000000000000e+00 -9.434832930564880371e-01 8.747404813766479492e-01 7.001153230667114258e-01 1.000000000000000000e+00 -9.470203518867492676e-01 8.805844187736511230e-01 7.108804583549499512e-01 1.000000000000000000e+00 -9.505574703216552734e-01 8.864282965660095215e-01 7.216455340385437012e-01 1.000000000000000000e+00 -9.540945887565612793e-01 8.922721743583679199e-01 7.324106097221374512e-01 1.000000000000000000e+00 -9.576317071914672852e-01 8.981161117553710938e-01 7.431756854057312012e-01 1.000000000000000000e+00 -9.611687660217285156e-01 9.039599895477294922e-01 7.539408206939697266e-01 1.000000000000000000e+00 -9.647058844566345215e-01 9.098039269447326660e-01 7.647058963775634766e-01 1.000000000000000000e+00 -9.645521044731140137e-01 9.118031263351440430e-01 7.723952531814575195e-01 1.000000000000000000e+00 -9.643983244895935059e-01 9.138023853302001953e-01 7.800846099853515625e-01 1.000000000000000000e+00 -9.642445445060729980e-01 9.158016443252563477e-01 7.877739071846008301e-01 1.000000000000000000e+00 -9.640907049179077148e-01 9.178008437156677246e-01 7.954632639884948730e-01 1.000000000000000000e+00 -9.639369249343872070e-01 9.198001027107238770e-01 8.031526207923889160e-01 1.000000000000000000e+00 -9.637831449508666992e-01 9.217993021011352539e-01 8.108419775962829590e-01 1.000000000000000000e+00 -9.636293649673461914e-01 9.237985610961914062e-01 8.185313344001770020e-01 1.000000000000000000e+00 -9.634755849838256836e-01 9.257977604866027832e-01 8.262206912040710449e-01 1.000000000000000000e+00 -9.633218050003051758e-01 9.277970194816589355e-01 8.339100480079650879e-01 1.000000000000000000e+00 -9.631680250167846680e-01 9.297962188720703125e-01 8.415994048118591309e-01 1.000000000000000000e+00 -9.630142450332641602e-01 9.317954778671264648e-01 8.492887616157531738e-01 1.000000000000000000e+00 -9.628604650497436523e-01 9.337946772575378418e-01 8.569780588150024414e-01 1.000000000000000000e+00 -9.627066254615783691e-01 9.357939362525939941e-01 8.646674156188964844e-01 1.000000000000000000e+00 -9.625528454780578613e-01 9.377931356430053711e-01 8.723567724227905273e-01 1.000000000000000000e+00 -9.623990654945373535e-01 9.397923946380615234e-01 8.800461292266845703e-01 1.000000000000000000e+00 -9.622452855110168457e-01 9.417915940284729004e-01 8.877354860305786133e-01 1.000000000000000000e+00 -9.620915055274963379e-01 9.437908530235290527e-01 8.954248428344726562e-01 1.000000000000000000e+00 -9.619377255439758301e-01 9.457900524139404297e-01 9.031141996383666992e-01 1.000000000000000000e+00 -9.617839455604553223e-01 9.477893114089965820e-01 9.108035564422607422e-01 1.000000000000000000e+00 -9.616301655769348145e-01 9.497885704040527344e-01 9.184929132461547852e-01 1.000000000000000000e+00 -9.614763259887695312e-01 9.517877697944641113e-01 9.261822104454040527e-01 1.000000000000000000e+00 -9.613225460052490234e-01 9.537870287895202637e-01 9.338715672492980957e-01 1.000000000000000000e+00 -9.611687660217285156e-01 9.557862281799316406e-01 9.415609240531921387e-01 1.000000000000000000e+00 -9.610149860382080078e-01 9.577854871749877930e-01 9.492502808570861816e-01 1.000000000000000000e+00 -9.608612060546875000e-01 9.597846865653991699e-01 9.569396376609802246e-01 1.000000000000000000e+00 -9.572471976280212402e-01 9.599384665489196777e-01 9.595540165901184082e-01 1.000000000000000000e+00 -9.501730203628540039e-01 9.582468271255493164e-01 9.570934176445007324e-01 1.000000000000000000e+00 -9.430987834930419922e-01 9.565551877021789551e-01 9.546328186988830566e-01 1.000000000000000000e+00 -9.360246062278747559e-01 9.548634886741638184e-01 9.521722197532653809e-01 1.000000000000000000e+00 -9.289504289627075195e-01 9.531718492507934570e-01 9.497116208076477051e-01 1.000000000000000000e+00 -9.218761920928955078e-01 9.514802098274230957e-01 9.472510814666748047e-01 1.000000000000000000e+00 -9.148020148277282715e-01 9.497885704040527344e-01 9.447904825210571289e-01 1.000000000000000000e+00 -9.077277779579162598e-01 9.480968713760375977e-01 9.423298835754394531e-01 1.000000000000000000e+00 -9.006536006927490234e-01 9.464052319526672363e-01 9.398692846298217773e-01 1.000000000000000000e+00 -8.935793638229370117e-01 9.447135925292968750e-01 9.374086856842041016e-01 1.000000000000000000e+00 -8.865051865577697754e-01 9.430218935012817383e-01 9.349480867385864258e-01 1.000000000000000000e+00 -8.794310092926025391e-01 9.413302540779113770e-01 9.324874877929687500e-01 1.000000000000000000e+00 -8.723567724227905273e-01 9.396386146545410156e-01 9.300268888473510742e-01 1.000000000000000000e+00 -8.652825951576232910e-01 9.379469156265258789e-01 9.275663495063781738e-01 1.000000000000000000e+00 -8.582083582878112793e-01 9.362552762031555176e-01 9.251057505607604980e-01 1.000000000000000000e+00 -8.511341810226440430e-01 9.345636367797851562e-01 9.226451516151428223e-01 1.000000000000000000e+00 -8.440600037574768066e-01 9.328719973564147949e-01 9.201845526695251465e-01 1.000000000000000000e+00 -8.369857668876647949e-01 9.311802983283996582e-01 9.177239537239074707e-01 1.000000000000000000e+00 -8.299115896224975586e-01 9.294886589050292969e-01 9.152633547782897949e-01 1.000000000000000000e+00 -8.228373527526855469e-01 9.277970194816589355e-01 9.128027558326721191e-01 1.000000000000000000e+00 -8.157631754875183105e-01 9.261053204536437988e-01 9.103421568870544434e-01 1.000000000000000000e+00 -8.086889386177062988e-01 9.244136810302734375e-01 9.078815579414367676e-01 1.000000000000000000e+00 -8.016147613525390625e-01 9.227220416069030762e-01 9.054210186004638672e-01 1.000000000000000000e+00 -7.945405840873718262e-01 9.210304021835327148e-01 9.029604196548461914e-01 1.000000000000000000e+00 -7.874663472175598145e-01 9.193387031555175781e-01 9.004998207092285156e-01 1.000000000000000000e+00 -7.803921699523925781e-01 9.176470637321472168e-01 8.980392217636108398e-01 1.000000000000000000e+00 -7.694732546806335449e-01 9.131872653961181641e-01 8.925029039382934570e-01 1.000000000000000000e+00 -7.585543990135192871e-01 9.087274074554443359e-01 8.869665265083312988e-01 1.000000000000000000e+00 -7.476355433464050293e-01 9.042676091194152832e-01 8.814302086830139160e-01 1.000000000000000000e+00 -7.367166280746459961e-01 8.998077511787414551e-01 8.758938908576965332e-01 1.000000000000000000e+00 -7.257977724075317383e-01 8.953479528427124023e-01 8.703575730323791504e-01 1.000000000000000000e+00 -7.148789167404174805e-01 8.908880949020385742e-01 8.648211956024169922e-01 1.000000000000000000e+00 -7.039600014686584473e-01 8.864282965660095215e-01 8.592848777770996094e-01 1.000000000000000000e+00 -6.930411458015441895e-01 8.819684982299804688e-01 8.537485599517822266e-01 1.000000000000000000e+00 -6.821222901344299316e-01 8.775086402893066406e-01 8.482122421264648438e-01 1.000000000000000000e+00 -6.712033748626708984e-01 8.730488419532775879e-01 8.426758646965026855e-01 1.000000000000000000e+00 -6.602845191955566406e-01 8.685889840126037598e-01 8.371395468711853027e-01 1.000000000000000000e+00 -6.493656039237976074e-01 8.641291856765747070e-01 8.316032290458679199e-01 1.000000000000000000e+00 -6.384467482566833496e-01 8.596693873405456543e-01 8.260669112205505371e-01 1.000000000000000000e+00 -6.275278925895690918e-01 8.552095293998718262e-01 8.205305933952331543e-01 1.000000000000000000e+00 -6.166089773178100586e-01 8.507497310638427734e-01 8.149942159652709961e-01 1.000000000000000000e+00 -6.056901216506958008e-01 8.462898731231689453e-01 8.094578981399536133e-01 1.000000000000000000e+00 -5.947712659835815430e-01 8.418300747871398926e-01 8.039215803146362305e-01 1.000000000000000000e+00 -5.838523507118225098e-01 8.373702168464660645e-01 7.983852624893188477e-01 1.000000000000000000e+00 -5.729334950447082520e-01 8.329104185104370117e-01 7.928488850593566895e-01 1.000000000000000000e+00 -5.620146393775939941e-01 8.284506201744079590e-01 7.873125672340393066e-01 1.000000000000000000e+00 -5.510957241058349609e-01 8.239907622337341309e-01 7.817762494087219238e-01 1.000000000000000000e+00 -5.401768684387207031e-01 8.195309638977050781e-01 7.762399315834045410e-01 1.000000000000000000e+00 -5.292579531669616699e-01 8.150711059570312500e-01 7.707035541534423828e-01 1.000000000000000000e+00 -5.183390974998474121e-01 8.106113076210021973e-01 7.651672363281250000e-01 1.000000000000000000e+00 -5.074202418327331543e-01 8.061515092849731445e-01 7.596309185028076172e-01 1.000000000000000000e+00 -4.961937665939331055e-01 7.997693419456481934e-01 7.530180811882019043e-01 1.000000000000000000e+00 -4.846597313880920410e-01 7.914648056030273438e-01 7.453287243843078613e-01 1.000000000000000000e+00 -4.731257259845733643e-01 7.831603288650512695e-01 7.376393675804138184e-01 1.000000000000000000e+00 -4.615916907787322998e-01 7.748558521270751953e-01 7.299500107765197754e-01 1.000000000000000000e+00 -4.500576555728912354e-01 7.665513157844543457e-01 7.222606539726257324e-01 1.000000000000000000e+00 -4.385236501693725586e-01 7.582468390464782715e-01 7.145712971687316895e-01 1.000000000000000000e+00 -4.269896149635314941e-01 7.499423027038574219e-01 7.068819403648376465e-01 1.000000000000000000e+00 -4.154555797576904297e-01 7.416378259658813477e-01 6.991926431655883789e-01 1.000000000000000000e+00 -4.039215743541717529e-01 7.333333492279052734e-01 6.915032863616943359e-01 1.000000000000000000e+00 -3.923875391483306885e-01 7.250288128852844238e-01 6.838139295578002930e-01 1.000000000000000000e+00 -3.808535039424896240e-01 7.167243361473083496e-01 6.761245727539062500e-01 1.000000000000000000e+00 -3.693194985389709473e-01 7.084198594093322754e-01 6.684352159500122070e-01 1.000000000000000000e+00 -3.577854633331298828e-01 7.001153230667114258e-01 6.607458591461181641e-01 1.000000000000000000e+00 -3.462514281272888184e-01 6.918108463287353516e-01 6.530565023422241211e-01 1.000000000000000000e+00 -3.347174227237701416e-01 6.835063695907592773e-01 6.453671455383300781e-01 1.000000000000000000e+00 -3.231833875179290771e-01 6.752018332481384277e-01 6.376777887344360352e-01 1.000000000000000000e+00 -3.116493523120880127e-01 6.668973565101623535e-01 6.299884915351867676e-01 1.000000000000000000e+00 -3.001153469085693359e-01 6.585928201675415039e-01 6.222991347312927246e-01 1.000000000000000000e+00 -2.885813117027282715e-01 6.502883434295654297e-01 6.146097779273986816e-01 1.000000000000000000e+00 -2.770472764968872070e-01 6.419838666915893555e-01 6.069204211235046387e-01 1.000000000000000000e+00 -2.655132710933685303e-01 6.336793303489685059e-01 5.992310643196105957e-01 1.000000000000000000e+00 -2.539792358875274658e-01 6.253748536109924316e-01 5.915417075157165527e-01 1.000000000000000000e+00 -2.424452155828475952e-01 6.170703768730163574e-01 5.838523507118225098e-01 1.000000000000000000e+00 -2.309111952781677246e-01 6.087658405303955078e-01 5.761629939079284668e-01 1.000000000000000000e+00 -2.193771600723266602e-01 6.004613637924194336e-01 5.684736371040344238e-01 1.000000000000000000e+00 -2.078431397676467896e-01 5.921568870544433594e-01 5.607843399047851562e-01 1.000000000000000000e+00 -1.998462080955505371e-01 5.846213102340698242e-01 5.532487630844116211e-01 1.000000000000000000e+00 -1.918492913246154785e-01 5.770857334136962891e-01 5.457131862640380859e-01 1.000000000000000000e+00 -1.838523596525192261e-01 5.695501565933227539e-01 5.381776094436645508e-01 1.000000000000000000e+00 -1.758554428815841675e-01 5.620146393775939941e-01 5.306420326232910156e-01 1.000000000000000000e+00 -1.678585112094879150e-01 5.544790625572204590e-01 5.231065154075622559e-01 1.000000000000000000e+00 -1.598615944385528564e-01 5.469434857368469238e-01 5.155709385871887207e-01 1.000000000000000000e+00 -1.518646627664566040e-01 5.394079089164733887e-01 5.080353617668151855e-01 1.000000000000000000e+00 -1.438677459955215454e-01 5.318723320960998535e-01 5.004997849464416504e-01 1.000000000000000000e+00 -1.358708143234252930e-01 5.243368148803710938e-01 4.929642379283905029e-01 1.000000000000000000e+00 -1.278738975524902344e-01 5.168012380599975586e-01 4.854286909103393555e-01 1.000000000000000000e+00 -1.198769733309745789e-01 5.092656612396240234e-01 4.778931140899658203e-01 1.000000000000000000e+00 -1.118800491094589233e-01 5.017300844192504883e-01 4.703575670719146729e-01 1.000000000000000000e+00 -1.038831248879432678e-01 4.941945374011993408e-01 4.628219902515411377e-01 1.000000000000000000e+00 -9.588620066642761230e-02 4.866589903831481934e-01 4.552864134311676025e-01 1.000000000000000000e+00 -8.788927644491195679e-02 4.791234135627746582e-01 4.477508664131164551e-01 1.000000000000000000e+00 -7.989235222339630127e-02 4.715878367424011230e-01 4.402152895927429199e-01 1.000000000000000000e+00 -7.189542800188064575e-02 4.640522897243499756e-01 4.326797425746917725e-01 1.000000000000000000e+00 -6.389850378036499023e-02 4.565167129039764404e-01 4.251441657543182373e-01 1.000000000000000000e+00 -5.590157583355903625e-02 4.489811658859252930e-01 4.176086187362670898e-01 1.000000000000000000e+00 -4.790465161204338074e-02 4.414455890655517578e-01 4.100730419158935547e-01 1.000000000000000000e+00 -3.990772739052772522e-02 4.339100420475006104e-01 4.025374948978424072e-01 1.000000000000000000e+00 -3.191080316901206970e-02 4.263744652271270752e-01 3.950019180774688721e-01 1.000000000000000000e+00 -2.391387894749641418e-02 4.188389182090759277e-01 3.874663710594177246e-01 1.000000000000000000e+00 -1.591695472598075867e-02 4.113033413887023926e-01 3.799307942390441895e-01 1.000000000000000000e+00 -7.920030504465103149e-03 4.037677943706512451e-01 3.723952472209930420e-01 1.000000000000000000e+00 -3.844675142318010330e-03 3.967704772949218750e-01 3.650903403759002686e-01 1.000000000000000000e+00 -3.690888173878192902e-03 3.903114199638366699e-01 3.580161333084106445e-01 1.000000000000000000e+00 -3.537101205438375473e-03 3.838523626327514648e-01 3.509419560432434082e-01 1.000000000000000000e+00 -3.383314004167914391e-03 3.773933053016662598e-01 3.438677489757537842e-01 1.000000000000000000e+00 -3.229527035728096962e-03 3.709342479705810547e-01 3.367935419082641602e-01 1.000000000000000000e+00 -3.075740067288279533e-03 3.644751906394958496e-01 3.297193348407745361e-01 1.000000000000000000e+00 -2.921953098848462105e-03 3.580161333084106445e-01 3.226451277732849121e-01 1.000000000000000000e+00 -2.768166130408644676e-03 3.515571057796478271e-01 3.155709207057952881e-01 1.000000000000000000e+00 -2.614379161968827248e-03 3.450980484485626221e-01 3.084967434406280518e-01 1.000000000000000000e+00 -2.460592193529009819e-03 3.386389911174774170e-01 3.014225363731384277e-01 1.000000000000000000e+00 -2.306804992258548737e-03 3.321799337863922119e-01 2.943483293056488037e-01 1.000000000000000000e+00 -2.153018023818731308e-03 3.257208764553070068e-01 2.872741222381591797e-01 1.000000000000000000e+00 -1.999231055378913879e-03 3.192618191242218018e-01 2.801999151706695557e-01 1.000000000000000000e+00 -1.845444086939096451e-03 3.128027617931365967e-01 2.731257081031799316e-01 1.000000000000000000e+00 -1.691657002083957195e-03 3.063437044620513916e-01 2.660515308380126953e-01 1.000000000000000000e+00 -1.537870033644139767e-03 2.998846471309661865e-01 2.589773237705230713e-01 1.000000000000000000e+00 -1.384083065204322338e-03 2.934256196022033691e-01 2.519031167030334473e-01 1.000000000000000000e+00 -1.230296096764504910e-03 2.869665622711181641e-01 2.448289096355438232e-01 1.000000000000000000e+00 -1.076509011909365654e-03 2.805075049400329590e-01 2.377547025680541992e-01 1.000000000000000000e+00 -9.227220434695482254e-04 2.740484476089477539e-01 2.306805104017257690e-01 1.000000000000000000e+00 -7.689350168220698833e-04 2.675893902778625488e-01 2.236063033342361450e-01 1.000000000000000000e+00 -6.151480483822524548e-04 2.611303329467773438e-01 2.165320962667465210e-01 1.000000000000000000e+00 -4.613610217347741127e-04 2.546712756156921387e-01 2.094579041004180908e-01 1.000000000000000000e+00 -3.075740241911262274e-04 2.482122331857681274e-01 2.023836970329284668e-01 1.000000000000000000e+00 -1.537870120955631137e-04 2.417531758546829224e-01 1.953094899654388428e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.352941185235977173e-01 1.882352977991104126e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/BuGn b/fastplotlib/utils/colormaps/BuGn deleted file mode 100644 index e7d2c33a6..000000000 --- a/fastplotlib/utils/colormaps/BuGn +++ /dev/null @@ -1,256 +0,0 @@ -9.686274528503417969e-01 9.882352948188781738e-01 9.921568632125854492e-01 1.000000000000000000e+00 -9.664129018783569336e-01 9.873740673065185547e-01 9.916647672653198242e-01 1.000000000000000000e+00 -9.641984105110168457e-01 9.865128993988037109e-01 9.911726117134094238e-01 1.000000000000000000e+00 -9.619838595390319824e-01 9.856516718864440918e-01 9.906805157661437988e-01 1.000000000000000000e+00 -9.597693085670471191e-01 9.847904443740844727e-01 9.901883602142333984e-01 1.000000000000000000e+00 -9.575547575950622559e-01 9.839292764663696289e-01 9.896962642669677734e-01 1.000000000000000000e+00 -9.553402662277221680e-01 9.830680489540100098e-01 9.892041683197021484e-01 1.000000000000000000e+00 -9.531257152557373047e-01 9.822068214416503906e-01 9.887120127677917480e-01 1.000000000000000000e+00 -9.509111642837524414e-01 9.813456535339355469e-01 9.882199168205261230e-01 1.000000000000000000e+00 -9.486966729164123535e-01 9.804844260215759277e-01 9.877278208732604980e-01 1.000000000000000000e+00 -9.464821219444274902e-01 9.796231985092163086e-01 9.872356653213500977e-01 1.000000000000000000e+00 -9.442675709724426270e-01 9.787620306015014648e-01 9.867435693740844727e-01 1.000000000000000000e+00 -9.420530796051025391e-01 9.779008030891418457e-01 9.862514138221740723e-01 1.000000000000000000e+00 -9.398385286331176758e-01 9.770395755767822266e-01 9.857593178749084473e-01 1.000000000000000000e+00 -9.376239776611328125e-01 9.761784076690673828e-01 9.852672219276428223e-01 1.000000000000000000e+00 -9.354094862937927246e-01 9.753171801567077637e-01 9.847750663757324219e-01 1.000000000000000000e+00 -9.331949353218078613e-01 9.744559526443481445e-01 9.842829704284667969e-01 1.000000000000000000e+00 -9.309803843498229980e-01 9.735947847366333008e-01 9.837908744812011719e-01 1.000000000000000000e+00 -9.287658333778381348e-01 9.727335572242736816e-01 9.832987189292907715e-01 1.000000000000000000e+00 -9.265513420104980469e-01 9.718723297119140625e-01 9.828066229820251465e-01 1.000000000000000000e+00 -9.243367910385131836e-01 9.710111618041992188e-01 9.823144674301147461e-01 1.000000000000000000e+00 -9.221222400665283203e-01 9.701499342918395996e-01 9.818223714828491211e-01 1.000000000000000000e+00 -9.199077486991882324e-01 9.692887067794799805e-01 9.813302755355834961e-01 1.000000000000000000e+00 -9.176931977272033691e-01 9.684275388717651367e-01 9.808381199836730957e-01 1.000000000000000000e+00 -9.154786467552185059e-01 9.675663113594055176e-01 9.803460240364074707e-01 1.000000000000000000e+00 -9.132641553878784180e-01 9.667050838470458984e-01 9.798539280891418457e-01 1.000000000000000000e+00 -9.110496044158935547e-01 9.658439159393310547e-01 9.793617725372314453e-01 1.000000000000000000e+00 -9.088350534439086914e-01 9.649826884269714355e-01 9.788696765899658203e-01 1.000000000000000000e+00 -9.066205024719238281e-01 9.641215205192565918e-01 9.783775210380554199e-01 1.000000000000000000e+00 -9.044060111045837402e-01 9.632602930068969727e-01 9.778854250907897949e-01 1.000000000000000000e+00 -9.021914601325988770e-01 9.623990654945373535e-01 9.773933291435241699e-01 1.000000000000000000e+00 -8.999769091606140137e-01 9.615378975868225098e-01 9.769011735916137695e-01 1.000000000000000000e+00 -8.976547718048095703e-01 9.606459140777587891e-01 9.761784076690673828e-01 1.000000000000000000e+00 -8.945789933204650879e-01 9.595386385917663574e-01 9.738408327102661133e-01 1.000000000000000000e+00 -8.915032744407653809e-01 9.584313631057739258e-01 9.715032577514648438e-01 1.000000000000000000e+00 -8.884275555610656738e-01 9.573240876197814941e-01 9.691656827926635742e-01 1.000000000000000000e+00 -8.853517770767211914e-01 9.562168121337890625e-01 9.668281674385070801e-01 1.000000000000000000e+00 -8.822760581970214844e-01 9.551095962524414062e-01 9.644905924797058105e-01 1.000000000000000000e+00 -8.792002797126770020e-01 9.540023207664489746e-01 9.621530175209045410e-01 1.000000000000000000e+00 -8.761245608329772949e-01 9.528950452804565430e-01 9.598154425621032715e-01 1.000000000000000000e+00 -8.730488419532775879e-01 9.517877697944641113e-01 9.574778676033020020e-01 1.000000000000000000e+00 -8.699730634689331055e-01 9.506804943084716797e-01 9.551403522491455078e-01 1.000000000000000000e+00 -8.668973445892333984e-01 9.495732188224792480e-01 9.528027772903442383e-01 1.000000000000000000e+00 -8.638216257095336914e-01 9.484660029411315918e-01 9.504652023315429688e-01 1.000000000000000000e+00 -8.607458472251892090e-01 9.473587274551391602e-01 9.481276273727416992e-01 1.000000000000000000e+00 -8.576701283454895020e-01 9.462514519691467285e-01 9.457900524139404297e-01 1.000000000000000000e+00 -8.545944094657897949e-01 9.451441764831542969e-01 9.434525370597839355e-01 1.000000000000000000e+00 -8.515186309814453125e-01 9.440369009971618652e-01 9.411149621009826660e-01 1.000000000000000000e+00 -8.484429121017456055e-01 9.429296255111694336e-01 9.387773871421813965e-01 1.000000000000000000e+00 -8.453671932220458984e-01 9.418223500251770020e-01 9.364398121833801270e-01 1.000000000000000000e+00 -8.422914147377014160e-01 9.407151341438293457e-01 9.341022968292236328e-01 1.000000000000000000e+00 -8.392156958580017090e-01 9.396078586578369141e-01 9.317647218704223633e-01 1.000000000000000000e+00 -8.361399173736572266e-01 9.385005831718444824e-01 9.294271469116210938e-01 1.000000000000000000e+00 -8.330641984939575195e-01 9.373933076858520508e-01 9.270895719528198242e-01 1.000000000000000000e+00 -8.299884796142578125e-01 9.362860321998596191e-01 9.247519969940185547e-01 1.000000000000000000e+00 -8.269127011299133301e-01 9.351787567138671875e-01 9.224144816398620605e-01 1.000000000000000000e+00 -8.238369822502136230e-01 9.340714812278747559e-01 9.200769066810607910e-01 1.000000000000000000e+00 -8.207612633705139160e-01 9.329642653465270996e-01 9.177393317222595215e-01 1.000000000000000000e+00 -8.176854848861694336e-01 9.318569898605346680e-01 9.154017567634582520e-01 1.000000000000000000e+00 -8.146097660064697266e-01 9.307497143745422363e-01 9.130641818046569824e-01 1.000000000000000000e+00 -8.115340471267700195e-01 9.296424388885498047e-01 9.107266664505004883e-01 1.000000000000000000e+00 -8.084582686424255371e-01 9.285351634025573730e-01 9.083890914916992188e-01 1.000000000000000000e+00 -8.053825497627258301e-01 9.274278879165649414e-01 9.060515165328979492e-01 1.000000000000000000e+00 -8.023068308830261230e-01 9.263206720352172852e-01 9.037139415740966797e-01 1.000000000000000000e+00 -7.984313964843750000e-01 9.248750209808349609e-01 9.010688066482543945e-01 1.000000000000000000e+00 -7.921568751335144043e-01 9.224144816398620605e-01 8.975009322166442871e-01 1.000000000000000000e+00 -7.858823537826538086e-01 9.199538826942443848e-01 8.939331173896789551e-01 1.000000000000000000e+00 -7.796078324317932129e-01 9.174932837486267090e-01 8.903652429580688477e-01 1.000000000000000000e+00 -7.733333110809326172e-01 9.150326848030090332e-01 8.867973685264587402e-01 1.000000000000000000e+00 -7.670588493347167969e-01 9.125720858573913574e-01 8.832295536994934082e-01 1.000000000000000000e+00 -7.607843279838562012e-01 9.101114869117736816e-01 8.796616792678833008e-01 1.000000000000000000e+00 -7.545098066329956055e-01 9.076508879661560059e-01 8.760938048362731934e-01 1.000000000000000000e+00 -7.482352852821350098e-01 9.051902890205383301e-01 8.725259304046630859e-01 1.000000000000000000e+00 -7.419607639312744141e-01 9.027296900749206543e-01 8.689581155776977539e-01 1.000000000000000000e+00 -7.356863021850585938e-01 9.002691507339477539e-01 8.653902411460876465e-01 1.000000000000000000e+00 -7.294117808341979980e-01 8.978085517883300781e-01 8.618223667144775391e-01 1.000000000000000000e+00 -7.231372594833374023e-01 8.953479528427124023e-01 8.582544922828674316e-01 1.000000000000000000e+00 -7.168627381324768066e-01 8.928873538970947266e-01 8.546866774559020996e-01 1.000000000000000000e+00 -7.105882167816162109e-01 8.904267549514770508e-01 8.511188030242919922e-01 1.000000000000000000e+00 -7.043137550354003906e-01 8.879661560058593750e-01 8.475509285926818848e-01 1.000000000000000000e+00 -6.980392336845397949e-01 8.855055570602416992e-01 8.439830541610717773e-01 1.000000000000000000e+00 -6.917647123336791992e-01 8.830449581146240234e-01 8.404152393341064453e-01 1.000000000000000000e+00 -6.854901909828186035e-01 8.805844187736511230e-01 8.368473649024963379e-01 1.000000000000000000e+00 -6.792156696319580078e-01 8.781238198280334473e-01 8.332794904708862305e-01 1.000000000000000000e+00 -6.729411482810974121e-01 8.756632208824157715e-01 8.297116756439208984e-01 1.000000000000000000e+00 -6.666666865348815918e-01 8.732026219367980957e-01 8.261438012123107910e-01 1.000000000000000000e+00 -6.603921651840209961e-01 8.707420229911804199e-01 8.225759267807006836e-01 1.000000000000000000e+00 -6.541176438331604004e-01 8.682814240455627441e-01 8.190080523490905762e-01 1.000000000000000000e+00 -6.478431224822998047e-01 8.658208250999450684e-01 8.154402375221252441e-01 1.000000000000000000e+00 -6.415686011314392090e-01 8.633602261543273926e-01 8.118723630905151367e-01 1.000000000000000000e+00 -6.352941393852233887e-01 8.608996272087097168e-01 8.083044886589050293e-01 1.000000000000000000e+00 -6.290196180343627930e-01 8.584390878677368164e-01 8.047366142272949219e-01 1.000000000000000000e+00 -6.227450966835021973e-01 8.559784889221191406e-01 8.011687994003295898e-01 1.000000000000000000e+00 -6.164705753326416016e-01 8.535178899765014648e-01 7.976009249687194824e-01 1.000000000000000000e+00 -6.101960539817810059e-01 8.510572910308837891e-01 7.940330505371093750e-01 1.000000000000000000e+00 -6.039215922355651855e-01 8.485966920852661133e-01 7.904651761054992676e-01 1.000000000000000000e+00 -5.976470708847045898e-01 8.460438251495361328e-01 7.865282297134399414e-01 1.000000000000000000e+00 -5.913725495338439941e-01 8.433371782302856445e-01 7.819761633872985840e-01 1.000000000000000000e+00 -5.850980281829833984e-01 8.406305313110351562e-01 7.774240970611572266e-01 1.000000000000000000e+00 -5.788235068321228027e-01 8.379238843917846680e-01 7.728719711303710938e-01 1.000000000000000000e+00 -5.725490450859069824e-01 8.352172374725341797e-01 7.683199048042297363e-01 1.000000000000000000e+00 -5.662745237350463867e-01 8.325105905532836914e-01 7.637677788734436035e-01 1.000000000000000000e+00 -5.600000023841857910e-01 8.298039436340332031e-01 7.592157125473022461e-01 1.000000000000000000e+00 -5.537254810333251953e-01 8.270972967147827148e-01 7.546635866165161133e-01 1.000000000000000000e+00 -5.474509596824645996e-01 8.243905901908874512e-01 7.501115202903747559e-01 1.000000000000000000e+00 -5.411764979362487793e-01 8.216839432716369629e-01 7.455593943595886230e-01 1.000000000000000000e+00 -5.349019765853881836e-01 8.189772963523864746e-01 7.410073280334472656e-01 1.000000000000000000e+00 -5.286274552345275879e-01 8.162706494331359863e-01 7.364552021026611328e-01 1.000000000000000000e+00 -5.223529338836669922e-01 8.135640025138854980e-01 7.319031357765197754e-01 1.000000000000000000e+00 -5.160784125328063965e-01 8.108573555946350098e-01 7.273510098457336426e-01 1.000000000000000000e+00 -5.098039507865905762e-01 8.081507086753845215e-01 7.227989435195922852e-01 1.000000000000000000e+00 -5.035294294357299805e-01 8.054440617561340332e-01 7.182468175888061523e-01 1.000000000000000000e+00 -4.972549080848693848e-01 8.027374148368835449e-01 7.136947512626647949e-01 1.000000000000000000e+00 -4.909803867340087891e-01 8.000307679176330566e-01 7.091426253318786621e-01 1.000000000000000000e+00 -4.847058951854705811e-01 7.973241209983825684e-01 7.045905590057373047e-01 1.000000000000000000e+00 -4.784313738346099854e-01 7.946174740791320801e-01 7.000384330749511719e-01 1.000000000000000000e+00 -4.721568524837493896e-01 7.919108271598815918e-01 6.954863667488098145e-01 1.000000000000000000e+00 -4.658823609352111816e-01 7.892041802406311035e-01 6.909342408180236816e-01 1.000000000000000000e+00 -4.596078395843505859e-01 7.864974737167358398e-01 6.863821744918823242e-01 1.000000000000000000e+00 -4.533333480358123779e-01 7.837908267974853516e-01 6.818300485610961914e-01 1.000000000000000000e+00 -4.470588266849517822e-01 7.810841798782348633e-01 6.772779822349548340e-01 1.000000000000000000e+00 -4.407843053340911865e-01 7.783775329589843750e-01 6.727258563041687012e-01 1.000000000000000000e+00 -4.345098137855529785e-01 7.756708860397338867e-01 6.681737899780273438e-01 1.000000000000000000e+00 -4.282352924346923828e-01 7.729642391204833984e-01 6.636216640472412109e-01 1.000000000000000000e+00 -4.219607710838317871e-01 7.702575922012329102e-01 6.590695977210998535e-01 1.000000000000000000e+00 -4.156862795352935791e-01 7.675509452819824219e-01 6.545174717903137207e-01 1.000000000000000000e+00 -4.094117581844329834e-01 7.648442983627319336e-01 6.499654054641723633e-01 1.000000000000000000e+00 -4.031372666358947754e-01 7.621376514434814453e-01 6.454132795333862305e-01 1.000000000000000000e+00 -3.977239429950714111e-01 7.595540285110473633e-01 6.403075456619262695e-01 1.000000000000000000e+00 -3.931718468666076660e-01 7.570934295654296875e-01 6.346482038497924805e-01 1.000000000000000000e+00 -3.886197507381439209e-01 7.546328306198120117e-01 6.289888620376586914e-01 1.000000000000000000e+00 -3.840676546096801758e-01 7.521722316741943359e-01 6.233294606208801270e-01 1.000000000000000000e+00 -3.795155584812164307e-01 7.497116327285766602e-01 6.176701188087463379e-01 1.000000000000000000e+00 -3.749634623527526855e-01 7.472510337829589844e-01 6.120107769966125488e-01 1.000000000000000000e+00 -3.704113662242889404e-01 7.447904944419860840e-01 6.063513755798339844e-01 1.000000000000000000e+00 -3.658592700958251953e-01 7.423298954963684082e-01 6.006920337677001953e-01 1.000000000000000000e+00 -3.613072037696838379e-01 7.398692965507507324e-01 5.950326919555664062e-01 1.000000000000000000e+00 -3.567551076412200928e-01 7.374086976051330566e-01 5.893732905387878418e-01 1.000000000000000000e+00 -3.522030115127563477e-01 7.349480986595153809e-01 5.837139487266540527e-01 1.000000000000000000e+00 -3.476509153842926025e-01 7.324874997138977051e-01 5.780546069145202637e-01 1.000000000000000000e+00 -3.430988192558288574e-01 7.300269007682800293e-01 5.723952054977416992e-01 1.000000000000000000e+00 -3.385467231273651123e-01 7.275663018226623535e-01 5.667358636856079102e-01 1.000000000000000000e+00 -3.339946269989013672e-01 7.251057028770446777e-01 5.610765218734741211e-01 1.000000000000000000e+00 -3.294425308704376221e-01 7.226451635360717773e-01 5.554171204566955566e-01 1.000000000000000000e+00 -3.248904347419738770e-01 7.201845645904541016e-01 5.497577786445617676e-01 1.000000000000000000e+00 -3.203383386135101318e-01 7.177239656448364258e-01 5.440984368324279785e-01 1.000000000000000000e+00 -3.157862424850463867e-01 7.152633666992187500e-01 5.384390354156494141e-01 1.000000000000000000e+00 -3.112341463565826416e-01 7.128027677536010742e-01 5.327796936035156250e-01 1.000000000000000000e+00 -3.066820502281188965e-01 7.103421688079833984e-01 5.271203517913818359e-01 1.000000000000000000e+00 -3.021299540996551514e-01 7.078815698623657227e-01 5.214609503746032715e-01 1.000000000000000000e+00 -2.975778579711914062e-01 7.054209709167480469e-01 5.158016085624694824e-01 1.000000000000000000e+00 -2.930257618427276611e-01 7.029603719711303711e-01 5.101422667503356934e-01 1.000000000000000000e+00 -2.884736657142639160e-01 7.004998326301574707e-01 5.044828653335571289e-01 1.000000000000000000e+00 -2.839215695858001709e-01 6.980392336845397949e-01 4.988235235214233398e-01 1.000000000000000000e+00 -2.793694734573364258e-01 6.955786347389221191e-01 4.931641817092895508e-01 1.000000000000000000e+00 -2.748173773288726807e-01 6.931180357933044434e-01 4.875048100948333740e-01 1.000000000000000000e+00 -2.702652812004089355e-01 6.906574368476867676e-01 4.818454384803771973e-01 1.000000000000000000e+00 -2.657131850719451904e-01 6.881968379020690918e-01 4.761860966682434082e-01 1.000000000000000000e+00 -2.611610889434814453e-01 6.857362389564514160e-01 4.705267250537872314e-01 1.000000000000000000e+00 -2.566089928150177002e-01 6.832756400108337402e-01 4.648673534393310547e-01 1.000000000000000000e+00 -2.525951564311981201e-01 6.796616911888122559e-01 4.589773118495941162e-01 1.000000000000000000e+00 -2.489042729139328003e-01 6.753556132316589355e-01 4.529488682746887207e-01 1.000000000000000000e+00 -2.452133744955062866e-01 6.710495948791503906e-01 4.469204246997833252e-01 1.000000000000000000e+00 -2.415224909782409668e-01 6.667435765266418457e-01 4.408919513225555420e-01 1.000000000000000000e+00 -2.378316074609756470e-01 6.624374985694885254e-01 4.348635077476501465e-01 1.000000000000000000e+00 -2.341407090425491333e-01 6.581314802169799805e-01 4.288350641727447510e-01 1.000000000000000000e+00 -2.304498255252838135e-01 6.538254618644714355e-01 4.228066205978393555e-01 1.000000000000000000e+00 -2.267589420080184937e-01 6.495194435119628906e-01 4.167781770229339600e-01 1.000000000000000000e+00 -2.230680435895919800e-01 6.452133655548095703e-01 4.107497036457061768e-01 1.000000000000000000e+00 -2.193771600723266602e-01 6.409073472023010254e-01 4.047212600708007812e-01 1.000000000000000000e+00 -2.156862765550613403e-01 6.366013288497924805e-01 3.986928164958953857e-01 1.000000000000000000e+00 -2.119953930377960205e-01 6.322952508926391602e-01 3.926643729209899902e-01 1.000000000000000000e+00 -2.083044946193695068e-01 6.279892325401306152e-01 3.866358995437622070e-01 1.000000000000000000e+00 -2.046136111021041870e-01 6.236832141876220703e-01 3.806074559688568115e-01 1.000000000000000000e+00 -2.009227275848388672e-01 6.193771362304687500e-01 3.745790123939514160e-01 1.000000000000000000e+00 -1.972318291664123535e-01 6.150711178779602051e-01 3.685505688190460205e-01 1.000000000000000000e+00 -1.935409456491470337e-01 6.107650995254516602e-01 3.625220954418182373e-01 1.000000000000000000e+00 -1.898500621318817139e-01 6.064590811729431152e-01 3.564936518669128418e-01 1.000000000000000000e+00 -1.861591637134552002e-01 6.021530032157897949e-01 3.504652082920074463e-01 1.000000000000000000e+00 -1.824682801961898804e-01 5.978469848632812500e-01 3.444367647171020508e-01 1.000000000000000000e+00 -1.787773966789245605e-01 5.935409665107727051e-01 3.384082913398742676e-01 1.000000000000000000e+00 -1.750864982604980469e-01 5.892348885536193848e-01 3.323798477649688721e-01 1.000000000000000000e+00 -1.713956147432327271e-01 5.849288702011108398e-01 3.263514041900634766e-01 1.000000000000000000e+00 -1.677047312259674072e-01 5.806228518486022949e-01 3.203229606151580811e-01 1.000000000000000000e+00 -1.640138477087020874e-01 5.763167738914489746e-01 3.142944872379302979e-01 1.000000000000000000e+00 -1.603229492902755737e-01 5.720107555389404297e-01 3.082660436630249023e-01 1.000000000000000000e+00 -1.566320657730102539e-01 5.677047371864318848e-01 3.022376000881195068e-01 1.000000000000000000e+00 -1.529411822557449341e-01 5.633987188339233398e-01 2.962091565132141113e-01 1.000000000000000000e+00 -1.492502838373184204e-01 5.590926408767700195e-01 2.901807129383087158e-01 1.000000000000000000e+00 -1.455594003200531006e-01 5.547866225242614746e-01 2.841522395610809326e-01 1.000000000000000000e+00 -1.418685168027877808e-01 5.504806041717529297e-01 2.781237959861755371e-01 1.000000000000000000e+00 -1.381776183843612671e-01 5.461745262145996094e-01 2.720953524112701416e-01 1.000000000000000000e+00 -1.340253800153732300e-01 5.423298478126525879e-01 2.682814300060272217e-01 1.000000000000000000e+00 -1.297193318605422974e-01 5.386390089988708496e-01 2.652056813240051270e-01 1.000000000000000000e+00 -1.254132986068725586e-01 5.349481105804443359e-01 2.621299624443054199e-01 1.000000000000000000e+00 -1.211072653532028198e-01 5.312572121620178223e-01 2.590542137622833252e-01 1.000000000000000000e+00 -1.168012320995330811e-01 5.275663137435913086e-01 2.559784650802612305e-01 1.000000000000000000e+00 -1.124951913952827454e-01 5.238754153251647949e-01 2.529027163982391357e-01 1.000000000000000000e+00 -1.081891581416130066e-01 5.201845169067382812e-01 2.498269826173782349e-01 1.000000000000000000e+00 -1.038831248879432678e-01 5.164936780929565430e-01 2.467512488365173340e-01 1.000000000000000000e+00 -9.957708418369293213e-02 5.128027796745300293e-01 2.436755150556564331e-01 1.000000000000000000e+00 -9.527105093002319336e-02 5.091118812561035156e-01 2.405997663736343384e-01 1.000000000000000000e+00 -9.096501022577285767e-02 5.054209828376770020e-01 2.375240325927734375e-01 1.000000000000000000e+00 -8.665897697210311890e-02 5.017300844192504883e-01 2.344482839107513428e-01 1.000000000000000000e+00 -8.235294371843338013e-02 4.980392158031463623e-01 2.313725501298904419e-01 1.000000000000000000e+00 -7.804690301418304443e-02 4.943483173847198486e-01 2.282968163490295410e-01 1.000000000000000000e+00 -7.374086976051330566e-02 4.906574487686157227e-01 2.252210676670074463e-01 1.000000000000000000e+00 -6.943482905626296997e-02 4.869665503501892090e-01 2.221453338861465454e-01 1.000000000000000000e+00 -6.512879580259323120e-02 4.832756519317626953e-01 2.190695852041244507e-01 1.000000000000000000e+00 -6.082275882363319397e-02 4.795847833156585693e-01 2.159938514232635498e-01 1.000000000000000000e+00 -5.651672556996345520e-02 4.758938848972320557e-01 2.129181027412414551e-01 1.000000000000000000e+00 -5.221068859100341797e-02 4.722029864788055420e-01 2.098423689603805542e-01 1.000000000000000000e+00 -4.790465161204338074e-02 4.685121178627014160e-01 2.067666351795196533e-01 1.000000000000000000e+00 -4.359861463308334351e-02 4.648212194442749023e-01 2.036908864974975586e-01 1.000000000000000000e+00 -3.929258137941360474e-02 4.611303210258483887e-01 2.006151527166366577e-01 1.000000000000000000e+00 -3.498654440045356750e-02 4.574394524097442627e-01 1.975394040346145630e-01 1.000000000000000000e+00 -3.068050742149353027e-02 4.537485539913177490e-01 1.944636702537536621e-01 1.000000000000000000e+00 -2.637447044253349304e-02 4.500576555728912354e-01 1.913879215717315674e-01 1.000000000000000000e+00 -2.206843532621860504e-02 4.463667869567871094e-01 1.883121877908706665e-01 1.000000000000000000e+00 -1.776239834725856781e-02 4.426758885383605957e-01 1.852364540100097656e-01 1.000000000000000000e+00 -1.345636323094367981e-02 4.389850199222564697e-01 1.821607053279876709e-01 1.000000000000000000e+00 -9.150327183306217194e-03 4.352941215038299561e-01 1.790849715471267700e-01 1.000000000000000000e+00 -4.844290670007467270e-03 4.316032230854034424e-01 1.760092228651046753e-01 1.000000000000000000e+00 -5.382545059546828270e-04 4.279123544692993164e-01 1.729334890842437744e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.230372905731201172e-01 1.707189530134201050e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.179930686950683594e-01 1.686274558305740356e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.129488766193389893e-01 1.665359437465667725e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.079046547412872314e-01 1.644444465637207031e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.028604328632354736e-01 1.623529344797134399e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.978162109851837158e-01 1.602614372968673706e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.927720189094543457e-01 1.581699401140213013e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.877277970314025879e-01 1.560784280300140381e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.826835751533508301e-01 1.539869308471679688e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.776393830776214600e-01 1.518954187631607056e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.725951611995697021e-01 1.498039215803146362e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.675509393215179443e-01 1.477124243974685669e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.625067174434661865e-01 1.456209123134613037e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.574625253677368164e-01 1.435294151306152344e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.524183034896850586e-01 1.414379030466079712e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.473740816116333008e-01 1.393464058637619019e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.423298597335815430e-01 1.372549086809158325e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.372856676578521729e-01 1.351633965969085693e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.322414457798004150e-01 1.330718994140625000e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.271972239017486572e-01 1.309803873300552368e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.221530318260192871e-01 1.288888901472091675e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.171088099479675293e-01 1.267973929643630981e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.120645880699157715e-01 1.247058808803558350e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.070203661918640137e-01 1.226143762469291687e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.019761741161346436e-01 1.205228790640830994e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.969319522380828857e-01 1.184313744306564331e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.918877303600311279e-01 1.163398697972297668e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.868435084819793701e-01 1.142483651638031006e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.817993164062500000e-01 1.121568605303764343e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.767550945281982422e-01 1.100653558969497681e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.717108726501464844e-01 1.079738587141036987e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.666666805744171143e-01 1.058823540806770325e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/BuPu b/fastplotlib/utils/colormaps/BuPu deleted file mode 100644 index bfdac03b4..000000000 --- a/fastplotlib/utils/colormaps/BuPu +++ /dev/null @@ -1,256 +0,0 @@ -9.686274528503417969e-01 9.882352948188781738e-01 9.921568632125854492e-01 1.000000000000000000e+00 -9.657977819442749023e-01 9.862667918205261230e-01 9.910495877265930176e-01 1.000000000000000000e+00 -9.629681110382080078e-01 9.842983484268188477e-01 9.899423122406005859e-01 1.000000000000000000e+00 -9.601383805274963379e-01 9.823298454284667969e-01 9.888350367546081543e-01 1.000000000000000000e+00 -9.573087096214294434e-01 9.803614020347595215e-01 9.877278208732604980e-01 1.000000000000000000e+00 -9.544790387153625488e-01 9.783928990364074707e-01 9.866205453872680664e-01 1.000000000000000000e+00 -9.516493678092956543e-01 9.764244556427001953e-01 9.855132699012756348e-01 1.000000000000000000e+00 -9.488196969032287598e-01 9.744559526443481445e-01 9.844059944152832031e-01 1.000000000000000000e+00 -9.459900259971618652e-01 9.724875092506408691e-01 9.832987189292907715e-01 1.000000000000000000e+00 -9.431602954864501953e-01 9.705190062522888184e-01 9.821914434432983398e-01 1.000000000000000000e+00 -9.403306245803833008e-01 9.685505628585815430e-01 9.810842275619506836e-01 1.000000000000000000e+00 -9.375009536743164062e-01 9.665820598602294922e-01 9.799769520759582520e-01 1.000000000000000000e+00 -9.346712827682495117e-01 9.646136164665222168e-01 9.788696765899658203e-01 1.000000000000000000e+00 -9.318416118621826172e-01 9.626451134681701660e-01 9.777624011039733887e-01 1.000000000000000000e+00 -9.290119409561157227e-01 9.606766700744628906e-01 9.766551256179809570e-01 1.000000000000000000e+00 -9.261822104454040527e-01 9.587081670761108398e-01 9.755478501319885254e-01 1.000000000000000000e+00 -9.233525395393371582e-01 9.567397236824035645e-01 9.744405746459960938e-01 1.000000000000000000e+00 -9.205228686332702637e-01 9.547712206840515137e-01 9.733333587646484375e-01 1.000000000000000000e+00 -9.176931977272033691e-01 9.528027772903442383e-01 9.722260832786560059e-01 1.000000000000000000e+00 -9.148635268211364746e-01 9.508342742919921875e-01 9.711188077926635742e-01 1.000000000000000000e+00 -9.120338559150695801e-01 9.488658308982849121e-01 9.700115323066711426e-01 1.000000000000000000e+00 -9.092041254043579102e-01 9.468973278999328613e-01 9.689042568206787109e-01 1.000000000000000000e+00 -9.063744544982910156e-01 9.449288845062255859e-01 9.677969813346862793e-01 1.000000000000000000e+00 -9.035447835922241211e-01 9.429603815078735352e-01 9.666897058486938477e-01 1.000000000000000000e+00 -9.007151126861572266e-01 9.409919381141662598e-01 9.655824899673461914e-01 1.000000000000000000e+00 -8.978854417800903320e-01 9.390234351158142090e-01 9.644752144813537598e-01 1.000000000000000000e+00 -8.950557708740234375e-01 9.370549917221069336e-01 9.633679389953613281e-01 1.000000000000000000e+00 -8.922260403633117676e-01 9.350864887237548828e-01 9.622606635093688965e-01 1.000000000000000000e+00 -8.893963694572448730e-01 9.331180453300476074e-01 9.611533880233764648e-01 1.000000000000000000e+00 -8.865666985511779785e-01 9.311495423316955566e-01 9.600461125373840332e-01 1.000000000000000000e+00 -8.837370276451110840e-01 9.291810989379882812e-01 9.589388966560363770e-01 1.000000000000000000e+00 -8.809073567390441895e-01 9.272125959396362305e-01 9.578316211700439453e-01 1.000000000000000000e+00 -8.779238462448120117e-01 9.251057505607604980e-01 9.566474556922912598e-01 1.000000000000000000e+00 -8.738638758659362793e-01 9.220299720764160156e-01 9.549250006675720215e-01 1.000000000000000000e+00 -8.698039054870605469e-01 9.189542531967163086e-01 9.532026052474975586e-01 1.000000000000000000e+00 -8.657439351081848145e-01 9.158785343170166016e-01 9.514802098274230957e-01 1.000000000000000000e+00 -8.616839647293090820e-01 9.128027558326721191e-01 9.497578144073486328e-01 1.000000000000000000e+00 -8.576239943504333496e-01 9.097270369529724121e-01 9.480353593826293945e-01 1.000000000000000000e+00 -8.535640239715576172e-01 9.066512584686279297e-01 9.463129639625549316e-01 1.000000000000000000e+00 -8.495040535926818848e-01 9.035755395889282227e-01 9.445905685424804688e-01 1.000000000000000000e+00 -8.454440832138061523e-01 9.004998207092285156e-01 9.428681135177612305e-01 1.000000000000000000e+00 -8.413841128349304199e-01 8.974240422248840332e-01 9.411457180976867676e-01 1.000000000000000000e+00 -8.373240828514099121e-01 8.943483233451843262e-01 9.394233226776123047e-01 1.000000000000000000e+00 -8.332641124725341797e-01 8.912726044654846191e-01 9.377008676528930664e-01 1.000000000000000000e+00 -8.292041420936584473e-01 8.881968259811401367e-01 9.359784722328186035e-01 1.000000000000000000e+00 -8.251441717147827148e-01 8.851211071014404297e-01 9.342560768127441406e-01 1.000000000000000000e+00 -8.210842013359069824e-01 8.820453882217407227e-01 9.325336217880249023e-01 1.000000000000000000e+00 -8.170242309570312500e-01 8.789696097373962402e-01 9.308112263679504395e-01 1.000000000000000000e+00 -8.129642605781555176e-01 8.758938908576965332e-01 9.290888309478759766e-01 1.000000000000000000e+00 -8.089042901992797852e-01 8.728181719779968262e-01 9.273663759231567383e-01 1.000000000000000000e+00 -8.048443198204040527e-01 8.697423934936523438e-01 9.256439805030822754e-01 1.000000000000000000e+00 -8.007842898368835449e-01 8.666666746139526367e-01 9.239215850830078125e-01 1.000000000000000000e+00 -7.967243194580078125e-01 8.635909557342529297e-01 9.221991300582885742e-01 1.000000000000000000e+00 -7.926643490791320801e-01 8.605151772499084473e-01 9.204767346382141113e-01 1.000000000000000000e+00 -7.886043787002563477e-01 8.574394583702087402e-01 9.187543392181396484e-01 1.000000000000000000e+00 -7.845444083213806152e-01 8.543636798858642578e-01 9.170318841934204102e-01 1.000000000000000000e+00 -7.804844379425048828e-01 8.512879610061645508e-01 9.153094887733459473e-01 1.000000000000000000e+00 -7.764244675636291504e-01 8.482122421264648438e-01 9.135870933532714844e-01 1.000000000000000000e+00 -7.723644971847534180e-01 8.451364636421203613e-01 9.118646383285522461e-01 1.000000000000000000e+00 -7.683045268058776855e-01 8.420607447624206543e-01 9.101422429084777832e-01 1.000000000000000000e+00 -7.642444968223571777e-01 8.389850258827209473e-01 9.084198474884033203e-01 1.000000000000000000e+00 -7.601845264434814453e-01 8.359092473983764648e-01 9.066974520683288574e-01 1.000000000000000000e+00 -7.561245560646057129e-01 8.328335285186767578e-01 9.049749970436096191e-01 1.000000000000000000e+00 -7.520645856857299805e-01 8.297578096389770508e-01 9.032526016235351562e-01 1.000000000000000000e+00 -7.480046153068542480e-01 8.267435431480407715e-01 9.015917181968688965e-01 1.000000000000000000e+00 -7.439446449279785156e-01 8.239138722419738770e-01 9.001153111457824707e-01 1.000000000000000000e+00 -7.398846745491027832e-01 8.210842013359069824e-01 8.986389636993408203e-01 1.000000000000000000e+00 -7.358247041702270508e-01 8.182545304298400879e-01 8.971626162528991699e-01 1.000000000000000000e+00 -7.317647337913513184e-01 8.154248595237731934e-01 8.956862688064575195e-01 1.000000000000000000e+00 -7.277047038078308105e-01 8.125951290130615234e-01 8.942099213600158691e-01 1.000000000000000000e+00 -7.236447334289550781e-01 8.097654581069946289e-01 8.927335739135742188e-01 1.000000000000000000e+00 -7.195847630500793457e-01 8.069357872009277344e-01 8.912572264671325684e-01 1.000000000000000000e+00 -7.155247926712036133e-01 8.041061162948608398e-01 8.897808790206909180e-01 1.000000000000000000e+00 -7.114648222923278809e-01 8.012764453887939453e-01 8.883044719696044922e-01 1.000000000000000000e+00 -7.074048519134521484e-01 7.984467744827270508e-01 8.868281245231628418e-01 1.000000000000000000e+00 -7.033448815345764160e-01 7.956170439720153809e-01 8.853517770767211914e-01 1.000000000000000000e+00 -6.992849111557006836e-01 7.927873730659484863e-01 8.838754296302795410e-01 1.000000000000000000e+00 -6.952249407768249512e-01 7.899577021598815918e-01 8.823990821838378906e-01 1.000000000000000000e+00 -6.911649107933044434e-01 7.871280312538146973e-01 8.809227347373962402e-01 1.000000000000000000e+00 -6.871049404144287109e-01 7.842983603477478027e-01 8.794463872909545898e-01 1.000000000000000000e+00 -6.830449700355529785e-01 7.814686894416809082e-01 8.779700398445129395e-01 1.000000000000000000e+00 -6.789849996566772461e-01 7.786389589309692383e-01 8.764936327934265137e-01 1.000000000000000000e+00 -6.749250292778015137e-01 7.758092880249023438e-01 8.750172853469848633e-01 1.000000000000000000e+00 -6.708650588989257812e-01 7.729796171188354492e-01 8.735409379005432129e-01 1.000000000000000000e+00 -6.668050885200500488e-01 7.701499462127685547e-01 8.720645904541015625e-01 1.000000000000000000e+00 -6.627451181411743164e-01 7.673202753067016602e-01 8.705882430076599121e-01 1.000000000000000000e+00 -6.586851477622985840e-01 7.644906044006347656e-01 8.691118955612182617e-01 1.000000000000000000e+00 -6.546251177787780762e-01 7.616608738899230957e-01 8.676355481147766113e-01 1.000000000000000000e+00 -6.505651473999023438e-01 7.588312029838562012e-01 8.661591410636901855e-01 1.000000000000000000e+00 -6.465051770210266113e-01 7.560015320777893066e-01 8.646827936172485352e-01 1.000000000000000000e+00 -6.424452066421508789e-01 7.531718611717224121e-01 8.632064461708068848e-01 1.000000000000000000e+00 -6.383852362632751465e-01 7.503421902656555176e-01 8.617300987243652344e-01 1.000000000000000000e+00 -6.343252658843994141e-01 7.475125193595886230e-01 8.602537512779235840e-01 1.000000000000000000e+00 -6.302652955055236816e-01 7.446827888488769531e-01 8.587774038314819336e-01 1.000000000000000000e+00 -6.262053251266479492e-01 7.418531179428100586e-01 8.573010563850402832e-01 1.000000000000000000e+00 -6.221453547477722168e-01 7.390234470367431641e-01 8.558247089385986328e-01 1.000000000000000000e+00 -6.187773942947387695e-01 7.355017066001892090e-01 8.539792299270629883e-01 1.000000000000000000e+00 -6.165628433227539062e-01 7.308266162872314453e-01 8.515186309814453125e-01 1.000000000000000000e+00 -6.143483519554138184e-01 7.261514663696289062e-01 8.490580320358276367e-01 1.000000000000000000e+00 -6.121338009834289551e-01 7.214763760566711426e-01 8.465974330902099609e-01 1.000000000000000000e+00 -6.099192500114440918e-01 7.168012261390686035e-01 8.441368937492370605e-01 1.000000000000000000e+00 -6.077047586441040039e-01 7.121260762214660645e-01 8.416762948036193848e-01 1.000000000000000000e+00 -6.054902076721191406e-01 7.074509859085083008e-01 8.392156958580017090e-01 1.000000000000000000e+00 -6.032756567001342773e-01 7.027758359909057617e-01 8.367550969123840332e-01 1.000000000000000000e+00 -6.010611057281494141e-01 6.981007456779479980e-01 8.342944979667663574e-01 1.000000000000000000e+00 -5.988466143608093262e-01 6.934255957603454590e-01 8.318338990211486816e-01 1.000000000000000000e+00 -5.966320633888244629e-01 6.887505054473876953e-01 8.293733000755310059e-01 1.000000000000000000e+00 -5.944175124168395996e-01 6.840753555297851562e-01 8.269127011299133301e-01 1.000000000000000000e+00 -5.922030210494995117e-01 6.794002056121826172e-01 8.244521617889404297e-01 1.000000000000000000e+00 -5.899884700775146484e-01 6.747251152992248535e-01 8.219915628433227539e-01 1.000000000000000000e+00 -5.877739191055297852e-01 6.700499653816223145e-01 8.195309638977050781e-01 1.000000000000000000e+00 -5.855594277381896973e-01 6.653748750686645508e-01 8.170703649520874023e-01 1.000000000000000000e+00 -5.833448767662048340e-01 6.606997251510620117e-01 8.146097660064697266e-01 1.000000000000000000e+00 -5.811303257942199707e-01 6.560246348381042480e-01 8.121491670608520508e-01 1.000000000000000000e+00 -5.789157748222351074e-01 6.513494849205017090e-01 8.096885681152343750e-01 1.000000000000000000e+00 -5.767012834548950195e-01 6.466743350028991699e-01 8.072279691696166992e-01 1.000000000000000000e+00 -5.744867324829101562e-01 6.419992446899414062e-01 8.047673702239990234e-01 1.000000000000000000e+00 -5.722721815109252930e-01 6.373240947723388672e-01 8.023068308830261230e-01 1.000000000000000000e+00 -5.700576901435852051e-01 6.326490044593811035e-01 7.998462319374084473e-01 1.000000000000000000e+00 -5.678431391716003418e-01 6.279738545417785645e-01 7.973856329917907715e-01 1.000000000000000000e+00 -5.656285881996154785e-01 6.232987046241760254e-01 7.949250340461730957e-01 1.000000000000000000e+00 -5.634140968322753906e-01 6.186236143112182617e-01 7.924644351005554199e-01 1.000000000000000000e+00 -5.611995458602905273e-01 6.139484643936157227e-01 7.900038361549377441e-01 1.000000000000000000e+00 -5.589849948883056641e-01 6.092733740806579590e-01 7.875432372093200684e-01 1.000000000000000000e+00 -5.567704439163208008e-01 6.045982241630554199e-01 7.850826382637023926e-01 1.000000000000000000e+00 -5.545559525489807129e-01 5.999231338500976562e-01 7.826220393180847168e-01 1.000000000000000000e+00 -5.523414015769958496e-01 5.952479839324951172e-01 7.801614999771118164e-01 1.000000000000000000e+00 -5.501268506050109863e-01 5.905728340148925781e-01 7.777009010314941406e-01 1.000000000000000000e+00 -5.490196347236633301e-01 5.855901837348937988e-01 7.751787900924682617e-01 1.000000000000000000e+00 -5.490196347236633301e-01 5.802999138832092285e-01 7.725951671600341797e-01 1.000000000000000000e+00 -5.490196347236633301e-01 5.750095844268798828e-01 7.700115442276000977e-01 1.000000000000000000e+00 -5.490196347236633301e-01 5.697193145751953125e-01 7.674279212951660156e-01 1.000000000000000000e+00 -5.490196347236633301e-01 5.644290447235107422e-01 7.648442983627319336e-01 1.000000000000000000e+00 -5.490196347236633301e-01 5.591387748718261719e-01 7.622606754302978516e-01 1.000000000000000000e+00 -5.490196347236633301e-01 5.538485050201416016e-01 7.596770524978637695e-01 1.000000000000000000e+00 -5.490196347236633301e-01 5.485582351684570312e-01 7.570934295654296875e-01 1.000000000000000000e+00 -5.490196347236633301e-01 5.432679653167724609e-01 7.545098066329956055e-01 1.000000000000000000e+00 -5.490196347236633301e-01 5.379776954650878906e-01 7.519261837005615234e-01 1.000000000000000000e+00 -5.490196347236633301e-01 5.326874256134033203e-01 7.493425607681274414e-01 1.000000000000000000e+00 -5.490196347236633301e-01 5.273971557617187500e-01 7.467589378356933594e-01 1.000000000000000000e+00 -5.490196347236633301e-01 5.221068859100341797e-01 7.441753149032592773e-01 1.000000000000000000e+00 -5.490196347236633301e-01 5.168166160583496094e-01 7.415916919708251953e-01 1.000000000000000000e+00 -5.490196347236633301e-01 5.115263462066650391e-01 7.390080690383911133e-01 1.000000000000000000e+00 -5.490196347236633301e-01 5.062360763549804688e-01 7.364244461059570312e-01 1.000000000000000000e+00 -5.490196347236633301e-01 5.009458065032958984e-01 7.338408231735229492e-01 1.000000000000000000e+00 -5.490196347236633301e-01 4.956555068492889404e-01 7.312572002410888672e-01 1.000000000000000000e+00 -5.490196347236633301e-01 4.903652369976043701e-01 7.286735773086547852e-01 1.000000000000000000e+00 -5.490196347236633301e-01 4.850749671459197998e-01 7.260899543762207031e-01 1.000000000000000000e+00 -5.490196347236633301e-01 4.797846972942352295e-01 7.235063314437866211e-01 1.000000000000000000e+00 -5.490196347236633301e-01 4.744944274425506592e-01 7.209227085113525391e-01 1.000000000000000000e+00 -5.490196347236633301e-01 4.692041575908660889e-01 7.183390855789184570e-01 1.000000000000000000e+00 -5.490196347236633301e-01 4.639138877391815186e-01 7.157554626464843750e-01 1.000000000000000000e+00 -5.490196347236633301e-01 4.586236178874969482e-01 7.131718397140502930e-01 1.000000000000000000e+00 -5.490196347236633301e-01 4.533333480358123779e-01 7.105882167816162109e-01 1.000000000000000000e+00 -5.490196347236633301e-01 4.480430483818054199e-01 7.080045938491821289e-01 1.000000000000000000e+00 -5.490196347236633301e-01 4.427527785301208496e-01 7.054209709167480469e-01 1.000000000000000000e+00 -5.490196347236633301e-01 4.374625086784362793e-01 7.028373479843139648e-01 1.000000000000000000e+00 -5.490196347236633301e-01 4.321722388267517090e-01 7.002537250518798828e-01 1.000000000000000000e+00 -5.490196347236633301e-01 4.268819689750671387e-01 6.976701021194458008e-01 1.000000000000000000e+00 -5.490196347236633301e-01 4.215916991233825684e-01 6.950864791870117188e-01 1.000000000000000000e+00 -5.487120151519775391e-01 4.163783192634582520e-01 6.925798058509826660e-01 1.000000000000000000e+00 -5.482199192047119141e-01 4.112110733985900879e-01 6.901192069053649902e-01 1.000000000000000000e+00 -5.477278232574462891e-01 4.060438275337219238e-01 6.876586079597473145e-01 1.000000000000000000e+00 -5.472356677055358887e-01 4.008765816688537598e-01 6.851980090141296387e-01 1.000000000000000000e+00 -5.467435717582702637e-01 3.957093358039855957e-01 6.827374100685119629e-01 1.000000000000000000e+00 -5.462514162063598633e-01 3.905420899391174316e-01 6.802768111228942871e-01 1.000000000000000000e+00 -5.457593202590942383e-01 3.853748440742492676e-01 6.778162121772766113e-01 1.000000000000000000e+00 -5.452672243118286133e-01 3.802075982093811035e-01 6.753556132316589355e-01 1.000000000000000000e+00 -5.447750687599182129e-01 3.750403821468353271e-01 6.728950142860412598e-01 1.000000000000000000e+00 -5.442829728126525879e-01 3.698731362819671631e-01 6.704344749450683594e-01 1.000000000000000000e+00 -5.437908768653869629e-01 3.647058904170989990e-01 6.679738759994506836e-01 1.000000000000000000e+00 -5.432987213134765625e-01 3.595386445522308350e-01 6.655132770538330078e-01 1.000000000000000000e+00 -5.428066253662109375e-01 3.543713986873626709e-01 6.630526781082153320e-01 1.000000000000000000e+00 -5.423144698143005371e-01 3.492041528224945068e-01 6.605920791625976562e-01 1.000000000000000000e+00 -5.418223738670349121e-01 3.440369069576263428e-01 6.581314802169799805e-01 1.000000000000000000e+00 -5.413302779197692871e-01 3.388696610927581787e-01 6.556708812713623047e-01 1.000000000000000000e+00 -5.408381223678588867e-01 3.337024152278900146e-01 6.532102823257446289e-01 1.000000000000000000e+00 -5.403460264205932617e-01 3.285351693630218506e-01 6.507496833801269531e-01 1.000000000000000000e+00 -5.398539304733276367e-01 3.233679234981536865e-01 6.482891440391540527e-01 1.000000000000000000e+00 -5.393617749214172363e-01 3.182006776332855225e-01 6.458285450935363770e-01 1.000000000000000000e+00 -5.388696789741516113e-01 3.130334615707397461e-01 6.433679461479187012e-01 1.000000000000000000e+00 -5.383775234222412109e-01 3.078662157058715820e-01 6.409073472023010254e-01 1.000000000000000000e+00 -5.378854274749755859e-01 3.026989698410034180e-01 6.384467482566833496e-01 1.000000000000000000e+00 -5.373933315277099609e-01 2.975317239761352539e-01 6.359861493110656738e-01 1.000000000000000000e+00 -5.369011759757995605e-01 2.923644781112670898e-01 6.335255503654479980e-01 1.000000000000000000e+00 -5.364090800285339355e-01 2.871972322463989258e-01 6.310649514198303223e-01 1.000000000000000000e+00 -5.359169840812683105e-01 2.820299863815307617e-01 6.286044120788574219e-01 1.000000000000000000e+00 -5.354248285293579102e-01 2.768627405166625977e-01 6.261438131332397461e-01 1.000000000000000000e+00 -5.349327325820922852e-01 2.716954946517944336e-01 6.236832141876220703e-01 1.000000000000000000e+00 -5.344405770301818848e-01 2.665282487869262695e-01 6.212226152420043945e-01 1.000000000000000000e+00 -5.339484810829162598e-01 2.613610029220581055e-01 6.187620162963867188e-01 1.000000000000000000e+00 -5.334563851356506348e-01 2.561937570571899414e-01 6.163014173507690430e-01 1.000000000000000000e+00 -5.326874256134033203e-01 2.502883374691009521e-01 6.126412749290466309e-01 1.000000000000000000e+00 -5.318261981010437012e-01 2.441368699073791504e-01 6.085813045501708984e-01 1.000000000000000000e+00 -5.309650301933288574e-01 2.379853874444961548e-01 6.045213341712951660e-01 1.000000000000000000e+00 -5.301038026809692383e-01 2.318339049816131592e-01 6.004613637924194336e-01 1.000000000000000000e+00 -5.292425751686096191e-01 2.256824225187301636e-01 5.964013934135437012e-01 1.000000000000000000e+00 -5.283814072608947754e-01 2.195309549570083618e-01 5.923414230346679688e-01 1.000000000000000000e+00 -5.275201797485351562e-01 2.133794724941253662e-01 5.882814526557922363e-01 1.000000000000000000e+00 -5.266589522361755371e-01 2.072279900312423706e-01 5.842214822769165039e-01 1.000000000000000000e+00 -5.257977843284606934e-01 2.010765075683593750e-01 5.801614522933959961e-01 1.000000000000000000e+00 -5.249365568161010742e-01 1.949250251054763794e-01 5.761014819145202637e-01 1.000000000000000000e+00 -5.240753293037414551e-01 1.887735426425933838e-01 5.720415115356445312e-01 1.000000000000000000e+00 -5.232141613960266113e-01 1.826220750808715820e-01 5.679815411567687988e-01 1.000000000000000000e+00 -5.223529338836669922e-01 1.764705926179885864e-01 5.639215707778930664e-01 1.000000000000000000e+00 -5.214917063713073730e-01 1.703191101551055908e-01 5.598616003990173340e-01 1.000000000000000000e+00 -5.206305384635925293e-01 1.641676276922225952e-01 5.558016300201416016e-01 1.000000000000000000e+00 -5.197693109512329102e-01 1.580161452293395996e-01 5.517416596412658691e-01 1.000000000000000000e+00 -5.189080834388732910e-01 1.518646627664566040e-01 5.476816892623901367e-01 1.000000000000000000e+00 -5.180469155311584473e-01 1.457131803035736084e-01 5.436216592788696289e-01 1.000000000000000000e+00 -5.171856880187988281e-01 1.395617127418518066e-01 5.395616888999938965e-01 1.000000000000000000e+00 -5.163245201110839844e-01 1.334102302789688110e-01 5.355017185211181641e-01 1.000000000000000000e+00 -5.154632925987243652e-01 1.272587478160858154e-01 5.314417481422424316e-01 1.000000000000000000e+00 -5.146020650863647461e-01 1.211072653532028198e-01 5.273817777633666992e-01 1.000000000000000000e+00 -5.137408971786499023e-01 1.149557828903198242e-01 5.233218073844909668e-01 1.000000000000000000e+00 -5.128796696662902832e-01 1.088043078780174255e-01 5.192618370056152344e-01 1.000000000000000000e+00 -5.120184421539306641e-01 1.026528254151344299e-01 5.152018666267395020e-01 1.000000000000000000e+00 -5.111572742462158203e-01 9.650134295225143433e-02 5.111418962478637695e-01 1.000000000000000000e+00 -5.102960467338562012e-01 9.034986793994903564e-02 5.070818662643432617e-01 1.000000000000000000e+00 -5.094348192214965820e-01 8.419838547706604004e-02 5.030218958854675293e-01 1.000000000000000000e+00 -5.085736513137817383e-01 7.804690301418304443e-02 4.989619255065917969e-01 1.000000000000000000e+00 -5.077124238014221191e-01 7.189542800188064575e-02 4.949019551277160645e-01 1.000000000000000000e+00 -5.068511962890625000e-01 6.574394553899765015e-02 4.908419847488403320e-01 1.000000000000000000e+00 -5.059900283813476562e-01 5.959246307611465454e-02 4.867820143699645996e-01 1.000000000000000000e+00 -5.002844929695129395e-01 5.720876529812812805e-02 4.809996187686920166e-01 1.000000000000000000e+00 -4.938869774341583252e-01 5.536332353949546814e-02 4.749711751937866211e-01 1.000000000000000000e+00 -4.874894320964813232e-01 5.351787805557250977e-02 4.689427018165588379e-01 1.000000000000000000e+00 -4.810918867588043213e-01 5.167243257164955139e-02 4.629142582416534424e-01 1.000000000000000000e+00 -4.746943414211273193e-01 4.982699081301689148e-02 4.568858146667480469e-01 1.000000000000000000e+00 -4.682967960834503174e-01 4.798154532909393311e-02 4.508573710918426514e-01 1.000000000000000000e+00 -4.618992805480957031e-01 4.613609984517097473e-02 4.448288977146148682e-01 1.000000000000000000e+00 -4.555017352104187012e-01 4.429065808653831482e-02 4.388004541397094727e-01 1.000000000000000000e+00 -4.491041898727416992e-01 4.244521260261535645e-02 4.327720105648040771e-01 1.000000000000000000e+00 -4.427066445350646973e-01 4.059977084398269653e-02 4.267435669898986816e-01 1.000000000000000000e+00 -4.363090991973876953e-01 3.875432536005973816e-02 4.207151234149932861e-01 1.000000000000000000e+00 -4.299115836620330811e-01 3.690887987613677979e-02 4.146866500377655029e-01 1.000000000000000000e+00 -4.235140383243560791e-01 3.506343811750411987e-02 4.086582064628601074e-01 1.000000000000000000e+00 -4.171164929866790771e-01 3.321799263358116150e-02 4.026297628879547119e-01 1.000000000000000000e+00 -4.107189476490020752e-01 3.137255087494850159e-02 3.966013193130493164e-01 1.000000000000000000e+00 -4.043214023113250732e-01 2.952710539102554321e-02 3.905728459358215332e-01 1.000000000000000000e+00 -3.979238867759704590e-01 2.768166176974773407e-02 3.845444023609161377e-01 1.000000000000000000e+00 -3.915263414382934570e-01 2.583621628582477570e-02 3.785159587860107422e-01 1.000000000000000000e+00 -3.851287961006164551e-01 2.399077266454696655e-02 3.724875152111053467e-01 1.000000000000000000e+00 -3.787312507629394531e-01 2.214532904326915741e-02 3.664590418338775635e-01 1.000000000000000000e+00 -3.723337054252624512e-01 2.029988542199134827e-02 3.604305982589721680e-01 1.000000000000000000e+00 -3.659361898899078369e-01 1.845443993806838989e-02 3.544021546840667725e-01 1.000000000000000000e+00 -3.595386445522308350e-01 1.660899631679058075e-02 3.483737111091613770e-01 1.000000000000000000e+00 -3.531410992145538330e-01 1.476355269551277161e-02 3.423452377319335938e-01 1.000000000000000000e+00 -3.467435538768768311e-01 1.291810814291238785e-02 3.363167941570281982e-01 1.000000000000000000e+00 -3.403460085391998291e-01 1.107266452163457870e-02 3.302883505821228027e-01 1.000000000000000000e+00 -3.339484930038452148e-01 9.227219969034194946e-03 3.242599070072174072e-01 1.000000000000000000e+00 -3.275509476661682129e-01 7.381776347756385803e-03 3.182314634323120117e-01 1.000000000000000000e+00 -3.211534023284912109e-01 5.536332260817289352e-03 3.122029900550842285e-01 1.000000000000000000e+00 -3.147558569908142090e-01 3.690888173878192902e-03 3.061745464801788330e-01 1.000000000000000000e+00 -3.083583116531372070e-01 1.845444086939096451e-03 3.001461029052734375e-01 1.000000000000000000e+00 -3.019607961177825928e-01 0.000000000000000000e+00 2.941176593303680420e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/CMRmap b/fastplotlib/utils/colormaps/CMRmap deleted file mode 100644 index a5fb9dac1..000000000 --- a/fastplotlib/utils/colormaps/CMRmap +++ /dev/null @@ -1,256 +0,0 @@ -0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.705882165580987930e-03 4.705882165580987930e-03 1.568627543747425079e-02 1.000000000000000000e+00 -9.411764331161975861e-03 9.411764331161975861e-03 3.137255087494850159e-02 1.000000000000000000e+00 -1.411764696240425110e-02 1.411764696240425110e-02 4.705882444977760315e-02 1.000000000000000000e+00 -1.882352866232395172e-02 1.882352866232395172e-02 6.274510174989700317e-02 1.000000000000000000e+00 -2.352941222488880157e-02 2.352941222488880157e-02 7.843137532472610474e-02 1.000000000000000000e+00 -2.823529392480850220e-02 2.823529392480850220e-02 9.411764889955520630e-02 1.000000000000000000e+00 -3.294117748737335205e-02 3.294117748737335205e-02 1.098039224743843079e-01 1.000000000000000000e+00 -3.764705732464790344e-02 3.764705732464790344e-02 1.254902034997940063e-01 1.000000000000000000e+00 -4.235294088721275330e-02 4.235294088721275330e-02 1.411764770746231079e-01 1.000000000000000000e+00 -4.705882444977760315e-02 4.705882444977760315e-02 1.568627506494522095e-01 1.000000000000000000e+00 -5.176470428705215454e-02 5.176470428705215454e-02 1.725490242242813110e-01 1.000000000000000000e+00 -5.647058784961700439e-02 5.647058784961700439e-02 1.882352977991104126e-01 1.000000000000000000e+00 -6.117647141218185425e-02 6.117647141218185425e-02 2.039215713739395142e-01 1.000000000000000000e+00 -6.588235497474670410e-02 6.588235497474670410e-02 2.196078449487686157e-01 1.000000000000000000e+00 -7.058823853731155396e-02 7.058823853731155396e-02 2.352941185235977173e-01 1.000000000000000000e+00 -7.529411464929580688e-02 7.529411464929580688e-02 2.509804069995880127e-01 1.000000000000000000e+00 -7.999999821186065674e-02 7.999999821186065674e-02 2.666666805744171143e-01 1.000000000000000000e+00 -8.470588177442550659e-02 8.470588177442550659e-02 2.823529541492462158e-01 1.000000000000000000e+00 -8.941176533699035645e-02 8.941176533699035645e-02 2.980392277240753174e-01 1.000000000000000000e+00 -9.411764889955520630e-02 9.411764889955520630e-02 3.137255012989044189e-01 1.000000000000000000e+00 -9.882353246212005615e-02 9.882353246212005615e-02 3.294117748737335205e-01 1.000000000000000000e+00 -1.035294085741043091e-01 1.035294085741043091e-01 3.450980484485626221e-01 1.000000000000000000e+00 -1.082352921366691589e-01 1.082352921366691589e-01 3.607843220233917236e-01 1.000000000000000000e+00 -1.129411756992340088e-01 1.129411756992340088e-01 3.764705955982208252e-01 1.000000000000000000e+00 -1.176470592617988586e-01 1.176470592617988586e-01 3.921568691730499268e-01 1.000000000000000000e+00 -1.223529428243637085e-01 1.223529428243637085e-01 4.078431427478790283e-01 1.000000000000000000e+00 -1.270588189363479614e-01 1.270588189363479614e-01 4.235294163227081299e-01 1.000000000000000000e+00 -1.317647099494934082e-01 1.317647099494934082e-01 4.392156898975372314e-01 1.000000000000000000e+00 -1.364705860614776611e-01 1.364705860614776611e-01 4.549019634723663330e-01 1.000000000000000000e+00 -1.411764770746231079e-01 1.411764770746231079e-01 4.705882370471954346e-01 1.000000000000000000e+00 -1.458823531866073608e-01 1.458823531866073608e-01 4.862745106220245361e-01 1.000000000000000000e+00 -1.505882292985916138e-01 1.500000059604644775e-01 5.009803771972656250e-01 1.000000000000000000e+00 -1.552941203117370605e-01 1.500000059604644775e-01 5.088235139846801758e-01 1.000000000000000000e+00 -1.599999964237213135e-01 1.500000059604644775e-01 5.166666507720947266e-01 1.000000000000000000e+00 -1.647058874368667603e-01 1.500000059604644775e-01 5.245097875595092773e-01 1.000000000000000000e+00 -1.694117635488510132e-01 1.500000059604644775e-01 5.323529243469238281e-01 1.000000000000000000e+00 -1.741176396608352661e-01 1.500000059604644775e-01 5.401960611343383789e-01 1.000000000000000000e+00 -1.788235306739807129e-01 1.500000059604644775e-01 5.480391979217529297e-01 1.000000000000000000e+00 -1.835294067859649658e-01 1.500000059604644775e-01 5.558823347091674805e-01 1.000000000000000000e+00 -1.882352977991104126e-01 1.500000059604644775e-01 5.637254714965820312e-01 1.000000000000000000e+00 -1.929411739110946655e-01 1.500000059604644775e-01 5.715686082839965820e-01 1.000000000000000000e+00 -1.976470649242401123e-01 1.500000059604644775e-01 5.794117450714111328e-01 1.000000000000000000e+00 -2.023529410362243652e-01 1.500000059604644775e-01 5.872548818588256836e-01 1.000000000000000000e+00 -2.070588171482086182e-01 1.500000059604644775e-01 5.950980186462402344e-01 1.000000000000000000e+00 -2.117647081613540649e-01 1.500000059604644775e-01 6.029411554336547852e-01 1.000000000000000000e+00 -2.164705842733383179e-01 1.500000059604644775e-01 6.107842922210693359e-01 1.000000000000000000e+00 -2.211764752864837646e-01 1.500000059604644775e-01 6.186274290084838867e-01 1.000000000000000000e+00 -2.258823513984680176e-01 1.500000059604644775e-01 6.264705657958984375e-01 1.000000000000000000e+00 -2.305882424116134644e-01 1.500000059604644775e-01 6.343137025833129883e-01 1.000000000000000000e+00 -2.352941185235977173e-01 1.500000059604644775e-01 6.421568393707275391e-01 1.000000000000000000e+00 -2.399999946355819702e-01 1.500000059604644775e-01 6.499999761581420898e-01 1.000000000000000000e+00 -2.447058856487274170e-01 1.500000059604644775e-01 6.578431129455566406e-01 1.000000000000000000e+00 -2.494117617607116699e-01 1.500000059604644775e-01 6.656862497329711914e-01 1.000000000000000000e+00 -2.541176378726959229e-01 1.500000059604644775e-01 6.735293865203857422e-01 1.000000000000000000e+00 -2.588235437870025635e-01 1.500000059604644775e-01 6.813725233078002930e-01 1.000000000000000000e+00 -2.635294198989868164e-01 1.500000059604644775e-01 6.892156600952148438e-01 1.000000000000000000e+00 -2.682352960109710693e-01 1.500000059604644775e-01 6.970587968826293945e-01 1.000000000000000000e+00 -2.729411721229553223e-01 1.500000059604644775e-01 7.049019336700439453e-01 1.000000000000000000e+00 -2.776470482349395752e-01 1.500000059604644775e-01 7.127450704574584961e-01 1.000000000000000000e+00 -2.823529541492462158e-01 1.500000059604644775e-01 7.205882072448730469e-01 1.000000000000000000e+00 -2.870588302612304688e-01 1.500000059604644775e-01 7.284313440322875977e-01 1.000000000000000000e+00 -2.917647063732147217e-01 1.500000059604644775e-01 7.362744808197021484e-01 1.000000000000000000e+00 -2.964705824851989746e-01 1.500000059604644775e-01 7.441176176071166992e-01 1.000000000000000000e+00 -3.023529350757598877e-01 1.503921598196029663e-01 7.480391860008239746e-01 1.000000000000000000e+00 -3.117647171020507812e-01 1.519607901573181152e-01 7.401960492134094238e-01 1.000000000000000000e+00 -3.211764693260192871e-01 1.535294055938720703e-01 7.323529124259948730e-01 1.000000000000000000e+00 -3.305882215499877930e-01 1.550980359315872192e-01 7.245097756385803223e-01 1.000000000000000000e+00 -3.400000035762786865e-01 1.566666662693023682e-01 7.166666388511657715e-01 1.000000000000000000e+00 -3.494117558002471924e-01 1.582352966070175171e-01 7.088235020637512207e-01 1.000000000000000000e+00 -3.588235378265380859e-01 1.598039269447326660e-01 7.009803652763366699e-01 1.000000000000000000e+00 -3.682352900505065918e-01 1.613725423812866211e-01 6.931372284889221191e-01 1.000000000000000000e+00 -3.776470720767974854e-01 1.629411727190017700e-01 6.852940917015075684e-01 1.000000000000000000e+00 -3.870588243007659912e-01 1.645098030567169189e-01 6.774509549140930176e-01 1.000000000000000000e+00 -3.964705765247344971e-01 1.660784333944320679e-01 6.696078181266784668e-01 1.000000000000000000e+00 -4.058823585510253906e-01 1.676470637321472168e-01 6.617646813392639160e-01 1.000000000000000000e+00 -4.152941107749938965e-01 1.692156791687011719e-01 6.539215445518493652e-01 1.000000000000000000e+00 -4.247058928012847900e-01 1.707843095064163208e-01 6.460784077644348145e-01 1.000000000000000000e+00 -4.341176450252532959e-01 1.723529398441314697e-01 6.382352709770202637e-01 1.000000000000000000e+00 -4.435293972492218018e-01 1.739215701818466187e-01 6.303921341896057129e-01 1.000000000000000000e+00 -4.529411792755126953e-01 1.754902005195617676e-01 6.225489974021911621e-01 1.000000000000000000e+00 -4.623529314994812012e-01 1.770588308572769165e-01 6.147058606147766113e-01 1.000000000000000000e+00 -4.717647135257720947e-01 1.786274462938308716e-01 6.068627238273620605e-01 1.000000000000000000e+00 -4.811764657497406006e-01 1.801960766315460205e-01 5.990195870399475098e-01 1.000000000000000000e+00 -4.905882477760314941e-01 1.817647069692611694e-01 5.911764502525329590e-01 1.000000000000000000e+00 -5.000000000000000000e-01 1.833333373069763184e-01 5.833333134651184082e-01 1.000000000000000000e+00 -5.094117522239685059e-01 1.849019676446914673e-01 5.754901766777038574e-01 1.000000000000000000e+00 -5.188235044479370117e-01 1.864705830812454224e-01 5.676470398902893066e-01 1.000000000000000000e+00 -5.282353162765502930e-01 1.880392134189605713e-01 5.598039031028747559e-01 1.000000000000000000e+00 -5.376470685005187988e-01 1.896078437566757202e-01 5.519607663154602051e-01 1.000000000000000000e+00 -5.470588207244873047e-01 1.911764740943908691e-01 5.441176295280456543e-01 1.000000000000000000e+00 -5.564705729484558105e-01 1.927451044321060181e-01 5.362744927406311035e-01 1.000000000000000000e+00 -5.658823251724243164e-01 1.943137198686599731e-01 5.284313559532165527e-01 1.000000000000000000e+00 -5.752941370010375977e-01 1.958823502063751221e-01 5.205882191658020020e-01 1.000000000000000000e+00 -5.847058892250061035e-01 1.974509805440902710e-01 5.127450823783874512e-01 1.000000000000000000e+00 -5.941176414489746094e-01 1.990196108818054199e-01 5.049019455909729004e-01 1.000000000000000000e+00 -6.047058701515197754e-01 2.005882412195205688e-01 4.958823621273040771e-01 1.000000000000000000e+00 -6.172549128532409668e-01 2.021568566560745239e-01 4.849019646644592285e-01 1.000000000000000000e+00 -6.298038959503173828e-01 2.037254869937896729e-01 4.739215672016143799e-01 1.000000000000000000e+00 -6.423529386520385742e-01 2.052941173315048218e-01 4.629411697387695312e-01 1.000000000000000000e+00 -6.549019813537597656e-01 2.068627476692199707e-01 4.519607722759246826e-01 1.000000000000000000e+00 -6.674509644508361816e-01 2.084313780069351196e-01 4.409804046154022217e-01 1.000000000000000000e+00 -6.800000071525573730e-01 2.099999934434890747e-01 4.300000071525573730e-01 1.000000000000000000e+00 -6.925489902496337891e-01 2.115686237812042236e-01 4.190196096897125244e-01 1.000000000000000000e+00 -7.050980329513549805e-01 2.131372541189193726e-01 4.080392122268676758e-01 1.000000000000000000e+00 -7.176470756530761719e-01 2.147058844566345215e-01 3.970588147640228271e-01 1.000000000000000000e+00 -7.301960587501525879e-01 2.162745147943496704e-01 3.860784173011779785e-01 1.000000000000000000e+00 -7.427451014518737793e-01 2.178431302309036255e-01 3.750980496406555176e-01 1.000000000000000000e+00 -7.552941441535949707e-01 2.194117605686187744e-01 3.641176521778106689e-01 1.000000000000000000e+00 -7.678431272506713867e-01 2.209803909063339233e-01 3.531372547149658203e-01 1.000000000000000000e+00 -7.803921699523925781e-01 2.225490212440490723e-01 3.421568572521209717e-01 1.000000000000000000e+00 -7.929411530494689941e-01 2.241176515817642212e-01 3.311764597892761230e-01 1.000000000000000000e+00 -8.054901957511901855e-01 2.256862819194793701e-01 3.201960921287536621e-01 1.000000000000000000e+00 -8.180392384529113770e-01 2.272548973560333252e-01 3.092156946659088135e-01 1.000000000000000000e+00 -8.305882215499877930e-01 2.288235276937484741e-01 2.982352972030639648e-01 1.000000000000000000e+00 -8.431372642517089844e-01 2.303921580314636230e-01 2.872548997402191162e-01 1.000000000000000000e+00 -8.556862473487854004e-01 2.319607883691787720e-01 2.762745022773742676e-01 1.000000000000000000e+00 -8.682352900505065918e-01 2.335294187068939209e-01 2.652941048145294189e-01 1.000000000000000000e+00 -8.807843327522277832e-01 2.350980341434478760e-01 2.543137371540069580e-01 1.000000000000000000e+00 -8.933333158493041992e-01 2.366666644811630249e-01 2.433333396911621094e-01 1.000000000000000000e+00 -9.058823585510253906e-01 2.382352948188781738e-01 2.323529422283172607e-01 1.000000000000000000e+00 -9.184314012527465820e-01 2.398039251565933228e-01 2.213725447654724121e-01 1.000000000000000000e+00 -9.309803843498229980e-01 2.413725554943084717e-01 2.103921622037887573e-01 1.000000000000000000e+00 -9.435294270515441895e-01 2.429411709308624268e-01 1.994117647409439087e-01 1.000000000000000000e+00 -9.560784101486206055e-01 2.445098012685775757e-01 1.884313672780990601e-01 1.000000000000000000e+00 -9.686274528503417969e-01 2.460784316062927246e-01 1.774509847164154053e-01 1.000000000000000000e+00 -9.811764955520629883e-01 2.476470619440078735e-01 1.664705872535705566e-01 1.000000000000000000e+00 -9.937254786491394043e-01 2.492156922817230225e-01 1.554901897907257080e-01 1.000000000000000000e+00 -9.984313845634460449e-01 2.539215683937072754e-01 1.476470530033111572e-01 1.000000000000000000e+00 -9.952940940856933594e-01 2.617647051811218262e-01 1.429411768913269043e-01 1.000000000000000000e+00 -9.921568632125854492e-01 2.696078419685363770e-01 1.382353007793426514e-01 1.000000000000000000e+00 -9.890196323394775391e-01 2.774509787559509277e-01 1.335294097661972046e-01 1.000000000000000000e+00 -9.858823418617248535e-01 2.852941155433654785e-01 1.288235336542129517e-01 1.000000000000000000e+00 -9.827451109886169434e-01 2.931372523307800293e-01 1.241176500916481018e-01 1.000000000000000000e+00 -9.796078205108642578e-01 3.009803891181945801e-01 1.194117665290832520e-01 1.000000000000000000e+00 -9.764705896377563477e-01 3.088235259056091309e-01 1.147058829665184021e-01 1.000000000000000000e+00 -9.733333587646484375e-01 3.166666626930236816e-01 1.099999994039535522e-01 1.000000000000000000e+00 -9.701960682868957520e-01 3.245097994804382324e-01 1.052941158413887024e-01 1.000000000000000000e+00 -9.670588374137878418e-01 3.323529362678527832e-01 1.005882322788238525e-01 1.000000000000000000e+00 -9.639215469360351562e-01 3.401960730552673340e-01 9.588235616683959961e-02 1.000000000000000000e+00 -9.607843160629272461e-01 3.480392098426818848e-01 9.117647260427474976e-02 1.000000000000000000e+00 -9.576470851898193359e-01 3.558823466300964355e-01 8.647058904170989990e-02 1.000000000000000000e+00 -9.545097947120666504e-01 3.637254834175109863e-01 8.176470547914505005e-02 1.000000000000000000e+00 -9.513725638389587402e-01 3.715686202049255371e-01 7.705882191658020020e-02 1.000000000000000000e+00 -9.482352733612060547e-01 3.794117569923400879e-01 7.235293835401535034e-02 1.000000000000000000e+00 -9.450980424880981445e-01 3.872548937797546387e-01 6.764706224203109741e-02 1.000000000000000000e+00 -9.419608116149902344e-01 3.950980305671691895e-01 6.294117867946624756e-02 1.000000000000000000e+00 -9.388235211372375488e-01 4.029411673545837402e-01 5.823529511690139771e-02 1.000000000000000000e+00 -9.356862902641296387e-01 4.107843041419982910e-01 5.352941155433654785e-02 1.000000000000000000e+00 -9.325489997863769531e-01 4.186274409294128418e-01 4.882352799177169800e-02 1.000000000000000000e+00 -9.294117689132690430e-01 4.264705777168273926e-01 4.411764815449714661e-02 1.000000000000000000e+00 -9.262745380401611328e-01 4.343137145042419434e-01 3.941176459193229675e-02 1.000000000000000000e+00 -9.231372475624084473e-01 4.421568512916564941e-01 3.470588102936744690e-02 1.000000000000000000e+00 -9.200000166893005371e-01 4.499999880790710449e-01 2.999999932944774628e-02 1.000000000000000000e+00 -9.168627262115478516e-01 4.578431248664855957e-01 2.529411762952804565e-02 1.000000000000000000e+00 -9.137254953384399414e-01 4.656862616539001465e-01 2.058823592960834503e-02 1.000000000000000000e+00 -9.105882644653320312e-01 4.735293984413146973e-01 1.588235236704349518e-02 1.000000000000000000e+00 -9.074509739875793457e-01 4.813725352287292480e-01 1.117647066712379456e-02 1.000000000000000000e+00 -9.043137431144714355e-01 4.892156720161437988e-01 6.470588035881519318e-03 1.000000000000000000e+00 -9.011764526367187500e-01 4.970588088035583496e-01 1.764705870300531387e-03 1.000000000000000000e+00 -8.999999761581420898e-01 5.049019455909729004e-01 1.960784429684281349e-03 1.000000000000000000e+00 -8.999999761581420898e-01 5.127450823783874512e-01 5.098039284348487854e-03 1.000000000000000000e+00 -8.999999761581420898e-01 5.205882191658020020e-01 8.235294371843338013e-03 1.000000000000000000e+00 -8.999999761581420898e-01 5.284313559532165527e-01 1.137254945933818817e-02 1.000000000000000000e+00 -8.999999761581420898e-01 5.362744927406311035e-01 1.450980361551046371e-02 1.000000000000000000e+00 -8.999999761581420898e-01 5.441176295280456543e-01 1.764705963432788849e-02 1.000000000000000000e+00 -8.999999761581420898e-01 5.519607663154602051e-01 2.078431285917758942e-02 1.000000000000000000e+00 -8.999999761581420898e-01 5.598039031028747559e-01 2.392156794667243958e-02 1.000000000000000000e+00 -8.999999761581420898e-01 5.676470398902893066e-01 2.705882303416728973e-02 1.000000000000000000e+00 -8.999999761581420898e-01 5.754901766777038574e-01 3.019607812166213989e-02 1.000000000000000000e+00 -8.999999761581420898e-01 5.833333134651184082e-01 3.333333507180213928e-02 1.000000000000000000e+00 -8.999999761581420898e-01 5.911764502525329590e-01 3.647058829665184021e-02 1.000000000000000000e+00 -8.999999761581420898e-01 5.990195870399475098e-01 3.960784152150154114e-02 1.000000000000000000e+00 -8.999999761581420898e-01 6.068627238273620605e-01 4.274509847164154053e-02 1.000000000000000000e+00 -8.999999761581420898e-01 6.147058606147766113e-01 4.588235169649124146e-02 1.000000000000000000e+00 -8.999999761581420898e-01 6.225489974021911621e-01 4.901960864663124084e-02 1.000000000000000000e+00 -8.999999761581420898e-01 6.303921341896057129e-01 5.215686187148094177e-02 1.000000000000000000e+00 -8.999999761581420898e-01 6.382352709770202637e-01 5.529411882162094116e-02 1.000000000000000000e+00 -8.999999761581420898e-01 6.460784077644348145e-01 5.843137204647064209e-02 1.000000000000000000e+00 -8.999999761581420898e-01 6.539215445518493652e-01 6.156862899661064148e-02 1.000000000000000000e+00 -8.999999761581420898e-01 6.617646813392639160e-01 6.470588594675064087e-02 1.000000000000000000e+00 -8.999999761581420898e-01 6.696078181266784668e-01 6.784313917160034180e-02 1.000000000000000000e+00 -8.999999761581420898e-01 6.774509549140930176e-01 7.098039239645004272e-02 1.000000000000000000e+00 -8.999999761581420898e-01 6.852940917015075684e-01 7.411764562129974365e-02 1.000000000000000000e+00 -8.999999761581420898e-01 6.931372284889221191e-01 7.725489884614944458e-02 1.000000000000000000e+00 -8.999999761581420898e-01 7.009803652763366699e-01 8.039215952157974243e-02 1.000000000000000000e+00 -8.999999761581420898e-01 7.088235020637512207e-01 8.352941274642944336e-02 1.000000000000000000e+00 -8.999999761581420898e-01 7.166666388511657715e-01 8.666666597127914429e-02 1.000000000000000000e+00 -8.999999761581420898e-01 7.245097756385803223e-01 8.980391919612884521e-02 1.000000000000000000e+00 -8.999999761581420898e-01 7.323529124259948730e-01 9.294117987155914307e-02 1.000000000000000000e+00 -8.999999761581420898e-01 7.401960492134094238e-01 9.607843309640884399e-02 1.000000000000000000e+00 -8.999999761581420898e-01 7.480391860008239746e-01 9.921568632125854492e-02 1.000000000000000000e+00 -8.999999761581420898e-01 7.535294294357299805e-01 1.094117611646652222e-01 1.000000000000000000e+00 -8.999999761581420898e-01 7.582352757453918457e-01 1.219607815146446228e-01 1.000000000000000000e+00 -8.999999761581420898e-01 7.629411816596984863e-01 1.345098018646240234e-01 1.000000000000000000e+00 -8.999999761581420898e-01 7.676470875740051270e-01 1.470588296651840210e-01 1.000000000000000000e+00 -8.999999761581420898e-01 7.723529338836669922e-01 1.596078425645828247e-01 1.000000000000000000e+00 -8.999999761581420898e-01 7.770588397979736328e-01 1.721568554639816284e-01 1.000000000000000000e+00 -8.999999761581420898e-01 7.817646861076354980e-01 1.847058832645416260e-01 1.000000000000000000e+00 -8.999999761581420898e-01 7.864705920219421387e-01 1.972548961639404297e-01 1.000000000000000000e+00 -8.999999761581420898e-01 7.911764979362487793e-01 2.098039239645004272e-01 1.000000000000000000e+00 -8.999999761581420898e-01 7.958823442459106445e-01 2.223529368638992310e-01 1.000000000000000000e+00 -8.999999761581420898e-01 8.005882501602172852e-01 2.349019646644592285e-01 1.000000000000000000e+00 -8.999999761581420898e-01 8.052940964698791504e-01 2.474509775638580322e-01 1.000000000000000000e+00 -8.999999761581420898e-01 8.100000023841857910e-01 2.599999904632568359e-01 1.000000000000000000e+00 -8.999999761581420898e-01 8.147059082984924316e-01 2.725490331649780273e-01 1.000000000000000000e+00 -8.999999761581420898e-01 8.194117546081542969e-01 2.850980460643768311e-01 1.000000000000000000e+00 -8.999999761581420898e-01 8.241176605224609375e-01 2.976470589637756348e-01 1.000000000000000000e+00 -8.999999761581420898e-01 8.288235068321228027e-01 3.101960718631744385e-01 1.000000000000000000e+00 -8.999999761581420898e-01 8.335294127464294434e-01 3.227450847625732422e-01 1.000000000000000000e+00 -8.999999761581420898e-01 8.382353186607360840e-01 3.352941274642944336e-01 1.000000000000000000e+00 -8.999999761581420898e-01 8.429411649703979492e-01 3.478431403636932373e-01 1.000000000000000000e+00 -8.999999761581420898e-01 8.476470708847045898e-01 3.603921532630920410e-01 1.000000000000000000e+00 -8.999999761581420898e-01 8.523529171943664551e-01 3.729411661624908447e-01 1.000000000000000000e+00 -8.999999761581420898e-01 8.570588231086730957e-01 3.854902088642120361e-01 1.000000000000000000e+00 -8.999999761581420898e-01 8.617647290229797363e-01 3.980392217636108398e-01 1.000000000000000000e+00 -8.999999761581420898e-01 8.664705753326416016e-01 4.105882346630096436e-01 1.000000000000000000e+00 -8.999999761581420898e-01 8.711764812469482422e-01 4.231372475624084473e-01 1.000000000000000000e+00 -8.999999761581420898e-01 8.758823275566101074e-01 4.356862604618072510e-01 1.000000000000000000e+00 -8.999999761581420898e-01 8.805882334709167480e-01 4.482353031635284424e-01 1.000000000000000000e+00 -8.999999761581420898e-01 8.852941393852233887e-01 4.607843160629272461e-01 1.000000000000000000e+00 -8.999999761581420898e-01 8.899999856948852539e-01 4.733333289623260498e-01 1.000000000000000000e+00 -8.999999761581420898e-01 8.947058916091918945e-01 4.858823418617248535e-01 1.000000000000000000e+00 -8.999999761581420898e-01 8.994117379188537598e-01 4.984313845634460449e-01 1.000000000000000000e+00 -9.027451276779174805e-01 9.027451276779174805e-01 5.137255191802978516e-01 1.000000000000000000e+00 -9.058823585510253906e-01 9.058823585510253906e-01 5.294117927551269531e-01 1.000000000000000000e+00 -9.090195894241333008e-01 9.090195894241333008e-01 5.450980663299560547e-01 1.000000000000000000e+00 -9.121568799018859863e-01 9.121568799018859863e-01 5.607843399047851562e-01 1.000000000000000000e+00 -9.152941107749938965e-01 9.152941107749938965e-01 5.764706134796142578e-01 1.000000000000000000e+00 -9.184314012527465820e-01 9.184314012527465820e-01 5.921568870544433594e-01 1.000000000000000000e+00 -9.215686321258544922e-01 9.215686321258544922e-01 6.078431606292724609e-01 1.000000000000000000e+00 -9.247058629989624023e-01 9.247058629989624023e-01 6.235294342041015625e-01 1.000000000000000000e+00 -9.278431534767150879e-01 9.278431534767150879e-01 6.392157077789306641e-01 1.000000000000000000e+00 -9.309803843498229980e-01 9.309803843498229980e-01 6.549019813537597656e-01 1.000000000000000000e+00 -9.341176748275756836e-01 9.341176748275756836e-01 6.705882549285888672e-01 1.000000000000000000e+00 -9.372549057006835938e-01 9.372549057006835938e-01 6.862745285034179688e-01 1.000000000000000000e+00 -9.403921365737915039e-01 9.403921365737915039e-01 7.019608020782470703e-01 1.000000000000000000e+00 -9.435294270515441895e-01 9.435294270515441895e-01 7.176470756530761719e-01 1.000000000000000000e+00 -9.466666579246520996e-01 9.466666579246520996e-01 7.333333492279052734e-01 1.000000000000000000e+00 -9.498039484024047852e-01 9.498039484024047852e-01 7.490196228027343750e-01 1.000000000000000000e+00 -9.529411792755126953e-01 9.529411792755126953e-01 7.647058963775634766e-01 1.000000000000000000e+00 -9.560784101486206055e-01 9.560784101486206055e-01 7.803921699523925781e-01 1.000000000000000000e+00 -9.592157006263732910e-01 9.592157006263732910e-01 7.960784435272216797e-01 1.000000000000000000e+00 -9.623529314994812012e-01 9.623529314994812012e-01 8.117647171020507812e-01 1.000000000000000000e+00 -9.654902219772338867e-01 9.654902219772338867e-01 8.274509906768798828e-01 1.000000000000000000e+00 -9.686274528503417969e-01 9.686274528503417969e-01 8.431372642517089844e-01 1.000000000000000000e+00 -9.717646837234497070e-01 9.717646837234497070e-01 8.588235378265380859e-01 1.000000000000000000e+00 -9.749019742012023926e-01 9.749019742012023926e-01 8.745098114013671875e-01 1.000000000000000000e+00 -9.780392050743103027e-01 9.780392050743103027e-01 8.901960849761962891e-01 1.000000000000000000e+00 -9.811764955520629883e-01 9.811764955520629883e-01 9.058823585510253906e-01 1.000000000000000000e+00 -9.843137264251708984e-01 9.843137264251708984e-01 9.215686321258544922e-01 1.000000000000000000e+00 -9.874509572982788086e-01 9.874509572982788086e-01 9.372549057006835938e-01 1.000000000000000000e+00 -9.905882477760314941e-01 9.905882477760314941e-01 9.529411792755126953e-01 1.000000000000000000e+00 -9.937254786491394043e-01 9.937254786491394043e-01 9.686274528503417969e-01 1.000000000000000000e+00 -9.968627691268920898e-01 9.968627691268920898e-01 9.843137264251708984e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/Dark2 b/fastplotlib/utils/colormaps/Dark2 deleted file mode 100644 index 08f686764..000000000 --- a/fastplotlib/utils/colormaps/Dark2 +++ /dev/null @@ -1,8 +0,0 @@ -1.058823540806770325e-01 6.196078658103942871e-01 4.666666686534881592e-01 1.000000000000000000e+00 -8.509804010391235352e-01 3.725490272045135498e-01 7.843137718737125397e-03 1.000000000000000000e+00 -4.588235318660736084e-01 4.392156898975372314e-01 7.019608020782470703e-01 1.000000000000000000e+00 -9.058823585510253906e-01 1.607843190431594849e-01 5.411764979362487793e-01 1.000000000000000000e+00 -4.000000059604644775e-01 6.509804129600524902e-01 1.176470592617988586e-01 1.000000000000000000e+00 -9.019607901573181152e-01 6.705882549285888672e-01 7.843137718737125397e-03 1.000000000000000000e+00 -6.509804129600524902e-01 4.627451002597808838e-01 1.137254908680915833e-01 1.000000000000000000e+00 -4.000000059604644775e-01 4.000000059604644775e-01 4.000000059604644775e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/GnBu b/fastplotlib/utils/colormaps/GnBu deleted file mode 100644 index a1d789dd7..000000000 --- a/fastplotlib/utils/colormaps/GnBu +++ /dev/null @@ -1,256 +0,0 @@ -9.686274528503417969e-01 9.882352948188781738e-01 9.411764740943908691e-01 1.000000000000000000e+00 -9.657977819442749023e-01 9.871280193328857422e-01 9.385928511619567871e-01 1.000000000000000000e+00 -9.629681110382080078e-01 9.860207438468933105e-01 9.360092282295227051e-01 1.000000000000000000e+00 -9.601383805274963379e-01 9.849134683609008789e-01 9.334256052970886230e-01 1.000000000000000000e+00 -9.573087096214294434e-01 9.838062524795532227e-01 9.308419823646545410e-01 1.000000000000000000e+00 -9.544790387153625488e-01 9.826989769935607910e-01 9.282583594322204590e-01 1.000000000000000000e+00 -9.516493678092956543e-01 9.815917015075683594e-01 9.256747364997863770e-01 1.000000000000000000e+00 -9.488196969032287598e-01 9.804844260215759277e-01 9.230911135673522949e-01 1.000000000000000000e+00 -9.459900259971618652e-01 9.793771505355834961e-01 9.205074906349182129e-01 1.000000000000000000e+00 -9.431602954864501953e-01 9.782698750495910645e-01 9.179238677024841309e-01 1.000000000000000000e+00 -9.403306245803833008e-01 9.771626591682434082e-01 9.153402447700500488e-01 1.000000000000000000e+00 -9.375009536743164062e-01 9.760553836822509766e-01 9.127566218376159668e-01 1.000000000000000000e+00 -9.346712827682495117e-01 9.749481081962585449e-01 9.101729989051818848e-01 1.000000000000000000e+00 -9.318416118621826172e-01 9.738408327102661133e-01 9.075893759727478027e-01 1.000000000000000000e+00 -9.290119409561157227e-01 9.727335572242736816e-01 9.050057530403137207e-01 1.000000000000000000e+00 -9.261822104454040527e-01 9.716262817382812500e-01 9.024221301078796387e-01 1.000000000000000000e+00 -9.233525395393371582e-01 9.705190062522888184e-01 8.998385071754455566e-01 1.000000000000000000e+00 -9.205228686332702637e-01 9.694117903709411621e-01 8.972548842430114746e-01 1.000000000000000000e+00 -9.176931977272033691e-01 9.683045148849487305e-01 8.946712613105773926e-01 1.000000000000000000e+00 -9.148635268211364746e-01 9.671972393989562988e-01 8.920876383781433105e-01 1.000000000000000000e+00 -9.120338559150695801e-01 9.660899639129638672e-01 8.895040154457092285e-01 1.000000000000000000e+00 -9.092041254043579102e-01 9.649826884269714355e-01 8.869203925132751465e-01 1.000000000000000000e+00 -9.063744544982910156e-01 9.638754129409790039e-01 8.843367695808410645e-01 1.000000000000000000e+00 -9.035447835922241211e-01 9.627681374549865723e-01 8.817531466484069824e-01 1.000000000000000000e+00 -9.007151126861572266e-01 9.616609215736389160e-01 8.791695237159729004e-01 1.000000000000000000e+00 -8.978854417800903320e-01 9.605536460876464844e-01 8.765859007835388184e-01 1.000000000000000000e+00 -8.950557708740234375e-01 9.594463706016540527e-01 8.740022778511047363e-01 1.000000000000000000e+00 -8.922260403633117676e-01 9.583390951156616211e-01 8.714187145233154297e-01 1.000000000000000000e+00 -8.893963694572448730e-01 9.572318196296691895e-01 8.688350915908813477e-01 1.000000000000000000e+00 -8.865666985511779785e-01 9.561245441436767578e-01 8.662514686584472656e-01 1.000000000000000000e+00 -8.837370276451110840e-01 9.550173282623291016e-01 8.636678457260131836e-01 1.000000000000000000e+00 -8.809073567390441895e-01 9.539100527763366699e-01 8.610842227935791016e-01 1.000000000000000000e+00 -8.781238198280334473e-01 9.528181552886962891e-01 8.584852218627929688e-01 1.000000000000000000e+00 -8.756632208824157715e-01 9.518339037895202637e-01 8.557785749435424805e-01 1.000000000000000000e+00 -8.732026219367980957e-01 9.508496522903442383e-01 8.530718684196472168e-01 1.000000000000000000e+00 -8.707420229911804199e-01 9.498654603958129883e-01 8.503652215003967285e-01 1.000000000000000000e+00 -8.682814240455627441e-01 9.488812088966369629e-01 8.476585745811462402e-01 1.000000000000000000e+00 -8.658208250999450684e-01 9.478969573974609375e-01 8.449519276618957520e-01 1.000000000000000000e+00 -8.633602261543273926e-01 9.469127058982849121e-01 8.422452807426452637e-01 1.000000000000000000e+00 -8.608996272087097168e-01 9.459285140037536621e-01 8.395386338233947754e-01 1.000000000000000000e+00 -8.584390878677368164e-01 9.449442625045776367e-01 8.368319869041442871e-01 1.000000000000000000e+00 -8.559784889221191406e-01 9.439600110054016113e-01 8.341253399848937988e-01 1.000000000000000000e+00 -8.535178899765014648e-01 9.429757595062255859e-01 8.314186930656433105e-01 1.000000000000000000e+00 -8.510572910308837891e-01 9.419915676116943359e-01 8.287120461463928223e-01 1.000000000000000000e+00 -8.485966920852661133e-01 9.410073161125183105e-01 8.260053992271423340e-01 1.000000000000000000e+00 -8.461360931396484375e-01 9.400230646133422852e-01 8.232987523078918457e-01 1.000000000000000000e+00 -8.436754941940307617e-01 9.390388131141662598e-01 8.205921053886413574e-01 1.000000000000000000e+00 -8.412148952484130859e-01 9.380546212196350098e-01 8.178854584693908691e-01 1.000000000000000000e+00 -8.387542963027954102e-01 9.370703697204589844e-01 8.151787519454956055e-01 1.000000000000000000e+00 -8.362937569618225098e-01 9.360861182212829590e-01 8.124721050262451172e-01 1.000000000000000000e+00 -8.338331580162048340e-01 9.351018667221069336e-01 8.097654581069946289e-01 1.000000000000000000e+00 -8.313725590705871582e-01 9.341176748275756836e-01 8.070588111877441406e-01 1.000000000000000000e+00 -8.289119601249694824e-01 9.331334233283996582e-01 8.043521642684936523e-01 1.000000000000000000e+00 -8.264513611793518066e-01 9.321491718292236328e-01 8.016455173492431641e-01 1.000000000000000000e+00 -8.239907622337341309e-01 9.311649203300476074e-01 7.989388704299926758e-01 1.000000000000000000e+00 -8.215301632881164551e-01 9.301807284355163574e-01 7.962322235107421875e-01 1.000000000000000000e+00 -8.190695643424987793e-01 9.291964769363403320e-01 7.935255765914916992e-01 1.000000000000000000e+00 -8.166090250015258789e-01 9.282122254371643066e-01 7.908189296722412109e-01 1.000000000000000000e+00 -8.141484260559082031e-01 9.272279739379882812e-01 7.881122827529907227e-01 1.000000000000000000e+00 -8.116878271102905273e-01 9.262437820434570312e-01 7.854056358337402344e-01 1.000000000000000000e+00 -8.092272281646728516e-01 9.252595305442810059e-01 7.826989889144897461e-01 1.000000000000000000e+00 -8.067666292190551758e-01 9.242752790451049805e-01 7.799922823905944824e-01 1.000000000000000000e+00 -8.043060302734375000e-01 9.232910275459289551e-01 7.772856354713439941e-01 1.000000000000000000e+00 -8.018454313278198242e-01 9.223067760467529297e-01 7.745789885520935059e-01 1.000000000000000000e+00 -7.988927364349365234e-01 9.211380481719970703e-01 7.720568776130676270e-01 1.000000000000000000e+00 -7.944636940956115723e-01 9.194155931472778320e-01 7.700884342193603516e-01 1.000000000000000000e+00 -7.900345921516418457e-01 9.176931977272033691e-01 7.681199312210083008e-01 1.000000000000000000e+00 -7.856055498123168945e-01 9.159708023071289062e-01 7.661514878273010254e-01 1.000000000000000000e+00 -7.811764478683471680e-01 9.142483472824096680e-01 7.641829848289489746e-01 1.000000000000000000e+00 -7.767474055290222168e-01 9.125259518623352051e-01 7.622145414352416992e-01 1.000000000000000000e+00 -7.723183631896972656e-01 9.108035564422607422e-01 7.602460384368896484e-01 1.000000000000000000e+00 -7.678892612457275391e-01 9.090811014175415039e-01 7.582775950431823730e-01 1.000000000000000000e+00 -7.634602189064025879e-01 9.073587059974670410e-01 7.563090920448303223e-01 1.000000000000000000e+00 -7.590311169624328613e-01 9.056363105773925781e-01 7.543406486511230469e-01 1.000000000000000000e+00 -7.546020746231079102e-01 9.039138555526733398e-01 7.523721456527709961e-01 1.000000000000000000e+00 -7.501730322837829590e-01 9.021914601325988770e-01 7.504037022590637207e-01 1.000000000000000000e+00 -7.457439303398132324e-01 9.004690647125244141e-01 7.484351992607116699e-01 1.000000000000000000e+00 -7.413148880004882812e-01 8.987466096878051758e-01 7.464667558670043945e-01 1.000000000000000000e+00 -7.368857860565185547e-01 8.970242142677307129e-01 7.444982528686523438e-01 1.000000000000000000e+00 -7.324567437171936035e-01 8.953018188476562500e-01 7.425298094749450684e-01 1.000000000000000000e+00 -7.280277013778686523e-01 8.935793638229370117e-01 7.405613064765930176e-01 1.000000000000000000e+00 -7.235985994338989258e-01 8.918569684028625488e-01 7.385928630828857422e-01 1.000000000000000000e+00 -7.191695570945739746e-01 8.901345729827880859e-01 7.366243600845336914e-01 1.000000000000000000e+00 -7.147404551506042480e-01 8.884121775627136230e-01 7.346559166908264160e-01 1.000000000000000000e+00 -7.103114128112792969e-01 8.866897225379943848e-01 7.326874136924743652e-01 1.000000000000000000e+00 -7.058823704719543457e-01 8.849673271179199219e-01 7.307189702987670898e-01 1.000000000000000000e+00 -7.014532685279846191e-01 8.832449316978454590e-01 7.287504673004150391e-01 1.000000000000000000e+00 -6.970242261886596680e-01 8.815224766731262207e-01 7.267820239067077637e-01 1.000000000000000000e+00 -6.925951838493347168e-01 8.798000812530517578e-01 7.248135209083557129e-01 1.000000000000000000e+00 -6.881660819053649902e-01 8.780776858329772949e-01 7.228450775146484375e-01 1.000000000000000000e+00 -6.837370395660400391e-01 8.763552308082580566e-01 7.208765745162963867e-01 1.000000000000000000e+00 -6.793079376220703125e-01 8.746328353881835938e-01 7.189081311225891113e-01 1.000000000000000000e+00 -6.748788952827453613e-01 8.729104399681091309e-01 7.169396281242370605e-01 1.000000000000000000e+00 -6.704498529434204102e-01 8.711879849433898926e-01 7.149711847305297852e-01 1.000000000000000000e+00 -6.660207509994506836e-01 8.694655895233154297e-01 7.130026817321777344e-01 1.000000000000000000e+00 -6.615917086601257324e-01 8.677431941032409668e-01 7.110342383384704590e-01 1.000000000000000000e+00 -6.567474007606506348e-01 8.658823370933532715e-01 7.104959487915039062e-01 1.000000000000000000e+00 -6.512110829353332520e-01 8.637908697128295898e-01 7.123414278030395508e-01 1.000000000000000000e+00 -6.456747651100158691e-01 8.616993427276611328e-01 7.141868472099304199e-01 1.000000000000000000e+00 -6.401383876800537109e-01 8.596078157424926758e-01 7.160322666168212891e-01 1.000000000000000000e+00 -6.346020698547363281e-01 8.575163483619689941e-01 7.178777456283569336e-01 1.000000000000000000e+00 -6.290657520294189453e-01 8.554248213768005371e-01 7.197231650352478027e-01 1.000000000000000000e+00 -6.235294342041015625e-01 8.533333539962768555e-01 7.215686440467834473e-01 1.000000000000000000e+00 -6.179930567741394043e-01 8.512418270111083984e-01 7.234140634536743164e-01 1.000000000000000000e+00 -6.124567389488220215e-01 8.491503000259399414e-01 7.252595424652099609e-01 1.000000000000000000e+00 -6.069204211235046387e-01 8.470588326454162598e-01 7.271049618721008301e-01 1.000000000000000000e+00 -6.013841032981872559e-01 8.449673056602478027e-01 7.289503812789916992e-01 1.000000000000000000e+00 -5.958477258682250977e-01 8.428758382797241211e-01 7.307958602905273438e-01 1.000000000000000000e+00 -5.903114080429077148e-01 8.407843112945556641e-01 7.326412796974182129e-01 1.000000000000000000e+00 -5.847750902175903320e-01 8.386927843093872070e-01 7.344867587089538574e-01 1.000000000000000000e+00 -5.792387723922729492e-01 8.366013169288635254e-01 7.363321781158447266e-01 1.000000000000000000e+00 -5.737023949623107910e-01 8.345097899436950684e-01 7.381775975227355957e-01 1.000000000000000000e+00 -5.681660771369934082e-01 8.324183225631713867e-01 7.400230765342712402e-01 1.000000000000000000e+00 -5.626297593116760254e-01 8.303267955780029297e-01 7.418684959411621094e-01 1.000000000000000000e+00 -5.570934414863586426e-01 8.282352685928344727e-01 7.437139749526977539e-01 1.000000000000000000e+00 -5.515570640563964844e-01 8.261438012123107910e-01 7.455593943595886230e-01 1.000000000000000000e+00 -5.460207462310791016e-01 8.240522742271423340e-01 7.474048733711242676e-01 1.000000000000000000e+00 -5.404844284057617188e-01 8.219608068466186523e-01 7.492502927780151367e-01 1.000000000000000000e+00 -5.349481105804443359e-01 8.198692798614501953e-01 7.510957121849060059e-01 1.000000000000000000e+00 -5.294117927551269531e-01 8.177777528762817383e-01 7.529411911964416504e-01 1.000000000000000000e+00 -5.238754153251647949e-01 8.156862854957580566e-01 7.547866106033325195e-01 1.000000000000000000e+00 -5.183390974998474121e-01 8.135947585105895996e-01 7.566320896148681641e-01 1.000000000000000000e+00 -5.128027796745300293e-01 8.115032911300659180e-01 7.584775090217590332e-01 1.000000000000000000e+00 -5.072664618492126465e-01 8.094117641448974609e-01 7.603229284286499023e-01 1.000000000000000000e+00 -5.017300844192504883e-01 8.073202371597290039e-01 7.621684074401855469e-01 1.000000000000000000e+00 -4.961937665939331055e-01 8.052287697792053223e-01 7.640138268470764160e-01 1.000000000000000000e+00 -4.906574487686157227e-01 8.031372427940368652e-01 7.658593058586120605e-01 1.000000000000000000e+00 -4.851211011409759521e-01 8.010457754135131836e-01 7.677047252655029297e-01 1.000000000000000000e+00 -4.795847833156585693e-01 7.984621524810791016e-01 7.695501446723937988e-01 1.000000000000000000e+00 -4.740484356880187988e-01 7.953863739967346191e-01 7.713956236839294434e-01 1.000000000000000000e+00 -4.685121178627014160e-01 7.923106551170349121e-01 7.732410430908203125e-01 1.000000000000000000e+00 -4.629757702350616455e-01 7.892349362373352051e-01 7.750865221023559570e-01 1.000000000000000000e+00 -4.574394524097442627e-01 7.861591577529907227e-01 7.769319415092468262e-01 1.000000000000000000e+00 -4.519031047821044922e-01 7.830834388732910156e-01 7.787774205207824707e-01 1.000000000000000000e+00 -4.463667869567871094e-01 7.800076603889465332e-01 7.806228399276733398e-01 1.000000000000000000e+00 -4.408304393291473389e-01 7.769319415092468262e-01 7.824682593345642090e-01 1.000000000000000000e+00 -4.352941215038299561e-01 7.738562226295471191e-01 7.843137383460998535e-01 1.000000000000000000e+00 -4.297577738761901855e-01 7.707804441452026367e-01 7.861591577529907227e-01 1.000000000000000000e+00 -4.242214560508728027e-01 7.677047252655029297e-01 7.880046367645263672e-01 1.000000000000000000e+00 -4.186851084232330322e-01 7.646290063858032227e-01 7.898500561714172363e-01 1.000000000000000000e+00 -4.131487905979156494e-01 7.615532279014587402e-01 7.916954755783081055e-01 1.000000000000000000e+00 -4.076124429702758789e-01 7.584775090217590332e-01 7.935409545898437500e-01 1.000000000000000000e+00 -4.020761251449584961e-01 7.554017901420593262e-01 7.953863739967346191e-01 1.000000000000000000e+00 -3.965397775173187256e-01 7.523260116577148438e-01 7.972318530082702637e-01 1.000000000000000000e+00 -3.910034596920013428e-01 7.492502927780151367e-01 7.990772724151611328e-01 1.000000000000000000e+00 -3.854671418666839600e-01 7.461745738983154297e-01 8.009227514266967773e-01 1.000000000000000000e+00 -3.799307942390441895e-01 7.430987954139709473e-01 8.027681708335876465e-01 1.000000000000000000e+00 -3.743944764137268066e-01 7.400230765342712402e-01 8.046135902404785156e-01 1.000000000000000000e+00 -3.688581287860870361e-01 7.369473576545715332e-01 8.064590692520141602e-01 1.000000000000000000e+00 -3.633218109607696533e-01 7.338715791702270508e-01 8.083044886589050293e-01 1.000000000000000000e+00 -3.577854633331298828e-01 7.307958602905273438e-01 8.101499676704406738e-01 1.000000000000000000e+00 -3.522491455078125000e-01 7.277200818061828613e-01 8.119953870773315430e-01 1.000000000000000000e+00 -3.467127978801727295e-01 7.246443629264831543e-01 8.138408064842224121e-01 1.000000000000000000e+00 -3.411764800548553467e-01 7.215686440467834473e-01 8.156862854957580566e-01 1.000000000000000000e+00 -3.356401324272155762e-01 7.184928655624389648e-01 8.175317049026489258e-01 1.000000000000000000e+00 -3.301038146018981934e-01 7.154171466827392578e-01 8.193771839141845703e-01 1.000000000000000000e+00 -3.245674669742584229e-01 7.123414278030395508e-01 8.212226033210754395e-01 1.000000000000000000e+00 -3.190311491489410400e-01 7.092656493186950684e-01 8.230680227279663086e-01 1.000000000000000000e+00 -3.134948015213012695e-01 7.061899304389953613e-01 8.249135017395019531e-01 1.000000000000000000e+00 -3.079584836959838867e-01 7.031142115592956543e-01 8.267589211463928223e-01 1.000000000000000000e+00 -3.031910657882690430e-01 6.989619135856628418e-01 8.258362412452697754e-01 1.000000000000000000e+00 -2.988850474357604980e-01 6.941637992858886719e-01 8.232526183128356934e-01 1.000000000000000000e+00 -2.945789992809295654e-01 6.893656253814697266e-01 8.206689953804016113e-01 1.000000000000000000e+00 -2.902729809284210205e-01 6.845674514770507812e-01 8.180853724479675293e-01 1.000000000000000000e+00 -2.859669327735900879e-01 6.797693371772766113e-01 8.155017495155334473e-01 1.000000000000000000e+00 -2.816609144210815430e-01 6.749711632728576660e-01 8.129181265830993652e-01 1.000000000000000000e+00 -2.773548662662506104e-01 6.701729893684387207e-01 8.103345036506652832e-01 1.000000000000000000e+00 -2.730488181114196777e-01 6.653748750686645508e-01 8.077508807182312012e-01 1.000000000000000000e+00 -2.687427997589111328e-01 6.605767011642456055e-01 8.051672577857971191e-01 1.000000000000000000e+00 -2.644367516040802002e-01 6.557785272598266602e-01 8.025836348533630371e-01 1.000000000000000000e+00 -2.601307332515716553e-01 6.509804129600524902e-01 8.000000119209289551e-01 1.000000000000000000e+00 -2.558246850967407227e-01 6.461822390556335449e-01 7.974163889884948730e-01 1.000000000000000000e+00 -2.515186369419097900e-01 6.413840651512145996e-01 7.948327660560607910e-01 1.000000000000000000e+00 -2.472126036882400513e-01 6.365859508514404297e-01 7.922491431236267090e-01 1.000000000000000000e+00 -2.429065704345703125e-01 6.317877769470214844e-01 7.896655201911926270e-01 1.000000000000000000e+00 -2.386005371809005737e-01 6.269896030426025391e-01 7.870818972587585449e-01 1.000000000000000000e+00 -2.342945039272308350e-01 6.221914887428283691e-01 7.844982743263244629e-01 1.000000000000000000e+00 -2.299884706735610962e-01 6.173933148384094238e-01 7.819146513938903809e-01 1.000000000000000000e+00 -2.256824225187301636e-01 6.125951409339904785e-01 7.793310284614562988e-01 1.000000000000000000e+00 -2.213763892650604248e-01 6.077970266342163086e-01 7.767474055290222168e-01 1.000000000000000000e+00 -2.170703560113906860e-01 6.029988527297973633e-01 7.741637825965881348e-01 1.000000000000000000e+00 -2.127643227577209473e-01 5.982006788253784180e-01 7.715801596641540527e-01 1.000000000000000000e+00 -2.084582895040512085e-01 5.934025645256042480e-01 7.689965367317199707e-01 1.000000000000000000e+00 -2.041522562503814697e-01 5.886043906211853027e-01 7.664129137992858887e-01 1.000000000000000000e+00 -1.998462080955505371e-01 5.838062167167663574e-01 7.638292908668518066e-01 1.000000000000000000e+00 -1.955401748418807983e-01 5.790081024169921875e-01 7.612456679344177246e-01 1.000000000000000000e+00 -1.912341415882110596e-01 5.742099285125732422e-01 7.586620450019836426e-01 1.000000000000000000e+00 -1.869281083345413208e-01 5.694117546081542969e-01 7.560784220695495605e-01 1.000000000000000000e+00 -1.826220750808715820e-01 5.646135807037353516e-01 7.534947991371154785e-01 1.000000000000000000e+00 -1.783160269260406494e-01 5.598154664039611816e-01 7.509111762046813965e-01 1.000000000000000000e+00 -1.740099936723709106e-01 5.550172924995422363e-01 7.483275532722473145e-01 1.000000000000000000e+00 -1.697039604187011719e-01 5.502191185951232910e-01 7.457439303398132324e-01 1.000000000000000000e+00 -1.653979271650314331e-01 5.456978082656860352e-01 7.434371113777160645e-01 1.000000000000000000e+00 -1.610918939113616943e-01 5.412687659263610840e-01 7.412226200103759766e-01 1.000000000000000000e+00 -1.567858457565307617e-01 5.368396639823913574e-01 7.390080690383911133e-01 1.000000000000000000e+00 -1.524798125028610229e-01 5.324106216430664062e-01 7.367935180664062500e-01 1.000000000000000000e+00 -1.481737792491912842e-01 5.279815196990966797e-01 7.345790266990661621e-01 1.000000000000000000e+00 -1.438677459955215454e-01 5.235524773597717285e-01 7.323644757270812988e-01 1.000000000000000000e+00 -1.395617127418518066e-01 5.191234350204467773e-01 7.301499247550964355e-01 1.000000000000000000e+00 -1.352556645870208740e-01 5.146943330764770508e-01 7.279354333877563477e-01 1.000000000000000000e+00 -1.309496313333511353e-01 5.102652907371520996e-01 7.257208824157714844e-01 1.000000000000000000e+00 -1.266435980796813965e-01 5.058361887931823730e-01 7.235063314437866211e-01 1.000000000000000000e+00 -1.223375648260116577e-01 5.014071464538574219e-01 7.212918400764465332e-01 1.000000000000000000e+00 -1.180315241217613220e-01 4.969780743122100830e-01 7.190772891044616699e-01 1.000000000000000000e+00 -1.137254908680915833e-01 4.925490319728851318e-01 7.168627381324768066e-01 1.000000000000000000e+00 -1.094194576144218445e-01 4.881199598312377930e-01 7.146481871604919434e-01 1.000000000000000000e+00 -1.051134169101715088e-01 4.836908876895904541e-01 7.124336957931518555e-01 1.000000000000000000e+00 -1.008073836565017700e-01 4.792618155479431152e-01 7.102191448211669922e-01 1.000000000000000000e+00 -9.650134295225143433e-02 4.748327434062957764e-01 7.080045938491821289e-01 1.000000000000000000e+00 -9.219530969858169556e-02 4.704037010669708252e-01 7.057901024818420410e-01 1.000000000000000000e+00 -8.788927644491195679e-02 4.659746289253234863e-01 7.035755515098571777e-01 1.000000000000000000e+00 -8.358323574066162109e-02 4.615455567836761475e-01 7.013610005378723145e-01 1.000000000000000000e+00 -7.927720248699188232e-02 4.571164846420288086e-01 6.991465091705322266e-01 1.000000000000000000e+00 -7.497116178274154663e-02 4.526874423027038574e-01 6.969319581985473633e-01 1.000000000000000000e+00 -7.066512852907180786e-02 4.482583701610565186e-01 6.947174072265625000e-01 1.000000000000000000e+00 -6.635909527540206909e-02 4.438292980194091797e-01 6.925028562545776367e-01 1.000000000000000000e+00 -6.205305829644203186e-02 4.394002258777618408e-01 6.902883648872375488e-01 1.000000000000000000e+00 -5.774702131748199463e-02 4.349711537361145020e-01 6.880738139152526855e-01 1.000000000000000000e+00 -5.344098433852195740e-02 4.305421113967895508e-01 6.858592629432678223e-01 1.000000000000000000e+00 -4.913494735956192017e-02 4.261130392551422119e-01 6.836447715759277344e-01 1.000000000000000000e+00 -4.482891038060188293e-02 4.216839671134948730e-01 6.814302206039428711e-01 1.000000000000000000e+00 -4.052287712693214417e-02 4.172548949718475342e-01 6.792156696319580078e-01 1.000000000000000000e+00 -3.621684014797210693e-02 4.128258228302001953e-01 6.770011782646179199e-01 1.000000000000000000e+00 -3.191080316901206970e-02 4.083967804908752441e-01 6.747866272926330566e-01 1.000000000000000000e+00 -3.137255087494850159e-02 4.035370945930480957e-01 6.698808073997497559e-01 1.000000000000000000e+00 -3.137255087494850159e-02 3.986159265041351318e-01 6.645905375480651855e-01 1.000000000000000000e+00 -3.137255087494850159e-02 3.936947286128997803e-01 6.593002676963806152e-01 1.000000000000000000e+00 -3.137255087494850159e-02 3.887735605239868164e-01 6.540099978446960449e-01 1.000000000000000000e+00 -3.137255087494850159e-02 3.838523626327514648e-01 6.487197279930114746e-01 1.000000000000000000e+00 -3.137255087494850159e-02 3.789311945438385010e-01 6.434294581413269043e-01 1.000000000000000000e+00 -3.137255087494850159e-02 3.740099966526031494e-01 6.381391882896423340e-01 1.000000000000000000e+00 -3.137255087494850159e-02 3.690887987613677979e-01 6.328489184379577637e-01 1.000000000000000000e+00 -3.137255087494850159e-02 3.641676306724548340e-01 6.275586485862731934e-01 1.000000000000000000e+00 -3.137255087494850159e-02 3.592464327812194824e-01 6.222683787345886230e-01 1.000000000000000000e+00 -3.137255087494850159e-02 3.543252646923065186e-01 6.169781088829040527e-01 1.000000000000000000e+00 -3.137255087494850159e-02 3.494040668010711670e-01 6.116878390312194824e-01 1.000000000000000000e+00 -3.137255087494850159e-02 3.444828987121582031e-01 6.063975691795349121e-01 1.000000000000000000e+00 -3.137255087494850159e-02 3.395617008209228516e-01 6.011072397232055664e-01 1.000000000000000000e+00 -3.137255087494850159e-02 3.346405327320098877e-01 5.958169698715209961e-01 1.000000000000000000e+00 -3.137255087494850159e-02 3.297193348407745361e-01 5.905267000198364258e-01 1.000000000000000000e+00 -3.137255087494850159e-02 3.247981667518615723e-01 5.852364301681518555e-01 1.000000000000000000e+00 -3.137255087494850159e-02 3.198769688606262207e-01 5.799461603164672852e-01 1.000000000000000000e+00 -3.137255087494850159e-02 3.149558007717132568e-01 5.746558904647827148e-01 1.000000000000000000e+00 -3.137255087494850159e-02 3.100346028804779053e-01 5.693656206130981445e-01 1.000000000000000000e+00 -3.137255087494850159e-02 3.051134049892425537e-01 5.640753507614135742e-01 1.000000000000000000e+00 -3.137255087494850159e-02 3.001922369003295898e-01 5.587850809097290039e-01 1.000000000000000000e+00 -3.137255087494850159e-02 2.952710390090942383e-01 5.534948110580444336e-01 1.000000000000000000e+00 -3.137255087494850159e-02 2.903498709201812744e-01 5.482045412063598633e-01 1.000000000000000000e+00 -3.137255087494850159e-02 2.854286730289459229e-01 5.429142713546752930e-01 1.000000000000000000e+00 -3.137255087494850159e-02 2.805075049400329590e-01 5.376240015029907227e-01 1.000000000000000000e+00 -3.137255087494850159e-02 2.755863070487976074e-01 5.323337316513061523e-01 1.000000000000000000e+00 -3.137255087494850159e-02 2.706651389598846436e-01 5.270434617996215820e-01 1.000000000000000000e+00 -3.137255087494850159e-02 2.657439410686492920e-01 5.217531919479370117e-01 1.000000000000000000e+00 -3.137255087494850159e-02 2.608227729797363281e-01 5.164629220962524414e-01 1.000000000000000000e+00 -3.137255087494850159e-02 2.559015750885009766e-01 5.111726522445678711e-01 1.000000000000000000e+00 -3.137255087494850159e-02 2.509804069995880127e-01 5.058823823928833008e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/Greens b/fastplotlib/utils/colormaps/Greens deleted file mode 100644 index 7e1a8733a..000000000 --- a/fastplotlib/utils/colormaps/Greens +++ /dev/null @@ -1,256 +0,0 @@ -9.686274528503417969e-01 9.882352948188781738e-01 9.607843160629272461e-01 1.000000000000000000e+00 -9.664129018783569336e-01 9.873740673065185547e-01 9.582006931304931641e-01 1.000000000000000000e+00 -9.641984105110168457e-01 9.865128993988037109e-01 9.556170701980590820e-01 1.000000000000000000e+00 -9.619838595390319824e-01 9.856516718864440918e-01 9.530334472656250000e-01 1.000000000000000000e+00 -9.597693085670471191e-01 9.847904443740844727e-01 9.504498243331909180e-01 1.000000000000000000e+00 -9.575547575950622559e-01 9.839292764663696289e-01 9.478662014007568359e-01 1.000000000000000000e+00 -9.553402662277221680e-01 9.830680489540100098e-01 9.452825784683227539e-01 1.000000000000000000e+00 -9.531257152557373047e-01 9.822068214416503906e-01 9.426989555358886719e-01 1.000000000000000000e+00 -9.509111642837524414e-01 9.813456535339355469e-01 9.401153326034545898e-01 1.000000000000000000e+00 -9.486966729164123535e-01 9.804844260215759277e-01 9.375317096710205078e-01 1.000000000000000000e+00 -9.464821219444274902e-01 9.796231985092163086e-01 9.349480867385864258e-01 1.000000000000000000e+00 -9.442675709724426270e-01 9.787620306015014648e-01 9.323644638061523438e-01 1.000000000000000000e+00 -9.420530796051025391e-01 9.779008030891418457e-01 9.297808408737182617e-01 1.000000000000000000e+00 -9.398385286331176758e-01 9.770395755767822266e-01 9.271972179412841797e-01 1.000000000000000000e+00 -9.376239776611328125e-01 9.761784076690673828e-01 9.246135950088500977e-01 1.000000000000000000e+00 -9.354094862937927246e-01 9.753171801567077637e-01 9.220299720764160156e-01 1.000000000000000000e+00 -9.331949353218078613e-01 9.744559526443481445e-01 9.194463491439819336e-01 1.000000000000000000e+00 -9.309803843498229980e-01 9.735947847366333008e-01 9.168627262115478516e-01 1.000000000000000000e+00 -9.287658333778381348e-01 9.727335572242736816e-01 9.142791032791137695e-01 1.000000000000000000e+00 -9.265513420104980469e-01 9.718723297119140625e-01 9.116954803466796875e-01 1.000000000000000000e+00 -9.243367910385131836e-01 9.710111618041992188e-01 9.091118574142456055e-01 1.000000000000000000e+00 -9.221222400665283203e-01 9.701499342918395996e-01 9.065282344818115234e-01 1.000000000000000000e+00 -9.199077486991882324e-01 9.692887067794799805e-01 9.039446115493774414e-01 1.000000000000000000e+00 -9.176931977272033691e-01 9.684275388717651367e-01 9.013609886169433594e-01 1.000000000000000000e+00 -9.154786467552185059e-01 9.675663113594055176e-01 8.987773656845092773e-01 1.000000000000000000e+00 -9.132641553878784180e-01 9.667050838470458984e-01 8.961937427520751953e-01 1.000000000000000000e+00 -9.110496044158935547e-01 9.658439159393310547e-01 8.936101794242858887e-01 1.000000000000000000e+00 -9.088350534439086914e-01 9.649826884269714355e-01 8.910265564918518066e-01 1.000000000000000000e+00 -9.066205024719238281e-01 9.641215205192565918e-01 8.884429335594177246e-01 1.000000000000000000e+00 -9.044060111045837402e-01 9.632602930068969727e-01 8.858593106269836426e-01 1.000000000000000000e+00 -9.021914601325988770e-01 9.623990654945373535e-01 8.832756876945495605e-01 1.000000000000000000e+00 -8.999769091606140137e-01 9.615378975868225098e-01 8.806920647621154785e-01 1.000000000000000000e+00 -8.975778818130493164e-01 9.605997800827026367e-01 8.779392838478088379e-01 1.000000000000000000e+00 -8.938869833946228027e-01 9.591234326362609863e-01 8.740022778511047363e-01 1.000000000000000000e+00 -8.901960849761962891e-01 9.576470851898193359e-01 8.700653314590454102e-01 1.000000000000000000e+00 -8.865051865577697754e-01 9.561706781387329102e-01 8.661283850669860840e-01 1.000000000000000000e+00 -8.828142881393432617e-01 9.546943306922912598e-01 8.621914386749267578e-01 1.000000000000000000e+00 -8.791233897209167480e-01 9.532179832458496094e-01 8.582544922828674316e-01 1.000000000000000000e+00 -8.754325509071350098e-01 9.517416357994079590e-01 8.543175458908081055e-01 1.000000000000000000e+00 -8.717416524887084961e-01 9.502652883529663086e-01 8.503805994987487793e-01 1.000000000000000000e+00 -8.680507540702819824e-01 9.487889409065246582e-01 8.464436531066894531e-01 1.000000000000000000e+00 -8.643598556518554688e-01 9.473125934600830078e-01 8.425067067146301270e-01 1.000000000000000000e+00 -8.606689572334289551e-01 9.458362460136413574e-01 8.385697603225708008e-01 1.000000000000000000e+00 -8.569780588150024414e-01 9.443598389625549316e-01 8.346328139305114746e-01 1.000000000000000000e+00 -8.532872200012207031e-01 9.428834915161132812e-01 8.306958675384521484e-01 1.000000000000000000e+00 -8.495963215827941895e-01 9.414071440696716309e-01 8.267589211463928223e-01 1.000000000000000000e+00 -8.459054231643676758e-01 9.399307966232299805e-01 8.228219747543334961e-01 1.000000000000000000e+00 -8.422145247459411621e-01 9.384544491767883301e-01 8.188850283622741699e-01 1.000000000000000000e+00 -8.385236263275146484e-01 9.369781017303466797e-01 8.149480819702148438e-01 1.000000000000000000e+00 -8.348327279090881348e-01 9.355017542839050293e-01 8.110111355781555176e-01 1.000000000000000000e+00 -8.311418890953063965e-01 9.340253472328186035e-01 8.070741891860961914e-01 1.000000000000000000e+00 -8.274509906768798828e-01 9.325489997863769531e-01 8.031372427940368652e-01 1.000000000000000000e+00 -8.237600922584533691e-01 9.310726523399353027e-01 7.992002964019775391e-01 1.000000000000000000e+00 -8.200691938400268555e-01 9.295963048934936523e-01 7.952633500099182129e-01 1.000000000000000000e+00 -8.163782954216003418e-01 9.281199574470520020e-01 7.913264036178588867e-01 1.000000000000000000e+00 -8.126874566078186035e-01 9.266436100006103516e-01 7.873894572257995605e-01 1.000000000000000000e+00 -8.089965581893920898e-01 9.251672625541687012e-01 7.834525108337402344e-01 1.000000000000000000e+00 -8.053056597709655762e-01 9.236909151077270508e-01 7.795155644416809082e-01 1.000000000000000000e+00 -8.016147613525390625e-01 9.222145080566406250e-01 7.755786180496215820e-01 1.000000000000000000e+00 -7.979238629341125488e-01 9.207381606101989746e-01 7.716416716575622559e-01 1.000000000000000000e+00 -7.942329645156860352e-01 9.192618131637573242e-01 7.677047252655029297e-01 1.000000000000000000e+00 -7.905421257019042969e-01 9.177854657173156738e-01 7.637677788734436035e-01 1.000000000000000000e+00 -7.868512272834777832e-01 9.163091182708740234e-01 7.598308324813842773e-01 1.000000000000000000e+00 -7.831603288650512695e-01 9.148327708244323730e-01 7.558938860893249512e-01 1.000000000000000000e+00 -7.792233824729919434e-01 9.132333993911743164e-01 7.518031597137451172e-01 1.000000000000000000e+00 -7.745482325553894043e-01 9.112648963928222656e-01 7.472510337829589844e-01 1.000000000000000000e+00 -7.698731422424316406e-01 9.092964529991149902e-01 7.426989674568176270e-01 1.000000000000000000e+00 -7.651979923248291016e-01 9.073279500007629395e-01 7.381468415260314941e-01 1.000000000000000000e+00 -7.605229020118713379e-01 9.053595066070556641e-01 7.335947751998901367e-01 1.000000000000000000e+00 -7.558477520942687988e-01 9.033910036087036133e-01 7.290426492691040039e-01 1.000000000000000000e+00 -7.511726021766662598e-01 9.014225006103515625e-01 7.244905829429626465e-01 1.000000000000000000e+00 -7.464975118637084961e-01 8.994540572166442871e-01 7.199384570121765137e-01 1.000000000000000000e+00 -7.418223619461059570e-01 8.974855542182922363e-01 7.153863906860351562e-01 1.000000000000000000e+00 -7.371472716331481934e-01 8.955171108245849609e-01 7.108342647552490234e-01 1.000000000000000000e+00 -7.324721217155456543e-01 8.935486078262329102e-01 7.062821984291076660e-01 1.000000000000000000e+00 -7.277969717979431152e-01 8.915801644325256348e-01 7.017301321029663086e-01 1.000000000000000000e+00 -7.231218814849853516e-01 8.896116614341735840e-01 6.971780061721801758e-01 1.000000000000000000e+00 -7.184467315673828125e-01 8.876432180404663086e-01 6.926259398460388184e-01 1.000000000000000000e+00 -7.137716412544250488e-01 8.856747150421142578e-01 6.880738139152526855e-01 1.000000000000000000e+00 -7.090964913368225098e-01 8.837062716484069824e-01 6.835217475891113281e-01 1.000000000000000000e+00 -7.044214010238647461e-01 8.817377686500549316e-01 6.789696216583251953e-01 1.000000000000000000e+00 -6.997462511062622070e-01 8.797693252563476562e-01 6.744175553321838379e-01 1.000000000000000000e+00 -6.950711011886596680e-01 8.778008222579956055e-01 6.698654294013977051e-01 1.000000000000000000e+00 -6.903960108757019043e-01 8.758323788642883301e-01 6.653133630752563477e-01 1.000000000000000000e+00 -6.857208609580993652e-01 8.738638758659362793e-01 6.607612371444702148e-01 1.000000000000000000e+00 -6.810457706451416016e-01 8.718954324722290039e-01 6.562091708183288574e-01 1.000000000000000000e+00 -6.763706207275390625e-01 8.699269294738769531e-01 6.516570448875427246e-01 1.000000000000000000e+00 -6.716955304145812988e-01 8.679584860801696777e-01 6.471049785614013672e-01 1.000000000000000000e+00 -6.670203804969787598e-01 8.659899830818176270e-01 6.425528526306152344e-01 1.000000000000000000e+00 -6.623452305793762207e-01 8.640215396881103516e-01 6.380007863044738770e-01 1.000000000000000000e+00 -6.576701402664184570e-01 8.620530366897583008e-01 6.334486603736877441e-01 1.000000000000000000e+00 -6.529949903488159180e-01 8.600845932960510254e-01 6.288965940475463867e-01 1.000000000000000000e+00 -6.483199000358581543e-01 8.581160902976989746e-01 6.243444681167602539e-01 1.000000000000000000e+00 -6.436447501182556152e-01 8.561476469039916992e-01 6.197924017906188965e-01 1.000000000000000000e+00 -6.389696002006530762e-01 8.541791439056396484e-01 6.152402758598327637e-01 1.000000000000000000e+00 -6.342945098876953125e-01 8.522107005119323730e-01 6.106882095336914062e-01 1.000000000000000000e+00 -6.292964220046997070e-01 8.500115275382995605e-01 6.061360836029052734e-01 1.000000000000000000e+00 -6.237601041793823242e-01 8.474279046058654785e-01 6.015840172767639160e-01 1.000000000000000000e+00 -6.182237863540649414e-01 8.448442816734313965e-01 5.970318913459777832e-01 1.000000000000000000e+00 -6.126874089241027832e-01 8.422606587409973145e-01 5.924798250198364258e-01 1.000000000000000000e+00 -6.071510910987854004e-01 8.396770358085632324e-01 5.879276990890502930e-01 1.000000000000000000e+00 -6.016147732734680176e-01 8.370934128761291504e-01 5.833756327629089355e-01 1.000000000000000000e+00 -5.960784554481506348e-01 8.345097899436950684e-01 5.788235068321228027e-01 1.000000000000000000e+00 -5.905420780181884766e-01 8.319261670112609863e-01 5.742714405059814453e-01 1.000000000000000000e+00 -5.850057601928710938e-01 8.293425440788269043e-01 5.697193145751953125e-01 1.000000000000000000e+00 -5.794694423675537109e-01 8.267589211463928223e-01 5.651672482490539551e-01 1.000000000000000000e+00 -5.739331245422363281e-01 8.241752982139587402e-01 5.606151223182678223e-01 1.000000000000000000e+00 -5.683967471122741699e-01 8.215916752815246582e-01 5.560630559921264648e-01 1.000000000000000000e+00 -5.628604292869567871e-01 8.190080523490905762e-01 5.515109300613403320e-01 1.000000000000000000e+00 -5.573241114616394043e-01 8.164244294166564941e-01 5.469588637351989746e-01 1.000000000000000000e+00 -5.517877936363220215e-01 8.138408064842224121e-01 5.424067378044128418e-01 1.000000000000000000e+00 -5.462514162063598633e-01 8.112571835517883301e-01 5.378546714782714844e-01 1.000000000000000000e+00 -5.407150983810424805e-01 8.086735606193542480e-01 5.333026051521301270e-01 1.000000000000000000e+00 -5.351787805557250977e-01 8.060899376869201660e-01 5.287504792213439941e-01 1.000000000000000000e+00 -5.296424627304077148e-01 8.035063147544860840e-01 5.241984128952026367e-01 1.000000000000000000e+00 -5.241060853004455566e-01 8.009227514266967773e-01 5.196462869644165039e-01 1.000000000000000000e+00 -5.185697674751281738e-01 7.983391284942626953e-01 5.150942206382751465e-01 1.000000000000000000e+00 -5.130334496498107910e-01 7.957555055618286133e-01 5.105420947074890137e-01 1.000000000000000000e+00 -5.074971318244934082e-01 7.931718826293945312e-01 5.059900283813476562e-01 1.000000000000000000e+00 -5.019608139991760254e-01 7.905882596969604492e-01 5.014379024505615234e-01 1.000000000000000000e+00 -4.964244663715362549e-01 7.880046367645263672e-01 4.968858063220977783e-01 1.000000000000000000e+00 -4.908881187438964844e-01 7.854210138320922852e-01 4.923337101936340332e-01 1.000000000000000000e+00 -4.853518009185791016e-01 7.828373908996582031e-01 4.877816140651702881e-01 1.000000000000000000e+00 -4.798154532909393311e-01 7.802537679672241211e-01 4.832295179367065430e-01 1.000000000000000000e+00 -4.742791354656219482e-01 7.776701450347900391e-01 4.786774218082427979e-01 1.000000000000000000e+00 -4.687427878379821777e-01 7.750865221023559570e-01 4.741253256797790527e-01 1.000000000000000000e+00 -4.632064700126647949e-01 7.725028991699218750e-01 4.695732295513153076e-01 1.000000000000000000e+00 -4.576701223850250244e-01 7.699192762374877930e-01 4.650211334228515625e-01 1.000000000000000000e+00 -4.517647027969360352e-01 7.670896053314208984e-01 4.612072408199310303e-01 1.000000000000000000e+00 -4.454901814460754395e-01 7.640138268470764160e-01 4.581314921379089355e-01 1.000000000000000000e+00 -4.392156898975372314e-01 7.609381079673767090e-01 4.550557434558868408e-01 1.000000000000000000e+00 -4.329411685466766357e-01 7.578623890876770020e-01 4.519799947738647461e-01 1.000000000000000000e+00 -4.266666769981384277e-01 7.547866106033325195e-01 4.489042758941650391e-01 1.000000000000000000e+00 -4.203921556472778320e-01 7.517108917236328125e-01 4.458285272121429443e-01 1.000000000000000000e+00 -4.141176342964172363e-01 7.486351132392883301e-01 4.427527785301208496e-01 1.000000000000000000e+00 -4.078431427478790283e-01 7.455593943595886230e-01 4.396770596504211426e-01 1.000000000000000000e+00 -4.015686213970184326e-01 7.424836754798889160e-01 4.366013109683990479e-01 1.000000000000000000e+00 -3.952941298484802246e-01 7.394078969955444336e-01 4.335255622863769531e-01 1.000000000000000000e+00 -3.890196084976196289e-01 7.363321781158447266e-01 4.304498136043548584e-01 1.000000000000000000e+00 -3.827450871467590332e-01 7.332564592361450195e-01 4.273740947246551514e-01 1.000000000000000000e+00 -3.764705955982208252e-01 7.301806807518005371e-01 4.242983460426330566e-01 1.000000000000000000e+00 -3.701960742473602295e-01 7.271049618721008301e-01 4.212225973606109619e-01 1.000000000000000000e+00 -3.639215826988220215e-01 7.240292429924011230e-01 4.181468784809112549e-01 1.000000000000000000e+00 -3.576470613479614258e-01 7.209534645080566406e-01 4.150711297988891602e-01 1.000000000000000000e+00 -3.513725399971008301e-01 7.178777456283569336e-01 4.119953811168670654e-01 1.000000000000000000e+00 -3.450980484485626221e-01 7.148020267486572266e-01 4.089196324348449707e-01 1.000000000000000000e+00 -3.388235270977020264e-01 7.117262482643127441e-01 4.058439135551452637e-01 1.000000000000000000e+00 -3.325490057468414307e-01 7.086505293846130371e-01 4.027681648731231689e-01 1.000000000000000000e+00 -3.262745141983032227e-01 7.055747509002685547e-01 3.996924161911010742e-01 1.000000000000000000e+00 -3.199999928474426270e-01 7.024990320205688477e-01 3.966166973114013672e-01 1.000000000000000000e+00 -3.137255012989044189e-01 6.994233131408691406e-01 3.935409486293792725e-01 1.000000000000000000e+00 -3.074509799480438232e-01 6.963475346565246582e-01 3.904651999473571777e-01 1.000000000000000000e+00 -3.011764585971832275e-01 6.932718157768249512e-01 3.873894512653350830e-01 1.000000000000000000e+00 -2.949019670486450195e-01 6.901960968971252441e-01 3.843137323856353760e-01 1.000000000000000000e+00 -2.886274456977844238e-01 6.871203184127807617e-01 3.812379837036132812e-01 1.000000000000000000e+00 -2.823529541492462158e-01 6.840445995330810547e-01 3.781622350215911865e-01 1.000000000000000000e+00 -2.760784327983856201e-01 6.809688806533813477e-01 3.750865161418914795e-01 1.000000000000000000e+00 -2.698039114475250244e-01 6.778931021690368652e-01 3.720107674598693848e-01 1.000000000000000000e+00 -2.635294198989868164e-01 6.748173832893371582e-01 3.689350187778472900e-01 1.000000000000000000e+00 -2.572548985481262207e-01 6.717416644096374512e-01 3.658592700958251953e-01 1.000000000000000000e+00 -2.525951564311981201e-01 6.681276559829711914e-01 3.628604412078857422e-01 1.000000000000000000e+00 -2.489042729139328003e-01 6.641907095909118652e-01 3.599077165126800537e-01 1.000000000000000000e+00 -2.452133744955062866e-01 6.602537631988525391e-01 3.569550216197967529e-01 1.000000000000000000e+00 -2.415224909782409668e-01 6.563168168067932129e-01 3.540022969245910645e-01 1.000000000000000000e+00 -2.378316074609756470e-01 6.523798704147338867e-01 3.510496020317077637e-01 1.000000000000000000e+00 -2.341407090425491333e-01 6.484429240226745605e-01 3.480968773365020752e-01 1.000000000000000000e+00 -2.304498255252838135e-01 6.445059776306152344e-01 3.451441824436187744e-01 1.000000000000000000e+00 -2.267589420080184937e-01 6.405690312385559082e-01 3.421914577484130859e-01 1.000000000000000000e+00 -2.230680435895919800e-01 6.366320848464965820e-01 3.392387628555297852e-01 1.000000000000000000e+00 -2.193771600723266602e-01 6.326951384544372559e-01 3.362860381603240967e-01 1.000000000000000000e+00 -2.156862765550613403e-01 6.287581920623779297e-01 3.333333432674407959e-01 1.000000000000000000e+00 -2.119953930377960205e-01 6.248212456703186035e-01 3.303806185722351074e-01 1.000000000000000000e+00 -2.083044946193695068e-01 6.208842992782592773e-01 3.274279236793518066e-01 1.000000000000000000e+00 -2.046136111021041870e-01 6.169473528861999512e-01 3.244751989841461182e-01 1.000000000000000000e+00 -2.009227275848388672e-01 6.130104064941406250e-01 3.215225040912628174e-01 1.000000000000000000e+00 -1.972318291664123535e-01 6.090734601020812988e-01 3.185697793960571289e-01 1.000000000000000000e+00 -1.935409456491470337e-01 6.051365137100219727e-01 3.156170845031738281e-01 1.000000000000000000e+00 -1.898500621318817139e-01 6.011995673179626465e-01 3.126643598079681396e-01 1.000000000000000000e+00 -1.861591637134552002e-01 5.972626209259033203e-01 3.097116351127624512e-01 1.000000000000000000e+00 -1.824682801961898804e-01 5.933256149291992188e-01 3.067589402198791504e-01 1.000000000000000000e+00 -1.787773966789245605e-01 5.893886685371398926e-01 3.038062155246734619e-01 1.000000000000000000e+00 -1.750864982604980469e-01 5.854517221450805664e-01 3.008535206317901611e-01 1.000000000000000000e+00 -1.713956147432327271e-01 5.815147757530212402e-01 2.979007959365844727e-01 1.000000000000000000e+00 -1.677047312259674072e-01 5.775778293609619141e-01 2.949481010437011719e-01 1.000000000000000000e+00 -1.640138477087020874e-01 5.736408829689025879e-01 2.919953763484954834e-01 1.000000000000000000e+00 -1.603229492902755737e-01 5.697039365768432617e-01 2.890426814556121826e-01 1.000000000000000000e+00 -1.566320657730102539e-01 5.657669901847839355e-01 2.860899567604064941e-01 1.000000000000000000e+00 -1.529411822557449341e-01 5.618300437927246094e-01 2.831372618675231934e-01 1.000000000000000000e+00 -1.492502838373184204e-01 5.578930974006652832e-01 2.801845371723175049e-01 1.000000000000000000e+00 -1.455594003200531006e-01 5.539561510086059570e-01 2.772318422794342041e-01 1.000000000000000000e+00 -1.418685168027877808e-01 5.500192046165466309e-01 2.742791175842285156e-01 1.000000000000000000e+00 -1.381776183843612671e-01 5.460822582244873047e-01 2.713264226913452148e-01 1.000000000000000000e+00 -1.340253800153732300e-01 5.423298478126525879e-01 2.682814300060272217e-01 1.000000000000000000e+00 -1.297193318605422974e-01 5.386390089988708496e-01 2.652056813240051270e-01 1.000000000000000000e+00 -1.254132986068725586e-01 5.349481105804443359e-01 2.621299624443054199e-01 1.000000000000000000e+00 -1.211072653532028198e-01 5.312572121620178223e-01 2.590542137622833252e-01 1.000000000000000000e+00 -1.168012320995330811e-01 5.275663137435913086e-01 2.559784650802612305e-01 1.000000000000000000e+00 -1.124951913952827454e-01 5.238754153251647949e-01 2.529027163982391357e-01 1.000000000000000000e+00 -1.081891581416130066e-01 5.201845169067382812e-01 2.498269826173782349e-01 1.000000000000000000e+00 -1.038831248879432678e-01 5.164936780929565430e-01 2.467512488365173340e-01 1.000000000000000000e+00 -9.957708418369293213e-02 5.128027796745300293e-01 2.436755150556564331e-01 1.000000000000000000e+00 -9.527105093002319336e-02 5.091118812561035156e-01 2.405997663736343384e-01 1.000000000000000000e+00 -9.096501022577285767e-02 5.054209828376770020e-01 2.375240325927734375e-01 1.000000000000000000e+00 -8.665897697210311890e-02 5.017300844192504883e-01 2.344482839107513428e-01 1.000000000000000000e+00 -8.235294371843338013e-02 4.980392158031463623e-01 2.313725501298904419e-01 1.000000000000000000e+00 -7.804690301418304443e-02 4.943483173847198486e-01 2.282968163490295410e-01 1.000000000000000000e+00 -7.374086976051330566e-02 4.906574487686157227e-01 2.252210676670074463e-01 1.000000000000000000e+00 -6.943482905626296997e-02 4.869665503501892090e-01 2.221453338861465454e-01 1.000000000000000000e+00 -6.512879580259323120e-02 4.832756519317626953e-01 2.190695852041244507e-01 1.000000000000000000e+00 -6.082275882363319397e-02 4.795847833156585693e-01 2.159938514232635498e-01 1.000000000000000000e+00 -5.651672556996345520e-02 4.758938848972320557e-01 2.129181027412414551e-01 1.000000000000000000e+00 -5.221068859100341797e-02 4.722029864788055420e-01 2.098423689603805542e-01 1.000000000000000000e+00 -4.790465161204338074e-02 4.685121178627014160e-01 2.067666351795196533e-01 1.000000000000000000e+00 -4.359861463308334351e-02 4.648212194442749023e-01 2.036908864974975586e-01 1.000000000000000000e+00 -3.929258137941360474e-02 4.611303210258483887e-01 2.006151527166366577e-01 1.000000000000000000e+00 -3.498654440045356750e-02 4.574394524097442627e-01 1.975394040346145630e-01 1.000000000000000000e+00 -3.068050742149353027e-02 4.537485539913177490e-01 1.944636702537536621e-01 1.000000000000000000e+00 -2.637447044253349304e-02 4.500576555728912354e-01 1.913879215717315674e-01 1.000000000000000000e+00 -2.206843532621860504e-02 4.463667869567871094e-01 1.883121877908706665e-01 1.000000000000000000e+00 -1.776239834725856781e-02 4.426758885383605957e-01 1.852364540100097656e-01 1.000000000000000000e+00 -1.345636323094367981e-02 4.389850199222564697e-01 1.821607053279876709e-01 1.000000000000000000e+00 -9.150327183306217194e-03 4.352941215038299561e-01 1.790849715471267700e-01 1.000000000000000000e+00 -4.844290670007467270e-03 4.316032230854034424e-01 1.760092228651046753e-01 1.000000000000000000e+00 -5.382545059546828270e-04 4.279123544692993164e-01 1.729334890842437744e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.230372905731201172e-01 1.707189530134201050e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.179930686950683594e-01 1.686274558305740356e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.129488766193389893e-01 1.665359437465667725e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.079046547412872314e-01 1.644444465637207031e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.028604328632354736e-01 1.623529344797134399e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.978162109851837158e-01 1.602614372968673706e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.927720189094543457e-01 1.581699401140213013e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.877277970314025879e-01 1.560784280300140381e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.826835751533508301e-01 1.539869308471679688e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.776393830776214600e-01 1.518954187631607056e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.725951611995697021e-01 1.498039215803146362e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.675509393215179443e-01 1.477124243974685669e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.625067174434661865e-01 1.456209123134613037e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.574625253677368164e-01 1.435294151306152344e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.524183034896850586e-01 1.414379030466079712e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.473740816116333008e-01 1.393464058637619019e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.423298597335815430e-01 1.372549086809158325e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.372856676578521729e-01 1.351633965969085693e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.322414457798004150e-01 1.330718994140625000e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.271972239017486572e-01 1.309803873300552368e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.221530318260192871e-01 1.288888901472091675e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.171088099479675293e-01 1.267973929643630981e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.120645880699157715e-01 1.247058808803558350e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.070203661918640137e-01 1.226143762469291687e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.019761741161346436e-01 1.205228790640830994e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.969319522380828857e-01 1.184313744306564331e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.918877303600311279e-01 1.163398697972297668e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.868435084819793701e-01 1.142483651638031006e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.817993164062500000e-01 1.121568605303764343e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.767550945281982422e-01 1.100653558969497681e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.717108726501464844e-01 1.079738587141036987e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.666666805744171143e-01 1.058823540806770325e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/Greys b/fastplotlib/utils/colormaps/Greys deleted file mode 100644 index d00696898..000000000 --- a/fastplotlib/utils/colormaps/Greys +++ /dev/null @@ -1,256 +0,0 @@ -1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -9.981545805931091309e-01 9.981545805931091309e-01 9.981545805931091309e-01 1.000000000000000000e+00 -9.963091015815734863e-01 9.963091015815734863e-01 9.963091015815734863e-01 1.000000000000000000e+00 -9.944636821746826172e-01 9.944636821746826172e-01 9.944636821746826172e-01 1.000000000000000000e+00 -9.926182031631469727e-01 9.926182031631469727e-01 9.926182031631469727e-01 1.000000000000000000e+00 -9.907727837562561035e-01 9.907727837562561035e-01 9.907727837562561035e-01 1.000000000000000000e+00 -9.889273643493652344e-01 9.889273643493652344e-01 9.889273643493652344e-01 1.000000000000000000e+00 -9.870818853378295898e-01 9.870818853378295898e-01 9.870818853378295898e-01 1.000000000000000000e+00 -9.852364659309387207e-01 9.852364659309387207e-01 9.852364659309387207e-01 1.000000000000000000e+00 -9.833909869194030762e-01 9.833909869194030762e-01 9.833909869194030762e-01 1.000000000000000000e+00 -9.815455675125122070e-01 9.815455675125122070e-01 9.815455675125122070e-01 1.000000000000000000e+00 -9.797000885009765625e-01 9.797000885009765625e-01 9.797000885009765625e-01 1.000000000000000000e+00 -9.778546690940856934e-01 9.778546690940856934e-01 9.778546690940856934e-01 1.000000000000000000e+00 -9.760092496871948242e-01 9.760092496871948242e-01 9.760092496871948242e-01 1.000000000000000000e+00 -9.741637706756591797e-01 9.741637706756591797e-01 9.741637706756591797e-01 1.000000000000000000e+00 -9.723183512687683105e-01 9.723183512687683105e-01 9.723183512687683105e-01 1.000000000000000000e+00 -9.704728722572326660e-01 9.704728722572326660e-01 9.704728722572326660e-01 1.000000000000000000e+00 -9.686274528503417969e-01 9.686274528503417969e-01 9.686274528503417969e-01 1.000000000000000000e+00 -9.667820334434509277e-01 9.667820334434509277e-01 9.667820334434509277e-01 1.000000000000000000e+00 -9.649365544319152832e-01 9.649365544319152832e-01 9.649365544319152832e-01 1.000000000000000000e+00 -9.630911350250244141e-01 9.630911350250244141e-01 9.630911350250244141e-01 1.000000000000000000e+00 -9.612456560134887695e-01 9.612456560134887695e-01 9.612456560134887695e-01 1.000000000000000000e+00 -9.594002366065979004e-01 9.594002366065979004e-01 9.594002366065979004e-01 1.000000000000000000e+00 -9.575547575950622559e-01 9.575547575950622559e-01 9.575547575950622559e-01 1.000000000000000000e+00 -9.557093381881713867e-01 9.557093381881713867e-01 9.557093381881713867e-01 1.000000000000000000e+00 -9.538639187812805176e-01 9.538639187812805176e-01 9.538639187812805176e-01 1.000000000000000000e+00 -9.520184397697448730e-01 9.520184397697448730e-01 9.520184397697448730e-01 1.000000000000000000e+00 -9.501730203628540039e-01 9.501730203628540039e-01 9.501730203628540039e-01 1.000000000000000000e+00 -9.483275413513183594e-01 9.483275413513183594e-01 9.483275413513183594e-01 1.000000000000000000e+00 -9.464821219444274902e-01 9.464821219444274902e-01 9.464821219444274902e-01 1.000000000000000000e+00 -9.446367025375366211e-01 9.446367025375366211e-01 9.446367025375366211e-01 1.000000000000000000e+00 -9.427912235260009766e-01 9.427912235260009766e-01 9.427912235260009766e-01 1.000000000000000000e+00 -9.408227801322937012e-01 9.408227801322937012e-01 9.408227801322937012e-01 1.000000000000000000e+00 -9.379931092262268066e-01 9.379931092262268066e-01 9.379931092262268066e-01 1.000000000000000000e+00 -9.351633787155151367e-01 9.351633787155151367e-01 9.351633787155151367e-01 1.000000000000000000e+00 -9.323337078094482422e-01 9.323337078094482422e-01 9.323337078094482422e-01 1.000000000000000000e+00 -9.295040369033813477e-01 9.295040369033813477e-01 9.295040369033813477e-01 1.000000000000000000e+00 -9.266743659973144531e-01 9.266743659973144531e-01 9.266743659973144531e-01 1.000000000000000000e+00 -9.238446950912475586e-01 9.238446950912475586e-01 9.238446950912475586e-01 1.000000000000000000e+00 -9.210149645805358887e-01 9.210149645805358887e-01 9.210149645805358887e-01 1.000000000000000000e+00 -9.181852936744689941e-01 9.181852936744689941e-01 9.181852936744689941e-01 1.000000000000000000e+00 -9.153556227684020996e-01 9.153556227684020996e-01 9.153556227684020996e-01 1.000000000000000000e+00 -9.125259518623352051e-01 9.125259518623352051e-01 9.125259518623352051e-01 1.000000000000000000e+00 -9.096962809562683105e-01 9.096962809562683105e-01 9.096962809562683105e-01 1.000000000000000000e+00 -9.068666100502014160e-01 9.068666100502014160e-01 9.068666100502014160e-01 1.000000000000000000e+00 -9.040368795394897461e-01 9.040368795394897461e-01 9.040368795394897461e-01 1.000000000000000000e+00 -9.012072086334228516e-01 9.012072086334228516e-01 9.012072086334228516e-01 1.000000000000000000e+00 -8.983775377273559570e-01 8.983775377273559570e-01 8.983775377273559570e-01 1.000000000000000000e+00 -8.955478668212890625e-01 8.955478668212890625e-01 8.955478668212890625e-01 1.000000000000000000e+00 -8.927181959152221680e-01 8.927181959152221680e-01 8.927181959152221680e-01 1.000000000000000000e+00 -8.898885250091552734e-01 8.898885250091552734e-01 8.898885250091552734e-01 1.000000000000000000e+00 -8.870587944984436035e-01 8.870587944984436035e-01 8.870587944984436035e-01 1.000000000000000000e+00 -8.842291235923767090e-01 8.842291235923767090e-01 8.842291235923767090e-01 1.000000000000000000e+00 -8.813994526863098145e-01 8.813994526863098145e-01 8.813994526863098145e-01 1.000000000000000000e+00 -8.785697817802429199e-01 8.785697817802429199e-01 8.785697817802429199e-01 1.000000000000000000e+00 -8.757401108741760254e-01 8.757401108741760254e-01 8.757401108741760254e-01 1.000000000000000000e+00 -8.729104399681091309e-01 8.729104399681091309e-01 8.729104399681091309e-01 1.000000000000000000e+00 -8.700807094573974609e-01 8.700807094573974609e-01 8.700807094573974609e-01 1.000000000000000000e+00 -8.672510385513305664e-01 8.672510385513305664e-01 8.672510385513305664e-01 1.000000000000000000e+00 -8.644213676452636719e-01 8.644213676452636719e-01 8.644213676452636719e-01 1.000000000000000000e+00 -8.615916967391967773e-01 8.615916967391967773e-01 8.615916967391967773e-01 1.000000000000000000e+00 -8.587620258331298828e-01 8.587620258331298828e-01 8.587620258331298828e-01 1.000000000000000000e+00 -8.559323549270629883e-01 8.559323549270629883e-01 8.559323549270629883e-01 1.000000000000000000e+00 -8.531026244163513184e-01 8.531026244163513184e-01 8.531026244163513184e-01 1.000000000000000000e+00 -8.501191735267639160e-01 8.501191735267639160e-01 8.501191735267639160e-01 1.000000000000000000e+00 -8.466743826866149902e-01 8.466743826866149902e-01 8.466743826866149902e-01 1.000000000000000000e+00 -8.432295322418212891e-01 8.432295322418212891e-01 8.432295322418212891e-01 1.000000000000000000e+00 -8.397846817970275879e-01 8.397846817970275879e-01 8.397846817970275879e-01 1.000000000000000000e+00 -8.363398909568786621e-01 8.363398909568786621e-01 8.363398909568786621e-01 1.000000000000000000e+00 -8.328950405120849609e-01 8.328950405120849609e-01 8.328950405120849609e-01 1.000000000000000000e+00 -8.294501900672912598e-01 8.294501900672912598e-01 8.294501900672912598e-01 1.000000000000000000e+00 -8.260053992271423340e-01 8.260053992271423340e-01 8.260053992271423340e-01 1.000000000000000000e+00 -8.225605487823486328e-01 8.225605487823486328e-01 8.225605487823486328e-01 1.000000000000000000e+00 -8.191156983375549316e-01 8.191156983375549316e-01 8.191156983375549316e-01 1.000000000000000000e+00 -8.156709074974060059e-01 8.156709074974060059e-01 8.156709074974060059e-01 1.000000000000000000e+00 -8.122260570526123047e-01 8.122260570526123047e-01 8.122260570526123047e-01 1.000000000000000000e+00 -8.087812662124633789e-01 8.087812662124633789e-01 8.087812662124633789e-01 1.000000000000000000e+00 -8.053364157676696777e-01 8.053364157676696777e-01 8.053364157676696777e-01 1.000000000000000000e+00 -8.018915653228759766e-01 8.018915653228759766e-01 8.018915653228759766e-01 1.000000000000000000e+00 -7.984467744827270508e-01 7.984467744827270508e-01 7.984467744827270508e-01 1.000000000000000000e+00 -7.950019240379333496e-01 7.950019240379333496e-01 7.950019240379333496e-01 1.000000000000000000e+00 -7.915570735931396484e-01 7.915570735931396484e-01 7.915570735931396484e-01 1.000000000000000000e+00 -7.881122827529907227e-01 7.881122827529907227e-01 7.881122827529907227e-01 1.000000000000000000e+00 -7.846674323081970215e-01 7.846674323081970215e-01 7.846674323081970215e-01 1.000000000000000000e+00 -7.812225818634033203e-01 7.812225818634033203e-01 7.812225818634033203e-01 1.000000000000000000e+00 -7.777777910232543945e-01 7.777777910232543945e-01 7.777777910232543945e-01 1.000000000000000000e+00 -7.743329405784606934e-01 7.743329405784606934e-01 7.743329405784606934e-01 1.000000000000000000e+00 -7.708881497383117676e-01 7.708881497383117676e-01 7.708881497383117676e-01 1.000000000000000000e+00 -7.674432992935180664e-01 7.674432992935180664e-01 7.674432992935180664e-01 1.000000000000000000e+00 -7.639984488487243652e-01 7.639984488487243652e-01 7.639984488487243652e-01 1.000000000000000000e+00 -7.605536580085754395e-01 7.605536580085754395e-01 7.605536580085754395e-01 1.000000000000000000e+00 -7.571088075637817383e-01 7.571088075637817383e-01 7.571088075637817383e-01 1.000000000000000000e+00 -7.536639571189880371e-01 7.536639571189880371e-01 7.536639571189880371e-01 1.000000000000000000e+00 -7.502191662788391113e-01 7.502191662788391113e-01 7.502191662788391113e-01 1.000000000000000000e+00 -7.467743158340454102e-01 7.467743158340454102e-01 7.467743158340454102e-01 1.000000000000000000e+00 -7.433294653892517090e-01 7.433294653892517090e-01 7.433294653892517090e-01 1.000000000000000000e+00 -7.393771409988403320e-01 7.393771409988403320e-01 7.393771409988403320e-01 1.000000000000000000e+00 -7.345790266990661621e-01 7.345790266990661621e-01 7.345790266990661621e-01 1.000000000000000000e+00 -7.297808527946472168e-01 7.297808527946472168e-01 7.297808527946472168e-01 1.000000000000000000e+00 -7.249826788902282715e-01 7.249826788902282715e-01 7.249826788902282715e-01 1.000000000000000000e+00 -7.201845645904541016e-01 7.201845645904541016e-01 7.201845645904541016e-01 1.000000000000000000e+00 -7.153863906860351562e-01 7.153863906860351562e-01 7.153863906860351562e-01 1.000000000000000000e+00 -7.105882167816162109e-01 7.105882167816162109e-01 7.105882167816162109e-01 1.000000000000000000e+00 -7.057901024818420410e-01 7.057901024818420410e-01 7.057901024818420410e-01 1.000000000000000000e+00 -7.009919285774230957e-01 7.009919285774230957e-01 7.009919285774230957e-01 1.000000000000000000e+00 -6.961937546730041504e-01 6.961937546730041504e-01 6.961937546730041504e-01 1.000000000000000000e+00 -6.913956403732299805e-01 6.913956403732299805e-01 6.913956403732299805e-01 1.000000000000000000e+00 -6.865974664688110352e-01 6.865974664688110352e-01 6.865974664688110352e-01 1.000000000000000000e+00 -6.817992925643920898e-01 6.817992925643920898e-01 6.817992925643920898e-01 1.000000000000000000e+00 -6.770011782646179199e-01 6.770011782646179199e-01 6.770011782646179199e-01 1.000000000000000000e+00 -6.722030043601989746e-01 6.722030043601989746e-01 6.722030043601989746e-01 1.000000000000000000e+00 -6.674048304557800293e-01 6.674048304557800293e-01 6.674048304557800293e-01 1.000000000000000000e+00 -6.626067161560058594e-01 6.626067161560058594e-01 6.626067161560058594e-01 1.000000000000000000e+00 -6.578085422515869141e-01 6.578085422515869141e-01 6.578085422515869141e-01 1.000000000000000000e+00 -6.530103683471679688e-01 6.530103683471679688e-01 6.530103683471679688e-01 1.000000000000000000e+00 -6.482122540473937988e-01 6.482122540473937988e-01 6.482122540473937988e-01 1.000000000000000000e+00 -6.434140801429748535e-01 6.434140801429748535e-01 6.434140801429748535e-01 1.000000000000000000e+00 -6.386159062385559082e-01 6.386159062385559082e-01 6.386159062385559082e-01 1.000000000000000000e+00 -6.338177919387817383e-01 6.338177919387817383e-01 6.338177919387817383e-01 1.000000000000000000e+00 -6.290196180343627930e-01 6.290196180343627930e-01 6.290196180343627930e-01 1.000000000000000000e+00 -6.242214441299438477e-01 6.242214441299438477e-01 6.242214441299438477e-01 1.000000000000000000e+00 -6.194232702255249023e-01 6.194232702255249023e-01 6.194232702255249023e-01 1.000000000000000000e+00 -6.146251559257507324e-01 6.146251559257507324e-01 6.146251559257507324e-01 1.000000000000000000e+00 -6.098269820213317871e-01 6.098269820213317871e-01 6.098269820213317871e-01 1.000000000000000000e+00 -6.050288081169128418e-01 6.050288081169128418e-01 6.050288081169128418e-01 1.000000000000000000e+00 -6.002306938171386719e-01 6.002306938171386719e-01 6.002306938171386719e-01 1.000000000000000000e+00 -5.954325199127197266e-01 5.954325199127197266e-01 5.954325199127197266e-01 1.000000000000000000e+00 -5.906343460083007812e-01 5.906343460083007812e-01 5.906343460083007812e-01 1.000000000000000000e+00 -5.860822796821594238e-01 5.860822796821594238e-01 5.860822796821594238e-01 1.000000000000000000e+00 -5.817762613296508789e-01 5.817762613296508789e-01 5.817762613296508789e-01 1.000000000000000000e+00 -5.774701833724975586e-01 5.774701833724975586e-01 5.774701833724975586e-01 1.000000000000000000e+00 -5.731641650199890137e-01 5.731641650199890137e-01 5.731641650199890137e-01 1.000000000000000000e+00 -5.688581466674804688e-01 5.688581466674804688e-01 5.688581466674804688e-01 1.000000000000000000e+00 -5.645520687103271484e-01 5.645520687103271484e-01 5.645520687103271484e-01 1.000000000000000000e+00 -5.602460503578186035e-01 5.602460503578186035e-01 5.602460503578186035e-01 1.000000000000000000e+00 -5.559400320053100586e-01 5.559400320053100586e-01 5.559400320053100586e-01 1.000000000000000000e+00 -5.516340136528015137e-01 5.516340136528015137e-01 5.516340136528015137e-01 1.000000000000000000e+00 -5.473279356956481934e-01 5.473279356956481934e-01 5.473279356956481934e-01 1.000000000000000000e+00 -5.430219173431396484e-01 5.430219173431396484e-01 5.430219173431396484e-01 1.000000000000000000e+00 -5.387158989906311035e-01 5.387158989906311035e-01 5.387158989906311035e-01 1.000000000000000000e+00 -5.344098210334777832e-01 5.344098210334777832e-01 5.344098210334777832e-01 1.000000000000000000e+00 -5.301038026809692383e-01 5.301038026809692383e-01 5.301038026809692383e-01 1.000000000000000000e+00 -5.257977843284606934e-01 5.257977843284606934e-01 5.257977843284606934e-01 1.000000000000000000e+00 -5.214917063713073730e-01 5.214917063713073730e-01 5.214917063713073730e-01 1.000000000000000000e+00 -5.171856880187988281e-01 5.171856880187988281e-01 5.171856880187988281e-01 1.000000000000000000e+00 -5.128796696662902832e-01 5.128796696662902832e-01 5.128796696662902832e-01 1.000000000000000000e+00 -5.085736513137817383e-01 5.085736513137817383e-01 5.085736513137817383e-01 1.000000000000000000e+00 -5.042675733566284180e-01 5.042675733566284180e-01 5.042675733566284180e-01 1.000000000000000000e+00 -4.999615550041198730e-01 4.999615550041198730e-01 4.999615550041198730e-01 1.000000000000000000e+00 -4.956555068492889404e-01 4.956555068492889404e-01 4.956555068492889404e-01 1.000000000000000000e+00 -4.913494884967803955e-01 4.913494884967803955e-01 4.913494884967803955e-01 1.000000000000000000e+00 -4.870434403419494629e-01 4.870434403419494629e-01 4.870434403419494629e-01 1.000000000000000000e+00 -4.827374219894409180e-01 4.827374219894409180e-01 4.827374219894409180e-01 1.000000000000000000e+00 -4.784313738346099854e-01 4.784313738346099854e-01 4.784313738346099854e-01 1.000000000000000000e+00 -4.741253256797790527e-01 4.741253256797790527e-01 4.741253256797790527e-01 1.000000000000000000e+00 -4.698193073272705078e-01 4.698193073272705078e-01 4.698193073272705078e-01 1.000000000000000000e+00 -4.655132591724395752e-01 4.655132591724395752e-01 4.655132591724395752e-01 1.000000000000000000e+00 -4.612072408199310303e-01 4.612072408199310303e-01 4.612072408199310303e-01 1.000000000000000000e+00 -4.569011926651000977e-01 4.569011926651000977e-01 4.569011926651000977e-01 1.000000000000000000e+00 -4.525951445102691650e-01 4.525951445102691650e-01 4.525951445102691650e-01 1.000000000000000000e+00 -4.484429061412811279e-01 4.484429061412811279e-01 4.484429061412811279e-01 1.000000000000000000e+00 -4.443829357624053955e-01 4.443829357624053955e-01 4.443829357624053955e-01 1.000000000000000000e+00 -4.403229653835296631e-01 4.403229653835296631e-01 4.403229653835296631e-01 1.000000000000000000e+00 -4.362629652023315430e-01 4.362629652023315430e-01 4.362629652023315430e-01 1.000000000000000000e+00 -4.322029948234558105e-01 4.322029948234558105e-01 4.322029948234558105e-01 1.000000000000000000e+00 -4.281430244445800781e-01 4.281430244445800781e-01 4.281430244445800781e-01 1.000000000000000000e+00 -4.240830540657043457e-01 4.240830540657043457e-01 4.240830540657043457e-01 1.000000000000000000e+00 -4.200230538845062256e-01 4.200230538845062256e-01 4.200230538845062256e-01 1.000000000000000000e+00 -4.159630835056304932e-01 4.159630835056304932e-01 4.159630835056304932e-01 1.000000000000000000e+00 -4.119031131267547607e-01 4.119031131267547607e-01 4.119031131267547607e-01 1.000000000000000000e+00 -4.078431427478790283e-01 4.078431427478790283e-01 4.078431427478790283e-01 1.000000000000000000e+00 -4.037831723690032959e-01 4.037831723690032959e-01 4.037831723690032959e-01 1.000000000000000000e+00 -3.997231721878051758e-01 3.997231721878051758e-01 3.997231721878051758e-01 1.000000000000000000e+00 -3.956632018089294434e-01 3.956632018089294434e-01 3.956632018089294434e-01 1.000000000000000000e+00 -3.916032314300537109e-01 3.916032314300537109e-01 3.916032314300537109e-01 1.000000000000000000e+00 -3.875432610511779785e-01 3.875432610511779785e-01 3.875432610511779785e-01 1.000000000000000000e+00 -3.834832608699798584e-01 3.834832608699798584e-01 3.834832608699798584e-01 1.000000000000000000e+00 -3.794232904911041260e-01 3.794232904911041260e-01 3.794232904911041260e-01 1.000000000000000000e+00 -3.753633201122283936e-01 3.753633201122283936e-01 3.753633201122283936e-01 1.000000000000000000e+00 -3.713033497333526611e-01 3.713033497333526611e-01 3.713033497333526611e-01 1.000000000000000000e+00 -3.672433793544769287e-01 3.672433793544769287e-01 3.672433793544769287e-01 1.000000000000000000e+00 -3.631833791732788086e-01 3.631833791732788086e-01 3.631833791732788086e-01 1.000000000000000000e+00 -3.591234087944030762e-01 3.591234087944030762e-01 3.591234087944030762e-01 1.000000000000000000e+00 -3.550634384155273438e-01 3.550634384155273438e-01 3.550634384155273438e-01 1.000000000000000000e+00 -3.510034680366516113e-01 3.510034680366516113e-01 3.510034680366516113e-01 1.000000000000000000e+00 -3.469434976577758789e-01 3.469434976577758789e-01 3.469434976577758789e-01 1.000000000000000000e+00 -3.428834974765777588e-01 3.428834974765777588e-01 3.428834974765777588e-01 1.000000000000000000e+00 -3.388235270977020264e-01 3.388235270977020264e-01 3.388235270977020264e-01 1.000000000000000000e+00 -3.347635567188262939e-01 3.347635567188262939e-01 3.347635567188262939e-01 1.000000000000000000e+00 -3.307035863399505615e-01 3.307035863399505615e-01 3.307035863399505615e-01 1.000000000000000000e+00 -3.266435861587524414e-01 3.266435861587524414e-01 3.266435861587524414e-01 1.000000000000000000e+00 -3.225836157798767090e-01 3.225836157798767090e-01 3.225836157798767090e-01 1.000000000000000000e+00 -3.174163699150085449e-01 3.174163699150085449e-01 3.174163699150085449e-01 1.000000000000000000e+00 -3.118800520896911621e-01 3.118800520896911621e-01 3.118800520896911621e-01 1.000000000000000000e+00 -3.063437044620513916e-01 3.063437044620513916e-01 3.063437044620513916e-01 1.000000000000000000e+00 -3.008073866367340088e-01 3.008073866367340088e-01 3.008073866367340088e-01 1.000000000000000000e+00 -2.952710390090942383e-01 2.952710390090942383e-01 2.952710390090942383e-01 1.000000000000000000e+00 -2.897347211837768555e-01 2.897347211837768555e-01 2.897347211837768555e-01 1.000000000000000000e+00 -2.841983735561370850e-01 2.841983735561370850e-01 2.841983735561370850e-01 1.000000000000000000e+00 -2.786620557308197021e-01 2.786620557308197021e-01 2.786620557308197021e-01 1.000000000000000000e+00 -2.731257081031799316e-01 2.731257081031799316e-01 2.731257081031799316e-01 1.000000000000000000e+00 -2.675893902778625488e-01 2.675893902778625488e-01 2.675893902778625488e-01 1.000000000000000000e+00 -2.620530426502227783e-01 2.620530426502227783e-01 2.620530426502227783e-01 1.000000000000000000e+00 -2.565167248249053955e-01 2.565167248249053955e-01 2.565167248249053955e-01 1.000000000000000000e+00 -2.509804069995880127e-01 2.509804069995880127e-01 2.509804069995880127e-01 1.000000000000000000e+00 -2.454440593719482422e-01 2.454440593719482422e-01 2.454440593719482422e-01 1.000000000000000000e+00 -2.399077266454696655e-01 2.399077266454696655e-01 2.399077266454696655e-01 1.000000000000000000e+00 -2.343713939189910889e-01 2.343713939189910889e-01 2.343713939189910889e-01 1.000000000000000000e+00 -2.288350611925125122e-01 2.288350611925125122e-01 2.288350611925125122e-01 1.000000000000000000e+00 -2.232987284660339355e-01 2.232987284660339355e-01 2.232987284660339355e-01 1.000000000000000000e+00 -2.177623957395553589e-01 2.177623957395553589e-01 2.177623957395553589e-01 1.000000000000000000e+00 -2.122260630130767822e-01 2.122260630130767822e-01 2.122260630130767822e-01 1.000000000000000000e+00 -2.066897302865982056e-01 2.066897302865982056e-01 2.066897302865982056e-01 1.000000000000000000e+00 -2.011533975601196289e-01 2.011533975601196289e-01 2.011533975601196289e-01 1.000000000000000000e+00 -1.956170648336410522e-01 1.956170648336410522e-01 1.956170648336410522e-01 1.000000000000000000e+00 -1.900807321071624756e-01 1.900807321071624756e-01 1.900807321071624756e-01 1.000000000000000000e+00 -1.845443993806838989e-01 1.845443993806838989e-01 1.845443993806838989e-01 1.000000000000000000e+00 -1.790080666542053223e-01 1.790080666542053223e-01 1.790080666542053223e-01 1.000000000000000000e+00 -1.734717488288879395e-01 1.734717488288879395e-01 1.734717488288879395e-01 1.000000000000000000e+00 -1.679354161024093628e-01 1.679354161024093628e-01 1.679354161024093628e-01 1.000000000000000000e+00 -1.623990833759307861e-01 1.623990833759307861e-01 1.623990833759307861e-01 1.000000000000000000e+00 -1.568627506494522095e-01 1.568627506494522095e-01 1.568627506494522095e-01 1.000000000000000000e+00 -1.513264179229736328e-01 1.513264179229736328e-01 1.513264179229736328e-01 1.000000000000000000e+00 -1.457900851964950562e-01 1.457900851964950562e-01 1.457900851964950562e-01 1.000000000000000000e+00 -1.411149501800537109e-01 1.411149501800537109e-01 1.411149501800537109e-01 1.000000000000000000e+00 -1.365628540515899658e-01 1.365628540515899658e-01 1.365628540515899658e-01 1.000000000000000000e+00 -1.320107579231262207e-01 1.320107579231262207e-01 1.320107579231262207e-01 1.000000000000000000e+00 -1.274586766958236694e-01 1.274586766958236694e-01 1.274586766958236694e-01 1.000000000000000000e+00 -1.229065731167793274e-01 1.229065731167793274e-01 1.229065731167793274e-01 1.000000000000000000e+00 -1.183544769883155823e-01 1.183544769883155823e-01 1.183544769883155823e-01 1.000000000000000000e+00 -1.138023808598518372e-01 1.138023808598518372e-01 1.138023808598518372e-01 1.000000000000000000e+00 -1.092502847313880920e-01 1.092502847313880920e-01 1.092502847313880920e-01 1.000000000000000000e+00 -1.046981960535049438e-01 1.046981960535049438e-01 1.046981960535049438e-01 1.000000000000000000e+00 -1.001460999250411987e-01 1.001460999250411987e-01 1.001460999250411987e-01 1.000000000000000000e+00 -9.559400379657745361e-02 9.559400379657745361e-02 9.559400379657745361e-02 1.000000000000000000e+00 -9.104190766811370850e-02 9.104190766811370850e-02 9.104190766811370850e-02 1.000000000000000000e+00 -8.648981153964996338e-02 8.648981153964996338e-02 8.648981153964996338e-02 1.000000000000000000e+00 -8.193771541118621826e-02 8.193771541118621826e-02 8.193771541118621826e-02 1.000000000000000000e+00 -7.738561928272247314e-02 7.738561928272247314e-02 7.738561928272247314e-02 1.000000000000000000e+00 -7.283352315425872803e-02 7.283352315425872803e-02 7.283352315425872803e-02 1.000000000000000000e+00 -6.828142702579498291e-02 6.828142702579498291e-02 6.828142702579498291e-02 1.000000000000000000e+00 -6.372933834791183472e-02 6.372933834791183472e-02 6.372933834791183472e-02 1.000000000000000000e+00 -5.917723849415779114e-02 5.917723849415779114e-02 5.917723849415779114e-02 1.000000000000000000e+00 -5.462514236569404602e-02 5.462514236569404602e-02 5.462514236569404602e-02 1.000000000000000000e+00 -5.007304996252059937e-02 5.007304996252059937e-02 5.007304996252059937e-02 1.000000000000000000e+00 -4.552095383405685425e-02 4.552095383405685425e-02 4.552095383405685425e-02 1.000000000000000000e+00 -4.096885770559310913e-02 4.096885770559310913e-02 4.096885770559310913e-02 1.000000000000000000e+00 -3.641676157712936401e-02 3.641676157712936401e-02 3.641676157712936401e-02 1.000000000000000000e+00 -3.186466917395591736e-02 3.186466917395591736e-02 3.186466917395591736e-02 1.000000000000000000e+00 -2.731257118284702301e-02 2.731257118284702301e-02 2.731257118284702301e-02 1.000000000000000000e+00 -2.276047691702842712e-02 2.276047691702842712e-02 2.276047691702842712e-02 1.000000000000000000e+00 -1.820838078856468201e-02 1.820838078856468201e-02 1.820838078856468201e-02 1.000000000000000000e+00 -1.365628559142351151e-02 1.365628559142351151e-02 1.365628559142351151e-02 1.000000000000000000e+00 -9.104190394282341003e-03 9.104190394282341003e-03 9.104190394282341003e-03 1.000000000000000000e+00 -4.552095197141170502e-03 4.552095197141170502e-03 4.552095197141170502e-03 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/OrRd b/fastplotlib/utils/colormaps/OrRd deleted file mode 100644 index 898a6d7c4..000000000 --- a/fastplotlib/utils/colormaps/OrRd +++ /dev/null @@ -1,256 +0,0 @@ -1.000000000000000000e+00 9.686274528503417969e-01 9.254902005195617676e-01 1.000000000000000000e+00 -9.998769760131835938e-01 9.667820334434509277e-01 9.210611581802368164e-01 1.000000000000000000e+00 -9.997539520263671875e-01 9.649365544319152832e-01 9.166320562362670898e-01 1.000000000000000000e+00 -9.996309280395507812e-01 9.630911350250244141e-01 9.122030138969421387e-01 1.000000000000000000e+00 -9.995079040527343750e-01 9.612456560134887695e-01 9.077739119529724121e-01 1.000000000000000000e+00 -9.993848800659179688e-01 9.594002366065979004e-01 9.033448696136474609e-01 1.000000000000000000e+00 -9.992617964744567871e-01 9.575547575950622559e-01 8.989158272743225098e-01 1.000000000000000000e+00 -9.991387724876403809e-01 9.557093381881713867e-01 8.944867253303527832e-01 1.000000000000000000e+00 -9.990157485008239746e-01 9.538639187812805176e-01 8.900576829910278320e-01 1.000000000000000000e+00 -9.988927245140075684e-01 9.520184397697448730e-01 8.856285810470581055e-01 1.000000000000000000e+00 -9.987697005271911621e-01 9.501730203628540039e-01 8.811995387077331543e-01 1.000000000000000000e+00 -9.986466765403747559e-01 9.483275413513183594e-01 8.767704963684082031e-01 1.000000000000000000e+00 -9.985236525535583496e-01 9.464821219444274902e-01 8.723413944244384766e-01 1.000000000000000000e+00 -9.984006285667419434e-01 9.446367025375366211e-01 8.679123520851135254e-01 1.000000000000000000e+00 -9.982776045799255371e-01 9.427912235260009766e-01 8.634832501411437988e-01 1.000000000000000000e+00 -9.981545805931091309e-01 9.409458041191101074e-01 8.590542078018188477e-01 1.000000000000000000e+00 -9.980314970016479492e-01 9.391003251075744629e-01 8.546251654624938965e-01 1.000000000000000000e+00 -9.979084730148315430e-01 9.372549057006835938e-01 8.501960635185241699e-01 1.000000000000000000e+00 -9.977854490280151367e-01 9.354094862937927246e-01 8.457670211791992188e-01 1.000000000000000000e+00 -9.976624250411987305e-01 9.335640072822570801e-01 8.413379192352294922e-01 1.000000000000000000e+00 -9.975394010543823242e-01 9.317185878753662109e-01 8.369088768959045410e-01 1.000000000000000000e+00 -9.974163770675659180e-01 9.298731088638305664e-01 8.324798345565795898e-01 1.000000000000000000e+00 -9.972933530807495117e-01 9.280276894569396973e-01 8.280507326126098633e-01 1.000000000000000000e+00 -9.971703290939331055e-01 9.261822104454040527e-01 8.236216902732849121e-01 1.000000000000000000e+00 -9.970473051071166992e-01 9.243367910385131836e-01 8.191926479339599609e-01 1.000000000000000000e+00 -9.969242811203002930e-01 9.224913716316223145e-01 8.147635459899902344e-01 1.000000000000000000e+00 -9.968012571334838867e-01 9.206458926200866699e-01 8.103345036506652832e-01 1.000000000000000000e+00 -9.966781735420227051e-01 9.188004732131958008e-01 8.059054017066955566e-01 1.000000000000000000e+00 -9.965551495552062988e-01 9.169549942016601562e-01 8.014763593673706055e-01 1.000000000000000000e+00 -9.964321255683898926e-01 9.151095747947692871e-01 7.970473170280456543e-01 1.000000000000000000e+00 -9.963091015815734863e-01 9.132641553878784180e-01 7.926182150840759277e-01 1.000000000000000000e+00 -9.961860775947570801e-01 9.114186763763427734e-01 7.881891727447509766e-01 1.000000000000000000e+00 -9.960630536079406738e-01 9.094963669776916504e-01 7.836678028106689453e-01 1.000000000000000000e+00 -9.959400296211242676e-01 9.070357680320739746e-01 7.785005569458007812e-01 1.000000000000000000e+00 -9.958170056343078613e-01 9.045751690864562988e-01 7.733333110809326172e-01 1.000000000000000000e+00 -9.956939816474914551e-01 9.021145701408386230e-01 7.681660652160644531e-01 1.000000000000000000e+00 -9.955709576606750488e-01 8.996539711952209473e-01 7.629988193511962891e-01 1.000000000000000000e+00 -9.954479336738586426e-01 8.971933722496032715e-01 7.578315734863281250e-01 1.000000000000000000e+00 -9.953248500823974609e-01 8.947327733039855957e-01 7.526643872261047363e-01 1.000000000000000000e+00 -9.952018260955810547e-01 8.922721743583679199e-01 7.474971413612365723e-01 1.000000000000000000e+00 -9.950788021087646484e-01 8.898116350173950195e-01 7.423298954963684082e-01 1.000000000000000000e+00 -9.949557781219482422e-01 8.873510360717773438e-01 7.371626496315002441e-01 1.000000000000000000e+00 -9.948327541351318359e-01 8.848904371261596680e-01 7.319954037666320801e-01 1.000000000000000000e+00 -9.947097301483154297e-01 8.824298381805419922e-01 7.268281579017639160e-01 1.000000000000000000e+00 -9.945867061614990234e-01 8.799692392349243164e-01 7.216609120368957520e-01 1.000000000000000000e+00 -9.944636821746826172e-01 8.775086402893066406e-01 7.164936661720275879e-01 1.000000000000000000e+00 -9.943406581878662109e-01 8.750480413436889648e-01 7.113264203071594238e-01 1.000000000000000000e+00 -9.942176342010498047e-01 8.725874423980712891e-01 7.061591744422912598e-01 1.000000000000000000e+00 -9.940945506095886230e-01 8.701269030570983887e-01 7.009919285774230957e-01 1.000000000000000000e+00 -9.939715266227722168e-01 8.676663041114807129e-01 6.958246827125549316e-01 1.000000000000000000e+00 -9.938485026359558105e-01 8.652057051658630371e-01 6.906574368476867676e-01 1.000000000000000000e+00 -9.937254786491394043e-01 8.627451062202453613e-01 6.854901909828186035e-01 1.000000000000000000e+00 -9.936024546623229980e-01 8.602845072746276855e-01 6.803229451179504395e-01 1.000000000000000000e+00 -9.934794306755065918e-01 8.578239083290100098e-01 6.751556992530822754e-01 1.000000000000000000e+00 -9.933564066886901855e-01 8.553633093833923340e-01 6.699884533882141113e-01 1.000000000000000000e+00 -9.932333827018737793e-01 8.529027104377746582e-01 6.648212075233459473e-01 1.000000000000000000e+00 -9.931103587150573730e-01 8.504421114921569824e-01 6.596539616584777832e-01 1.000000000000000000e+00 -9.929873347282409668e-01 8.479815721511840820e-01 6.544867157936096191e-01 1.000000000000000000e+00 -9.928643107414245605e-01 8.455209732055664062e-01 6.493194699287414551e-01 1.000000000000000000e+00 -9.927412271499633789e-01 8.430603742599487305e-01 6.441522240638732910e-01 1.000000000000000000e+00 -9.926182031631469727e-01 8.405997753143310547e-01 6.389849781990051270e-01 1.000000000000000000e+00 -9.924951791763305664e-01 8.381391763687133789e-01 6.338177919387817383e-01 1.000000000000000000e+00 -9.923721551895141602e-01 8.356785774230957031e-01 6.286505460739135742e-01 1.000000000000000000e+00 -9.922491312026977539e-01 8.332179784774780273e-01 6.234833002090454102e-01 1.000000000000000000e+00 -9.921568632125854492e-01 8.306035995483398438e-01 6.188081502914428711e-01 1.000000000000000000e+00 -9.921568632125854492e-01 8.275278806686401367e-01 6.156094074249267578e-01 1.000000000000000000e+00 -9.921568632125854492e-01 8.244521617889404297e-01 6.124106049537658691e-01 1.000000000000000000e+00 -9.921568632125854492e-01 8.213763833045959473e-01 6.092118620872497559e-01 1.000000000000000000e+00 -9.921568632125854492e-01 8.183006644248962402e-01 6.060130596160888672e-01 1.000000000000000000e+00 -9.921568632125854492e-01 8.152248859405517578e-01 6.028143167495727539e-01 1.000000000000000000e+00 -9.921568632125854492e-01 8.121491670608520508e-01 5.996155142784118652e-01 1.000000000000000000e+00 -9.921568632125854492e-01 8.090734481811523438e-01 5.964167714118957520e-01 1.000000000000000000e+00 -9.921568632125854492e-01 8.059976696968078613e-01 5.932179689407348633e-01 1.000000000000000000e+00 -9.921568632125854492e-01 8.029219508171081543e-01 5.900192260742187500e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.998462319374084473e-01 5.868204832077026367e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.967704534530639648e-01 5.836216807365417480e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.936947345733642578e-01 5.804229378700256348e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.906190156936645508e-01 5.772241353988647461e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.875432372093200684e-01 5.740253925323486328e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.844675183296203613e-01 5.708265900611877441e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.813917994499206543e-01 5.676278471946716309e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.783160209655761719e-01 5.644290447235107422e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.752403020858764648e-01 5.612303018569946289e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.721645236015319824e-01 5.580314993858337402e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.690888047218322754e-01 5.548327565193176270e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.660130858421325684e-01 5.516340136528015137e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.629373073577880859e-01 5.484352111816406250e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.598615884780883789e-01 5.452364683151245117e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.567858695983886719e-01 5.420376658439636230e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.537100911140441895e-01 5.388389229774475098e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.506343722343444824e-01 5.356401205062866211e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.475586533546447754e-01 5.324413776397705078e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.444828748703002930e-01 5.292425751686096191e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.414071559906005859e-01 5.260438323020935059e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.383314371109008789e-01 5.228450298309326172e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.352556586265563965e-01 5.196462869644165039e-01 1.000000000000000000e+00 -9.921107292175292969e-01 7.312110662460327148e-01 5.156632065773010254e-01 1.000000000000000000e+00 -9.919877052307128906e-01 7.255517244338989258e-01 5.103729367256164551e-01 1.000000000000000000e+00 -9.918646812438964844e-01 7.198923230171203613e-01 5.050826668739318848e-01 1.000000000000000000e+00 -9.917416572570800781e-01 7.142329812049865723e-01 4.997923970222473145e-01 1.000000000000000000e+00 -9.916186332702636719e-01 7.085736393928527832e-01 4.945021271705627441e-01 1.000000000000000000e+00 -9.914955496788024902e-01 7.029142379760742188e-01 4.892118275165557861e-01 1.000000000000000000e+00 -9.913725256919860840e-01 6.972548961639404297e-01 4.839215576648712158e-01 1.000000000000000000e+00 -9.912495017051696777e-01 6.915955543518066406e-01 4.786312878131866455e-01 1.000000000000000000e+00 -9.911264777183532715e-01 6.859361529350280762e-01 4.733410179615020752e-01 1.000000000000000000e+00 -9.910034537315368652e-01 6.802768111228942871e-01 4.680507481098175049e-01 1.000000000000000000e+00 -9.908804297447204590e-01 6.746174693107604980e-01 4.627604782581329346e-01 1.000000000000000000e+00 -9.907574057579040527e-01 6.689580678939819336e-01 4.574702084064483643e-01 1.000000000000000000e+00 -9.906343817710876465e-01 6.632987260818481445e-01 4.521799385547637939e-01 1.000000000000000000e+00 -9.905113577842712402e-01 6.576393842697143555e-01 4.468896687030792236e-01 1.000000000000000000e+00 -9.903883337974548340e-01 6.519799828529357910e-01 4.415993988513946533e-01 1.000000000000000000e+00 -9.902653098106384277e-01 6.463206410408020020e-01 4.363090991973876953e-01 1.000000000000000000e+00 -9.901422262191772461e-01 6.406612992286682129e-01 4.310188293457031250e-01 1.000000000000000000e+00 -9.900192022323608398e-01 6.350018978118896484e-01 4.257285594940185547e-01 1.000000000000000000e+00 -9.898961782455444336e-01 6.293425559997558594e-01 4.204382896423339844e-01 1.000000000000000000e+00 -9.897731542587280273e-01 6.236832141876220703e-01 4.151480197906494141e-01 1.000000000000000000e+00 -9.896501302719116211e-01 6.180238127708435059e-01 4.098577499389648438e-01 1.000000000000000000e+00 -9.895271062850952148e-01 6.123644709587097168e-01 4.045674800872802734e-01 1.000000000000000000e+00 -9.894040822982788086e-01 6.067051291465759277e-01 3.992772102355957031e-01 1.000000000000000000e+00 -9.892810583114624023e-01 6.010457277297973633e-01 3.939869403839111328e-01 1.000000000000000000e+00 -9.891580343246459961e-01 5.953863859176635742e-01 3.886966407299041748e-01 1.000000000000000000e+00 -9.890350103378295898e-01 5.897270441055297852e-01 3.834063708782196045e-01 1.000000000000000000e+00 -9.889119863510131836e-01 5.840676426887512207e-01 3.781161010265350342e-01 1.000000000000000000e+00 -9.887889027595520020e-01 5.784083008766174316e-01 3.728258311748504639e-01 1.000000000000000000e+00 -9.886658787727355957e-01 5.727489590644836426e-01 3.675355613231658936e-01 1.000000000000000000e+00 -9.885428547859191895e-01 5.670895576477050781e-01 3.622452914714813232e-01 1.000000000000000000e+00 -9.884198307991027832e-01 5.614302158355712891e-01 3.569550216197967529e-01 1.000000000000000000e+00 -9.882968068122863770e-01 5.557708740234375000e-01 3.516647517681121826e-01 1.000000000000000000e+00 -9.874355792999267578e-01 5.504806041717529297e-01 3.479738533496856689e-01 1.000000000000000000e+00 -9.858362078666687012e-01 5.455594062805175781e-01 3.458823561668395996e-01 1.000000000000000000e+00 -9.842368364334106445e-01 5.406382083892822266e-01 3.437908589839935303e-01 1.000000000000000000e+00 -9.826374650001525879e-01 5.357170104980468750e-01 3.416993319988250732e-01 1.000000000000000000e+00 -9.810380339622497559e-01 5.307958722114562988e-01 3.396078348159790039e-01 1.000000000000000000e+00 -9.794386625289916992e-01 5.258746743202209473e-01 3.375163376331329346e-01 1.000000000000000000e+00 -9.778392910957336426e-01 5.209534764289855957e-01 3.354248404502868652e-01 1.000000000000000000e+00 -9.762399196624755859e-01 5.160322785377502441e-01 3.333333432674407959e-01 1.000000000000000000e+00 -9.746405482292175293e-01 5.111111402511596680e-01 3.312418162822723389e-01 1.000000000000000000e+00 -9.730411171913146973e-01 5.061899423599243164e-01 3.291503190994262695e-01 1.000000000000000000e+00 -9.714417457580566406e-01 5.012687444686889648e-01 3.270588219165802002e-01 1.000000000000000000e+00 -9.698423743247985840e-01 4.963475465774536133e-01 3.249673247337341309e-01 1.000000000000000000e+00 -9.682430028915405273e-01 4.914263784885406494e-01 3.228758275508880615e-01 1.000000000000000000e+00 -9.666435718536376953e-01 4.865051805973052979e-01 3.207843005657196045e-01 1.000000000000000000e+00 -9.650442004203796387e-01 4.815840125083923340e-01 3.186928033828735352e-01 1.000000000000000000e+00 -9.634448289871215820e-01 4.766628146171569824e-01 3.166013062000274658e-01 1.000000000000000000e+00 -9.618454575538635254e-01 4.717416465282440186e-01 3.145098090171813965e-01 1.000000000000000000e+00 -9.602460861206054688e-01 4.668204486370086670e-01 3.124183118343353271e-01 1.000000000000000000e+00 -9.586466550827026367e-01 4.618992805480957031e-01 3.103267848491668701e-01 1.000000000000000000e+00 -9.570472836494445801e-01 4.569780826568603516e-01 3.082352876663208008e-01 1.000000000000000000e+00 -9.554479122161865234e-01 4.520569145679473877e-01 3.061437904834747314e-01 1.000000000000000000e+00 -9.538485407829284668e-01 4.471357166767120361e-01 3.040522933006286621e-01 1.000000000000000000e+00 -9.522491097450256348e-01 4.422145187854766846e-01 3.019607961177825928e-01 1.000000000000000000e+00 -9.506497383117675781e-01 4.372933506965637207e-01 2.998692691326141357e-01 1.000000000000000000e+00 -9.490503668785095215e-01 4.323721528053283691e-01 2.977777719497680664e-01 1.000000000000000000e+00 -9.474509954452514648e-01 4.274509847164154053e-01 2.956862747669219971e-01 1.000000000000000000e+00 -9.458516240119934082e-01 4.225297868251800537e-01 2.935947775840759277e-01 1.000000000000000000e+00 -9.442521929740905762e-01 4.176086187362670898e-01 2.915032804012298584e-01 1.000000000000000000e+00 -9.426528215408325195e-01 4.126874208450317383e-01 2.894117534160614014e-01 1.000000000000000000e+00 -9.410534501075744629e-01 4.077662527561187744e-01 2.873202562332153320e-01 1.000000000000000000e+00 -9.394540786743164062e-01 4.028450548648834229e-01 2.852287590503692627e-01 1.000000000000000000e+00 -9.378546476364135742e-01 3.979238867759704590e-01 2.831372618675231934e-01 1.000000000000000000e+00 -9.354094862937927246e-01 3.920030891895294189e-01 2.792003154754638672e-01 1.000000000000000000e+00 -9.324567317962646484e-01 3.854825198650360107e-01 2.741560935974121094e-01 1.000000000000000000e+00 -9.295040369033813477e-01 3.789619505405426025e-01 2.691118717193603516e-01 1.000000000000000000e+00 -9.265513420104980469e-01 3.724413812160491943e-01 2.640676796436309814e-01 1.000000000000000000e+00 -9.235985875129699707e-01 3.659208118915557861e-01 2.590234577655792236e-01 1.000000000000000000e+00 -9.206458926200866699e-01 3.594002425670623779e-01 2.539792358875274658e-01 1.000000000000000000e+00 -9.176931977272033691e-01 3.528796732425689697e-01 2.489350289106369019e-01 1.000000000000000000e+00 -9.147405028343200684e-01 3.463591039180755615e-01 2.438908070325851440e-01 1.000000000000000000e+00 -9.117877483367919922e-01 3.398385345935821533e-01 2.388466000556945801e-01 1.000000000000000000e+00 -9.088350534439086914e-01 3.333179652690887451e-01 2.338023781776428223e-01 1.000000000000000000e+00 -9.058823585510253906e-01 3.267973959445953369e-01 2.287581712007522583e-01 1.000000000000000000e+00 -9.029296636581420898e-01 3.202768266201019287e-01 2.237139493227005005e-01 1.000000000000000000e+00 -8.999769091606140137e-01 3.137562572956085205e-01 2.186697423458099365e-01 1.000000000000000000e+00 -8.970242142677307129e-01 3.072356879711151123e-01 2.136255353689193726e-01 1.000000000000000000e+00 -8.940715193748474121e-01 3.007151186466217041e-01 2.085813134908676147e-01 1.000000000000000000e+00 -8.911188244819641113e-01 2.941945493221282959e-01 2.035371065139770508e-01 1.000000000000000000e+00 -8.881660699844360352e-01 2.876739799976348877e-01 1.984928846359252930e-01 1.000000000000000000e+00 -8.852133750915527344e-01 2.811534106731414795e-01 1.934486776590347290e-01 1.000000000000000000e+00 -8.822606801986694336e-01 2.746328413486480713e-01 1.884044557809829712e-01 1.000000000000000000e+00 -8.793079853057861328e-01 2.681122720241546631e-01 1.833602488040924072e-01 1.000000000000000000e+00 -8.763552308082580566e-01 2.615917026996612549e-01 1.783160269260406494e-01 1.000000000000000000e+00 -8.734025359153747559e-01 2.550711333751678467e-01 1.732718199491500854e-01 1.000000000000000000e+00 -8.704498410224914551e-01 2.485505640506744385e-01 1.682275980710983276e-01 1.000000000000000000e+00 -8.674971461296081543e-01 2.420299947261810303e-01 1.631833910942077637e-01 1.000000000000000000e+00 -8.645443916320800781e-01 2.355094254016876221e-01 1.581391841173171997e-01 1.000000000000000000e+00 -8.615916967391967773e-01 2.289888560771942139e-01 1.530949622392654419e-01 1.000000000000000000e+00 -8.586390018463134766e-01 2.224682867527008057e-01 1.480507552623748779e-01 1.000000000000000000e+00 -8.556862473487854004e-01 2.159477174282073975e-01 1.430065333843231201e-01 1.000000000000000000e+00 -8.527335524559020996e-01 2.094271481037139893e-01 1.379623264074325562e-01 1.000000000000000000e+00 -8.497808575630187988e-01 2.029065787792205811e-01 1.329181045293807983e-01 1.000000000000000000e+00 -8.468281626701354980e-01 1.963860094547271729e-01 1.278738975524902344e-01 1.000000000000000000e+00 -8.438754081726074219e-01 1.898654401302337646e-01 1.228296831250190735e-01 1.000000000000000000e+00 -8.398154377937316895e-01 1.838062256574630737e-01 1.187081858515739441e-01 1.000000000000000000e+00 -8.353863954544067383e-01 1.779008060693740845e-01 1.148942708969116211e-01 1.000000000000000000e+00 -8.309573531150817871e-01 1.719953864812850952e-01 1.110803559422492981e-01 1.000000000000000000e+00 -8.265282511711120605e-01 1.660899668931961060e-01 1.072664335370063782e-01 1.000000000000000000e+00 -8.220992088317871094e-01 1.601845473051071167e-01 1.034525185823440552e-01 1.000000000000000000e+00 -8.176701068878173828e-01 1.542791277170181274e-01 9.963860362768173218e-02 1.000000000000000000e+00 -8.132410645484924316e-01 1.483737081289291382e-01 9.582468122243881226e-02 1.000000000000000000e+00 -8.088120222091674805e-01 1.424682885408401489e-01 9.201076626777648926e-02 1.000000000000000000e+00 -8.043829202651977539e-01 1.365628540515899658e-01 8.819684386253356934e-02 1.000000000000000000e+00 -7.999538779258728027e-01 1.306574344635009766e-01 8.438292890787124634e-02 1.000000000000000000e+00 -7.955247759819030762e-01 1.247520148754119873e-01 8.056901395320892334e-02 1.000000000000000000e+00 -7.910957336425781250e-01 1.188465952873229980e-01 7.675509154796600342e-02 1.000000000000000000e+00 -7.866666913032531738e-01 1.129411756992340088e-01 7.294117659330368042e-02 1.000000000000000000e+00 -7.822375893592834473e-01 1.070357561111450195e-01 6.912726163864135742e-02 1.000000000000000000e+00 -7.778085470199584961e-01 1.011303365230560303e-01 6.531333923339843750e-02 1.000000000000000000e+00 -7.733794450759887695e-01 9.522491693496704102e-02 6.149942427873611450e-02 1.000000000000000000e+00 -7.689504027366638184e-01 8.931948989629745483e-02 5.768550559878349304e-02 1.000000000000000000e+00 -7.645213603973388672e-01 8.341407030820846558e-02 5.387158691883087158e-02 1.000000000000000000e+00 -7.600922584533691406e-01 7.750865072011947632e-02 5.005767196416854858e-02 1.000000000000000000e+00 -7.556632161140441895e-01 7.160323113203048706e-02 4.624375328421592712e-02 1.000000000000000000e+00 -7.512341141700744629e-01 6.569781154394149780e-02 4.242983460426330566e-02 1.000000000000000000e+00 -7.468050718307495117e-01 5.979238823056221008e-02 3.861591592431068420e-02 1.000000000000000000e+00 -7.423760294914245605e-01 5.388696491718292236e-02 3.480200096964836121e-02 1.000000000000000000e+00 -7.379469275474548340e-01 4.798154532909393311e-02 3.098808228969573975e-02 1.000000000000000000e+00 -7.335178852081298828e-01 4.207612574100494385e-02 2.717416360974311829e-02 1.000000000000000000e+00 -7.290887832641601562e-01 3.617070242762565613e-02 2.336024679243564606e-02 1.000000000000000000e+00 -7.246597409248352051e-01 3.026528283953666687e-02 1.954632811248302460e-02 1.000000000000000000e+00 -7.202306985855102539e-01 2.435986138880252838e-02 1.573241129517555237e-02 1.000000000000000000e+00 -7.158015966415405273e-01 1.845443993806838989e-02 1.191849261522293091e-02 1.000000000000000000e+00 -7.113725543022155762e-01 1.254901941865682602e-02 8.104574866592884064e-03 1.000000000000000000e+00 -7.069435119628906250e-01 6.643598433583974838e-03 4.290657583624124527e-03 1.000000000000000000e+00 -7.025144100189208984e-01 7.381776231341063976e-04 4.767397185787558556e-04 1.000000000000000000e+00 -6.963629126548767090e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.899654269218444824e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.835678815841674805e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.771703362464904785e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.707727909088134766e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.643752455711364746e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.579777002334594727e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.515801548957824707e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.451826095581054688e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.387850642204284668e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.323875188827514648e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.259900331497192383e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.195924878120422363e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.131949424743652344e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.067973971366882324e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.003998517990112305e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.940023064613342285e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.876047611236572266e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.812072157859802246e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.748096704483032227e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.684121251106262207e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.620146393775939941e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.556170940399169922e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.492195487022399902e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.428220033645629883e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.364244580268859863e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.300269126892089844e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.236293673515319824e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.172318220138549805e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.108342766761779785e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.044367313385009766e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.980392158031463623e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/Oranges b/fastplotlib/utils/colormaps/Oranges deleted file mode 100644 index 26534a14b..000000000 --- a/fastplotlib/utils/colormaps/Oranges +++ /dev/null @@ -1,256 +0,0 @@ -1.000000000000000000e+00 9.607843160629272461e-01 9.215686321258544922e-01 1.000000000000000000e+00 -9.998769760131835938e-01 9.589388966560363770e-01 9.180007576942443848e-01 1.000000000000000000e+00 -9.997539520263671875e-01 9.570934176445007324e-01 9.144328832626342773e-01 1.000000000000000000e+00 -9.996309280395507812e-01 9.552479982376098633e-01 9.108650684356689453e-01 1.000000000000000000e+00 -9.995079040527343750e-01 9.534025192260742188e-01 9.072971940040588379e-01 1.000000000000000000e+00 -9.993848800659179688e-01 9.515570998191833496e-01 9.037293195724487305e-01 1.000000000000000000e+00 -9.992617964744567871e-01 9.497116208076477051e-01 9.001615047454833984e-01 1.000000000000000000e+00 -9.991387724876403809e-01 9.478662014007568359e-01 8.965936303138732910e-01 1.000000000000000000e+00 -9.990157485008239746e-01 9.460207819938659668e-01 8.930257558822631836e-01 1.000000000000000000e+00 -9.988927245140075684e-01 9.441753029823303223e-01 8.894578814506530762e-01 1.000000000000000000e+00 -9.987697005271911621e-01 9.423298835754394531e-01 8.858900666236877441e-01 1.000000000000000000e+00 -9.986466765403747559e-01 9.404844045639038086e-01 8.823221921920776367e-01 1.000000000000000000e+00 -9.985236525535583496e-01 9.386389851570129395e-01 8.787543177604675293e-01 1.000000000000000000e+00 -9.984006285667419434e-01 9.367935657501220703e-01 8.751864433288574219e-01 1.000000000000000000e+00 -9.982776045799255371e-01 9.349480867385864258e-01 8.716186285018920898e-01 1.000000000000000000e+00 -9.981545805931091309e-01 9.331026673316955566e-01 8.680507540702819824e-01 1.000000000000000000e+00 -9.980314970016479492e-01 9.312571883201599121e-01 8.644828796386718750e-01 1.000000000000000000e+00 -9.979084730148315430e-01 9.294117689132690430e-01 8.609150052070617676e-01 1.000000000000000000e+00 -9.977854490280151367e-01 9.275663495063781738e-01 8.573471903800964355e-01 1.000000000000000000e+00 -9.976624250411987305e-01 9.257208704948425293e-01 8.537793159484863281e-01 1.000000000000000000e+00 -9.975394010543823242e-01 9.238754510879516602e-01 8.502114415168762207e-01 1.000000000000000000e+00 -9.974163770675659180e-01 9.220299720764160156e-01 8.466436266899108887e-01 1.000000000000000000e+00 -9.972933530807495117e-01 9.201845526695251465e-01 8.430757522583007812e-01 1.000000000000000000e+00 -9.971703290939331055e-01 9.183390736579895020e-01 8.395078778266906738e-01 1.000000000000000000e+00 -9.970473051071166992e-01 9.164936542510986328e-01 8.359400033950805664e-01 1.000000000000000000e+00 -9.969242811203002930e-01 9.146482348442077637e-01 8.323721885681152344e-01 1.000000000000000000e+00 -9.968012571334838867e-01 9.128027558326721191e-01 8.288043141365051270e-01 1.000000000000000000e+00 -9.966781735420227051e-01 9.109573364257812500e-01 8.252364397048950195e-01 1.000000000000000000e+00 -9.965551495552062988e-01 9.091118574142456055e-01 8.216685652732849121e-01 1.000000000000000000e+00 -9.964321255683898926e-01 9.072664380073547363e-01 8.181007504463195801e-01 1.000000000000000000e+00 -9.963091015815734863e-01 9.054210186004638672e-01 8.145328760147094727e-01 1.000000000000000000e+00 -9.961860775947570801e-01 9.035755395889282227e-01 8.109650015830993652e-01 1.000000000000000000e+00 -9.960630536079406738e-01 9.016224741935729980e-01 8.071664571762084961e-01 1.000000000000000000e+00 -9.959400296211242676e-01 8.989158272743225098e-01 8.017531633377075195e-01 1.000000000000000000e+00 -9.958170056343078613e-01 8.962091207504272461e-01 7.963398694992065430e-01 1.000000000000000000e+00 -9.956939816474914551e-01 8.935024738311767578e-01 7.909265756607055664e-01 1.000000000000000000e+00 -9.955709576606750488e-01 8.907958269119262695e-01 7.855132818222045898e-01 1.000000000000000000e+00 -9.954479336738586426e-01 8.880891799926757812e-01 7.800999879837036133e-01 1.000000000000000000e+00 -9.953248500823974609e-01 8.853825330734252930e-01 7.746866345405578613e-01 1.000000000000000000e+00 -9.952018260955810547e-01 8.826758861541748047e-01 7.692733407020568848e-01 1.000000000000000000e+00 -9.950788021087646484e-01 8.799692392349243164e-01 7.638600468635559082e-01 1.000000000000000000e+00 -9.949557781219482422e-01 8.772625923156738281e-01 7.584467530250549316e-01 1.000000000000000000e+00 -9.948327541351318359e-01 8.745559453964233398e-01 7.530334591865539551e-01 1.000000000000000000e+00 -9.947097301483154297e-01 8.718492984771728516e-01 7.476201653480529785e-01 1.000000000000000000e+00 -9.945867061614990234e-01 8.691426515579223633e-01 7.422068715095520020e-01 1.000000000000000000e+00 -9.944636821746826172e-01 8.664360046386718750e-01 7.367935180664062500e-01 1.000000000000000000e+00 -9.943406581878662109e-01 8.637293577194213867e-01 7.313802242279052734e-01 1.000000000000000000e+00 -9.942176342010498047e-01 8.610227108001708984e-01 7.259669303894042969e-01 1.000000000000000000e+00 -9.940945506095886230e-01 8.583160042762756348e-01 7.205536365509033203e-01 1.000000000000000000e+00 -9.939715266227722168e-01 8.556093573570251465e-01 7.151403427124023438e-01 1.000000000000000000e+00 -9.938485026359558105e-01 8.529027104377746582e-01 7.097270488739013672e-01 1.000000000000000000e+00 -9.937254786491394043e-01 8.501960635185241699e-01 7.043137550354003906e-01 1.000000000000000000e+00 -9.936024546623229980e-01 8.474894165992736816e-01 6.989004015922546387e-01 1.000000000000000000e+00 -9.934794306755065918e-01 8.447827696800231934e-01 6.934871077537536621e-01 1.000000000000000000e+00 -9.933564066886901855e-01 8.420761227607727051e-01 6.880738139152526855e-01 1.000000000000000000e+00 -9.932333827018737793e-01 8.393694758415222168e-01 6.826605200767517090e-01 1.000000000000000000e+00 -9.931103587150573730e-01 8.366628289222717285e-01 6.772472262382507324e-01 1.000000000000000000e+00 -9.929873347282409668e-01 8.339561820030212402e-01 6.718339323997497559e-01 1.000000000000000000e+00 -9.928643107414245605e-01 8.312495350837707520e-01 6.664205789566040039e-01 1.000000000000000000e+00 -9.927412271499633789e-01 8.285428881645202637e-01 6.610072851181030273e-01 1.000000000000000000e+00 -9.926182031631469727e-01 8.258362412452697754e-01 6.555939912796020508e-01 1.000000000000000000e+00 -9.924951791763305664e-01 8.231295943260192871e-01 6.501806974411010742e-01 1.000000000000000000e+00 -9.923721551895141602e-01 8.204228878021240234e-01 6.447674036026000977e-01 1.000000000000000000e+00 -9.922491312026977539e-01 8.177162408828735352e-01 6.393541097640991211e-01 1.000000000000000000e+00 -9.921568632125854492e-01 8.146405220031738281e-01 6.336024403572082520e-01 1.000000000000000000e+00 -9.921568632125854492e-01 8.104575276374816895e-01 6.268358230590820312e-01 1.000000000000000000e+00 -9.921568632125854492e-01 8.062745332717895508e-01 6.200692057609558105e-01 1.000000000000000000e+00 -9.921568632125854492e-01 8.020914793014526367e-01 6.133025884628295898e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.979084849357604980e-01 6.065359711647033691e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.937254905700683594e-01 5.997692942619323730e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.895424962043762207e-01 5.930026769638061523e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.853595018386840820e-01 5.862360596656799316e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.811764478683471680e-01 5.794694423675537109e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.769934535026550293e-01 5.727028250694274902e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.728104591369628906e-01 5.659362077713012695e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.686274647712707520e-01 5.591695308685302734e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.644444704055786133e-01 5.524029135704040527e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.602614164352416992e-01 5.456362962722778320e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.560784220695495605e-01 5.388696789741516113e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.518954277038574219e-01 5.321030616760253906e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.477124333381652832e-01 5.253363847732543945e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.435294389724731445e-01 5.185697674751281738e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.393463850021362305e-01 5.118031501770019531e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.351633906364440918e-01 5.050365328788757324e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.309803962707519531e-01 4.982698857784271240e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.267974019050598145e-01 4.915032684803009033e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.226144075393676758e-01 4.847366511821746826e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.184313535690307617e-01 4.779700040817260742e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.142483592033386230e-01 4.712033867835998535e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.100653648376464844e-01 4.644367694854736328e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.058823704719543457e-01 4.576701223850250244e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.016993761062622070e-01 4.509035050868988037e-01 1.000000000000000000e+00 -9.921568632125854492e-01 6.975163221359252930e-01 4.441368579864501953e-01 1.000000000000000000e+00 -9.921568632125854492e-01 6.933333277702331543e-01 4.373702406883239746e-01 1.000000000000000000e+00 -9.921568632125854492e-01 6.891503334045410156e-01 4.306036233901977539e-01 1.000000000000000000e+00 -9.921568632125854492e-01 6.849673390388488770e-01 4.238369762897491455e-01 1.000000000000000000e+00 -9.921568632125854492e-01 6.808304786682128906e-01 4.174394607543945312e-01 1.000000000000000000e+00 -9.921568632125854492e-01 6.767704486846923828e-01 4.116570651531219482e-01 1.000000000000000000e+00 -9.921568632125854492e-01 6.727104783058166504e-01 4.058746695518493652e-01 1.000000000000000000e+00 -9.921568632125854492e-01 6.686505079269409180e-01 4.000922739505767822e-01 1.000000000000000000e+00 -9.921568632125854492e-01 6.645905375480651855e-01 3.943098783493041992e-01 1.000000000000000000e+00 -9.921568632125854492e-01 6.605305671691894531e-01 3.885274827480316162e-01 1.000000000000000000e+00 -9.921568632125854492e-01 6.564705967903137207e-01 3.827450871467590332e-01 1.000000000000000000e+00 -9.921568632125854492e-01 6.524106264114379883e-01 3.769627213478088379e-01 1.000000000000000000e+00 -9.921568632125854492e-01 6.483506560325622559e-01 3.711803257465362549e-01 1.000000000000000000e+00 -9.921568632125854492e-01 6.442906856536865234e-01 3.653979301452636719e-01 1.000000000000000000e+00 -9.921568632125854492e-01 6.402306556701660156e-01 3.596155345439910889e-01 1.000000000000000000e+00 -9.921568632125854492e-01 6.361706852912902832e-01 3.538331389427185059e-01 1.000000000000000000e+00 -9.921568632125854492e-01 6.321107149124145508e-01 3.480507433414459229e-01 1.000000000000000000e+00 -9.921568632125854492e-01 6.280507445335388184e-01 3.422683477401733398e-01 1.000000000000000000e+00 -9.921568632125854492e-01 6.239907741546630859e-01 3.364859521389007568e-01 1.000000000000000000e+00 -9.921568632125854492e-01 6.199308037757873535e-01 3.307035863399505615e-01 1.000000000000000000e+00 -9.921568632125854492e-01 6.158708333969116211e-01 3.249211907386779785e-01 1.000000000000000000e+00 -9.921568632125854492e-01 6.118108630180358887e-01 3.191387951374053955e-01 1.000000000000000000e+00 -9.921568632125854492e-01 6.077508926391601562e-01 3.133563995361328125e-01 1.000000000000000000e+00 -9.921568632125854492e-01 6.036908626556396484e-01 3.075740039348602295e-01 1.000000000000000000e+00 -9.921568632125854492e-01 5.996308922767639160e-01 3.017916083335876465e-01 1.000000000000000000e+00 -9.921568632125854492e-01 5.955709218978881836e-01 2.960092127323150635e-01 1.000000000000000000e+00 -9.921568632125854492e-01 5.915109515190124512e-01 2.902268469333648682e-01 1.000000000000000000e+00 -9.921568632125854492e-01 5.874509811401367188e-01 2.844444513320922852e-01 1.000000000000000000e+00 -9.921568632125854492e-01 5.833910107612609863e-01 2.786620557308197021e-01 1.000000000000000000e+00 -9.921568632125854492e-01 5.793310403823852539e-01 2.728796601295471191e-01 1.000000000000000000e+00 -9.921568632125854492e-01 5.752710700035095215e-01 2.670972645282745361e-01 1.000000000000000000e+00 -9.921568632125854492e-01 5.712110996246337891e-01 2.613148689270019531e-01 1.000000000000000000e+00 -9.921568632125854492e-01 5.671510696411132812e-01 2.555324733257293701e-01 1.000000000000000000e+00 -9.921568632125854492e-01 5.630910992622375488e-01 2.497500926256179810e-01 1.000000000000000000e+00 -9.921568632125854492e-01 5.590311288833618164e-01 2.439677119255065918e-01 1.000000000000000000e+00 -9.921568632125854492e-01 5.549711585044860840e-01 2.381853163242340088e-01 1.000000000000000000e+00 -9.914186596870422363e-01 5.507266521453857422e-01 2.327720075845718384e-01 1.000000000000000000e+00 -9.899423122406005859e-01 5.462975502014160156e-01 2.277278006076812744e-01 1.000000000000000000e+00 -9.884659647941589355e-01 5.418685078620910645e-01 2.226835787296295166e-01 1.000000000000000000e+00 -9.869896173477172852e-01 5.374394655227661133e-01 2.176393717527389526e-01 1.000000000000000000e+00 -9.855132699012756348e-01 5.330103635787963867e-01 2.125951498746871948e-01 1.000000000000000000e+00 -9.840369224548339844e-01 5.285813212394714355e-01 2.075509428977966309e-01 1.000000000000000000e+00 -9.825605750083923340e-01 5.241522789001464844e-01 2.025067210197448730e-01 1.000000000000000000e+00 -9.810842275619506836e-01 5.197231769561767578e-01 1.974625140428543091e-01 1.000000000000000000e+00 -9.796078205108642578e-01 5.152941346168518066e-01 1.924183070659637451e-01 1.000000000000000000e+00 -9.781314730644226074e-01 5.108650326728820801e-01 1.873740851879119873e-01 1.000000000000000000e+00 -9.766551256179809570e-01 5.064359903335571289e-01 1.823298782110214233e-01 1.000000000000000000e+00 -9.751787781715393066e-01 5.020069479942321777e-01 1.772856563329696655e-01 1.000000000000000000e+00 -9.737024307250976562e-01 4.975778460502624512e-01 1.722414493560791016e-01 1.000000000000000000e+00 -9.722260832786560059e-01 4.931488037109375000e-01 1.671972274780273438e-01 1.000000000000000000e+00 -9.707497358322143555e-01 4.887197315692901611e-01 1.621530205011367798e-01 1.000000000000000000e+00 -9.692733287811279297e-01 4.842906594276428223e-01 1.571087986230850220e-01 1.000000000000000000e+00 -9.677969813346862793e-01 4.798615872859954834e-01 1.520645916461944580e-01 1.000000000000000000e+00 -9.663206338882446289e-01 4.754325151443481445e-01 1.470203697681427002e-01 1.000000000000000000e+00 -9.648442864418029785e-01 4.710034728050231934e-01 1.419761627912521362e-01 1.000000000000000000e+00 -9.633679389953613281e-01 4.665744006633758545e-01 1.369319558143615723e-01 1.000000000000000000e+00 -9.618915915489196777e-01 4.621453285217285156e-01 1.318877339363098145e-01 1.000000000000000000e+00 -9.604152441024780273e-01 4.577162563800811768e-01 1.268435269594192505e-01 1.000000000000000000e+00 -9.589388966560363770e-01 4.532871842384338379e-01 1.217993050813674927e-01 1.000000000000000000e+00 -9.574624896049499512e-01 4.488581418991088867e-01 1.167550906538963318e-01 1.000000000000000000e+00 -9.559861421585083008e-01 4.444290697574615479e-01 1.117108836770057678e-01 1.000000000000000000e+00 -9.545097947120666504e-01 4.399999976158142090e-01 1.066666692495346069e-01 1.000000000000000000e+00 -9.530334472656250000e-01 4.355709254741668701e-01 1.016224548220634460e-01 1.000000000000000000e+00 -9.515570998191833496e-01 4.311418831348419189e-01 9.657824039459228516e-02 1.000000000000000000e+00 -9.500807523727416992e-01 4.267128109931945801e-01 9.153402596712112427e-02 1.000000000000000000e+00 -9.486044049263000488e-01 4.222837388515472412e-01 8.648981153964996338e-02 1.000000000000000000e+00 -9.471280574798583984e-01 4.178546667098999023e-01 8.144559711217880249e-02 1.000000000000000000e+00 -9.456516504287719727e-01 4.134255945682525635e-01 7.640138268470764160e-02 1.000000000000000000e+00 -9.432526230812072754e-01 4.092272222042083740e-01 7.312572002410888672e-02 1.000000000000000000e+00 -9.402998685836791992e-01 4.051672518253326416e-01 7.091119140386581421e-02 1.000000000000000000e+00 -9.373471736907958984e-01 4.011072516441345215e-01 6.869665533304214478e-02 1.000000000000000000e+00 -9.343944787979125977e-01 3.970472812652587891e-01 6.648211926221847534e-02 1.000000000000000000e+00 -9.314417243003845215e-01 3.929873108863830566e-01 6.426759064197540283e-02 1.000000000000000000e+00 -9.284890294075012207e-01 3.889273405075073242e-01 6.205305829644203186e-02 1.000000000000000000e+00 -9.255363345146179199e-01 3.848673701286315918e-01 5.983852222561836243e-02 1.000000000000000000e+00 -9.225836396217346191e-01 3.808073699474334717e-01 5.762398988008499146e-02 1.000000000000000000e+00 -9.196308851242065430e-01 3.767473995685577393e-01 5.540945753455162048e-02 1.000000000000000000e+00 -9.166781902313232422e-01 3.726874291896820068e-01 5.319492518901824951e-02 1.000000000000000000e+00 -9.137254953384399414e-01 3.686274588108062744e-01 5.098039284348487854e-02 1.000000000000000000e+00 -9.107728004455566406e-01 3.645674884319305420e-01 4.876586049795150757e-02 1.000000000000000000e+00 -9.078200459480285645e-01 3.605074882507324219e-01 4.655132815241813660e-02 1.000000000000000000e+00 -9.048673510551452637e-01 3.564475178718566895e-01 4.433679208159446716e-02 1.000000000000000000e+00 -9.019146561622619629e-01 3.523875474929809570e-01 4.212225973606109619e-02 1.000000000000000000e+00 -8.989619612693786621e-01 3.483275771141052246e-01 3.990772739052772522e-02 1.000000000000000000e+00 -8.960092067718505859e-01 3.442675769329071045e-01 3.769319504499435425e-02 1.000000000000000000e+00 -8.930565118789672852e-01 3.402076065540313721e-01 3.547866269946098328e-02 1.000000000000000000e+00 -8.901038169860839844e-01 3.361476361751556396e-01 3.326413035392761230e-02 1.000000000000000000e+00 -8.871511220932006836e-01 3.320876657962799072e-01 3.104959614574909210e-02 1.000000000000000000e+00 -8.841983675956726074e-01 3.280276954174041748e-01 2.883506380021572113e-02 1.000000000000000000e+00 -8.812456727027893066e-01 3.239676952362060547e-01 2.662053145468235016e-02 1.000000000000000000e+00 -8.782929778099060059e-01 3.199077248573303223e-01 2.440599724650382996e-02 1.000000000000000000e+00 -8.753402829170227051e-01 3.158477544784545898e-01 2.219146490097045898e-02 1.000000000000000000e+00 -8.723875284194946289e-01 3.117877840995788574e-01 1.997693255543708801e-02 1.000000000000000000e+00 -8.694348335266113281e-01 3.077277839183807373e-01 1.776239834725856781e-02 1.000000000000000000e+00 -8.664821386337280273e-01 3.036678135395050049e-01 1.554786600172519684e-02 1.000000000000000000e+00 -8.635293841361999512e-01 2.996078431606292725e-01 1.333333365619182587e-02 1.000000000000000000e+00 -8.605766892433166504e-01 2.955478727817535400e-01 1.111880037933588028e-02 1.000000000000000000e+00 -8.576239943504333496e-01 2.914879024028778076e-01 8.904268033802509308e-03 1.000000000000000000e+00 -8.546712994575500488e-01 2.874279022216796875e-01 6.689734756946563721e-03 1.000000000000000000e+00 -8.517185449600219727e-01 2.833679318428039551e-01 4.475201945751905441e-03 1.000000000000000000e+00 -8.462744951248168945e-01 2.806920409202575684e-01 4.106113221496343613e-03 1.000000000000000000e+00 -8.399999737739562988e-01 2.784775197505950928e-01 4.352172371000051498e-03 1.000000000000000000e+00 -8.337255120277404785e-01 2.762629687786102295e-01 4.598231520503759384e-03 1.000000000000000000e+00 -8.274509906768798828e-01 2.740484476089477539e-01 4.844290670007467270e-03 1.000000000000000000e+00 -8.211764693260192871e-01 2.718338966369628906e-01 5.090349819511175156e-03 1.000000000000000000e+00 -8.149019479751586914e-01 2.696193754673004150e-01 5.336408969014883041e-03 1.000000000000000000e+00 -8.086274266242980957e-01 2.674048542976379395e-01 5.582468118518590927e-03 1.000000000000000000e+00 -8.023529648780822754e-01 2.651903033256530762e-01 5.828527268022298813e-03 1.000000000000000000e+00 -7.960784435272216797e-01 2.629757821559906006e-01 6.074586883187294006e-03 1.000000000000000000e+00 -7.898039221763610840e-01 2.607612311840057373e-01 6.320646032691001892e-03 1.000000000000000000e+00 -7.835294008255004883e-01 2.585467100143432617e-01 6.566705182194709778e-03 1.000000000000000000e+00 -7.772548794746398926e-01 2.563321888446807861e-01 6.812764331698417664e-03 1.000000000000000000e+00 -7.709804177284240723e-01 2.541176378726959229e-01 7.058823481202125549e-03 1.000000000000000000e+00 -7.647058963775634766e-01 2.519031167030334473e-01 7.304882630705833435e-03 1.000000000000000000e+00 -7.584313750267028809e-01 2.496885806322097778e-01 7.550941780209541321e-03 1.000000000000000000e+00 -7.521568536758422852e-01 2.474740445613861084e-01 7.797000929713249207e-03 1.000000000000000000e+00 -7.458823323249816895e-01 2.452595084905624390e-01 8.043060079216957092e-03 1.000000000000000000e+00 -7.396078705787658691e-01 2.430449873208999634e-01 8.289119228720664978e-03 1.000000000000000000e+00 -7.333333492279052734e-01 2.408304512500762939e-01 8.535178378224372864e-03 1.000000000000000000e+00 -7.270588278770446777e-01 2.386159151792526245e-01 8.781237527728080750e-03 1.000000000000000000e+00 -7.207843065261840820e-01 2.364013791084289551e-01 9.027297608554363251e-03 1.000000000000000000e+00 -7.145097851753234863e-01 2.341868579387664795e-01 9.273356758058071136e-03 1.000000000000000000e+00 -7.082353234291076660e-01 2.319723218679428101e-01 9.519415907561779022e-03 1.000000000000000000e+00 -7.019608020782470703e-01 2.297577857971191406e-01 9.765475057065486908e-03 1.000000000000000000e+00 -6.956862807273864746e-01 2.275432497262954712e-01 1.001153420656919479e-02 1.000000000000000000e+00 -6.894117593765258789e-01 2.253287136554718018e-01 1.025759335607290268e-02 1.000000000000000000e+00 -6.831372380256652832e-01 2.231141924858093262e-01 1.050365250557661057e-02 1.000000000000000000e+00 -6.768627166748046875e-01 2.208996564149856567e-01 1.074971165508031845e-02 1.000000000000000000e+00 -6.705882549285888672e-01 2.186851203441619873e-01 1.099577080458402634e-02 1.000000000000000000e+00 -6.643137335777282715e-01 2.164705842733383179e-01 1.124182995408773422e-02 1.000000000000000000e+00 -6.580392122268676758e-01 2.142560482025146484e-01 1.148788910359144211e-02 1.000000000000000000e+00 -6.517646908760070801e-01 2.120415270328521729e-01 1.173394825309514999e-02 1.000000000000000000e+00 -6.467819809913635254e-01 2.101499438285827637e-01 1.187235675752162933e-02 1.000000000000000000e+00 -6.419838666915893555e-01 2.083044946193695068e-01 1.199538633227348328e-02 1.000000000000000000e+00 -6.371856927871704102e-01 2.064590603113174438e-01 1.211841590702533722e-02 1.000000000000000000e+00 -6.323875188827514648e-01 2.046136111021041870e-01 1.224144548177719116e-02 1.000000000000000000e+00 -6.275894045829772949e-01 2.027681618928909302e-01 1.236447505652904510e-02 1.000000000000000000e+00 -6.227912306785583496e-01 2.009227275848388672e-01 1.248750463128089905e-02 1.000000000000000000e+00 -6.179930567741394043e-01 1.990772783756256104e-01 1.261053420603275299e-02 1.000000000000000000e+00 -6.131949424743652344e-01 1.972318291664123535e-01 1.273356378078460693e-02 1.000000000000000000e+00 -6.083967685699462891e-01 1.953863948583602905e-01 1.285659335553646088e-02 1.000000000000000000e+00 -6.035985946655273438e-01 1.935409456491470337e-01 1.297962293028831482e-02 1.000000000000000000e+00 -5.988004803657531738e-01 1.916954964399337769e-01 1.310265250504016876e-02 1.000000000000000000e+00 -5.940023064613342285e-01 1.898500621318817139e-01 1.322568207979202271e-02 1.000000000000000000e+00 -5.892041325569152832e-01 1.880046129226684570e-01 1.334871165454387665e-02 1.000000000000000000e+00 -5.844060182571411133e-01 1.861591637134552002e-01 1.347174122929573059e-02 1.000000000000000000e+00 -5.796078443527221680e-01 1.843137294054031372e-01 1.359477080404758453e-02 1.000000000000000000e+00 -5.748096704483032227e-01 1.824682801961898804e-01 1.371780131012201309e-02 1.000000000000000000e+00 -5.700115561485290527e-01 1.806228309869766235e-01 1.384083088487386703e-02 1.000000000000000000e+00 -5.652133822441101074e-01 1.787773966789245605e-01 1.396386045962572098e-02 1.000000000000000000e+00 -5.604152083396911621e-01 1.769319474697113037e-01 1.408689003437757492e-02 1.000000000000000000e+00 -5.556170940399169922e-01 1.750864982604980469e-01 1.420991960912942886e-02 1.000000000000000000e+00 -5.508189201354980469e-01 1.732410639524459839e-01 1.433294918388128281e-02 1.000000000000000000e+00 -5.460207462310791016e-01 1.713956147432327271e-01 1.445597875863313675e-02 1.000000000000000000e+00 -5.412226319313049316e-01 1.695501804351806641e-01 1.457900833338499069e-02 1.000000000000000000e+00 -5.364244580268859863e-01 1.677047312259674072e-01 1.470203790813684464e-02 1.000000000000000000e+00 -5.316262841224670410e-01 1.658592820167541504e-01 1.482506748288869858e-02 1.000000000000000000e+00 -5.268281698226928711e-01 1.640138477087020874e-01 1.494809705764055252e-02 1.000000000000000000e+00 -5.220299959182739258e-01 1.621683984994888306e-01 1.507112663239240646e-02 1.000000000000000000e+00 -5.172318220138549805e-01 1.603229492902755737e-01 1.519415620714426041e-02 1.000000000000000000e+00 -5.124337077140808105e-01 1.584775149822235107e-01 1.531718578189611435e-02 1.000000000000000000e+00 -5.076355338096618652e-01 1.566320657730102539e-01 1.544021535664796829e-02 1.000000000000000000e+00 -5.028373599052429199e-01 1.547866165637969971e-01 1.556324493139982224e-02 1.000000000000000000e+00 -4.980392158031463623e-01 1.529411822557449341e-01 1.568627543747425079e-02 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/PRGn b/fastplotlib/utils/colormaps/PRGn deleted file mode 100644 index 33b818b35..000000000 --- a/fastplotlib/utils/colormaps/PRGn +++ /dev/null @@ -1,256 +0,0 @@ -2.509804069995880127e-01 0.000000000000000000e+00 2.941176593303680420e-01 1.000000000000000000e+00 -2.592848837375640869e-01 6.459054071456193924e-03 3.027297258377075195e-01 1.000000000000000000e+00 -2.675893902778625488e-01 1.291810814291238785e-02 3.113417923450469971e-01 1.000000000000000000e+00 -2.758938968181610107e-01 1.937716268002986908e-02 3.199538588523864746e-01 1.000000000000000000e+00 -2.841983735561370850e-01 2.583621628582477570e-02 3.285659253597259521e-01 1.000000000000000000e+00 -2.925028800964355469e-01 3.229527175426483154e-02 3.371780216693878174e-01 1.000000000000000000e+00 -3.008073866367340088e-01 3.875432536005973816e-02 3.457900881767272949e-01 1.000000000000000000e+00 -3.091118931770324707e-01 4.521337896585464478e-02 3.544021546840667725e-01 1.000000000000000000e+00 -3.174163699150085449e-01 5.167243257164955139e-02 3.630142211914062500e-01 1.000000000000000000e+00 -3.257208764553070068e-01 5.813148617744445801e-02 3.716262876987457275e-01 1.000000000000000000e+00 -3.340253829956054688e-01 6.459054350852966309e-02 3.802383840084075928e-01 1.000000000000000000e+00 -3.423298597335815430e-01 7.104959338903427124e-02 3.888504505157470703e-01 1.000000000000000000e+00 -3.506343662738800049e-01 7.750865072011947632e-02 3.974625170230865479e-01 1.000000000000000000e+00 -3.589388728141784668e-01 8.396770805120468140e-02 4.060745835304260254e-01 1.000000000000000000e+00 -3.672433793544769287e-01 9.042675793170928955e-02 4.146866500377655029e-01 1.000000000000000000e+00 -3.755478560924530029e-01 9.688581526279449463e-02 4.232987165451049805e-01 1.000000000000000000e+00 -3.838523626327514648e-01 1.033448651432991028e-01 4.319108128547668457e-01 1.000000000000000000e+00 -3.921568691730499268e-01 1.098039224743843079e-01 4.405228793621063232e-01 1.000000000000000000e+00 -4.004613757133483887e-01 1.162629723548889160e-01 4.491349458694458008e-01 1.000000000000000000e+00 -4.087658524513244629e-01 1.227220296859741211e-01 4.577470123767852783e-01 1.000000000000000000e+00 -4.170703589916229248e-01 1.291810870170593262e-01 4.663590788841247559e-01 1.000000000000000000e+00 -4.253748655319213867e-01 1.356401443481445312e-01 4.749711751937866211e-01 1.000000000000000000e+00 -4.336793422698974609e-01 1.420991867780685425e-01 4.835832417011260986e-01 1.000000000000000000e+00 -4.419838488101959229e-01 1.485582441091537476e-01 4.921953082084655762e-01 1.000000000000000000e+00 -4.502883553504943848e-01 1.550173014402389526e-01 5.008074045181274414e-01 1.000000000000000000e+00 -4.585928618907928467e-01 1.614763587713241577e-01 5.094194412231445312e-01 1.000000000000000000e+00 -4.654363691806793213e-01 1.700884252786636353e-01 5.168012380599975586e-01 1.000000000000000000e+00 -4.708189070224761963e-01 1.808535158634185791e-01 5.229527354240417480e-01 1.000000000000000000e+00 -4.762014746665954590e-01 1.916186064481735229e-01 5.291041731834411621e-01 1.000000000000000000e+00 -4.815840125083923340e-01 2.023836970329284668e-01 5.352556705474853516e-01 1.000000000000000000e+00 -4.869665503501892090e-01 2.131487876176834106e-01 5.414071679115295410e-01 1.000000000000000000e+00 -4.923490881919860840e-01 2.239138782024383545e-01 5.475586056709289551e-01 1.000000000000000000e+00 -4.977316558361053467e-01 2.346789687871932983e-01 5.537101030349731445e-01 1.000000000000000000e+00 -5.031141638755798340e-01 2.454440593719482422e-01 5.598616003990173340e-01 1.000000000000000000e+00 -5.084967613220214844e-01 2.562091648578643799e-01 5.660130977630615234e-01 1.000000000000000000e+00 -5.138792991638183594e-01 2.669742405414581299e-01 5.721645355224609375e-01 1.000000000000000000e+00 -5.192618370056152344e-01 2.777393162250518799e-01 5.783160328865051270e-01 1.000000000000000000e+00 -5.246443748474121094e-01 2.885044217109680176e-01 5.844675302505493164e-01 1.000000000000000000e+00 -5.300269126892089844e-01 2.992694973945617676e-01 5.906189680099487305e-01 1.000000000000000000e+00 -5.354094505310058594e-01 3.100346028804779053e-01 5.967704653739929199e-01 1.000000000000000000e+00 -5.407919883728027344e-01 3.207996785640716553e-01 6.029219627380371094e-01 1.000000000000000000e+00 -5.461745262145996094e-01 3.315647840499877930e-01 6.090734601020812988e-01 1.000000000000000000e+00 -5.515570640563964844e-01 3.423298597335815430e-01 6.152248978614807129e-01 1.000000000000000000e+00 -5.569396615028381348e-01 3.530949652194976807e-01 6.213763952255249023e-01 1.000000000000000000e+00 -5.623221993446350098e-01 3.638600409030914307e-01 6.275278925895690918e-01 1.000000000000000000e+00 -5.677047371864318848e-01 3.746251463890075684e-01 6.336793303489685059e-01 1.000000000000000000e+00 -5.730872750282287598e-01 3.853902220726013184e-01 6.398308277130126953e-01 1.000000000000000000e+00 -5.784698128700256348e-01 3.961553275585174561e-01 6.459823250770568848e-01 1.000000000000000000e+00 -5.838523507118225098e-01 4.069204032421112061e-01 6.521338224411010742e-01 1.000000000000000000e+00 -5.892348885536193848e-01 4.176855087280273438e-01 6.582852602005004883e-01 1.000000000000000000e+00 -5.946174263954162598e-01 4.284505844116210938e-01 6.644367575645446777e-01 1.000000000000000000e+00 -6.000000238418579102e-01 4.392156898975372314e-01 6.705882549285888672e-01 1.000000000000000000e+00 -6.063052415847778320e-01 4.473663866519927979e-01 6.761245727539062500e-01 1.000000000000000000e+00 -6.126105189323425293e-01 4.555171132087707520e-01 6.816608905792236328e-01 1.000000000000000000e+00 -6.189157962799072266e-01 4.636678099632263184e-01 6.871972084045410156e-01 1.000000000000000000e+00 -6.252210736274719238e-01 4.718185365200042725e-01 6.927335858345031738e-01 1.000000000000000000e+00 -6.315263509750366211e-01 4.799692332744598389e-01 6.982699036598205566e-01 1.000000000000000000e+00 -6.378316283226013184e-01 4.881199598312377930e-01 7.038062214851379395e-01 1.000000000000000000e+00 -6.441368460655212402e-01 4.962706565856933594e-01 7.093425393104553223e-01 1.000000000000000000e+00 -6.504421234130859375e-01 5.044213533401489258e-01 7.148789167404174805e-01 1.000000000000000000e+00 -6.567474007606506348e-01 5.125721096992492676e-01 7.204152345657348633e-01 1.000000000000000000e+00 -6.630526781082153320e-01 5.207228064537048340e-01 7.259515523910522461e-01 1.000000000000000000e+00 -6.693579554557800293e-01 5.288735032081604004e-01 7.314878702163696289e-01 1.000000000000000000e+00 -6.756632328033447266e-01 5.370241999626159668e-01 7.370242476463317871e-01 1.000000000000000000e+00 -6.819684505462646484e-01 5.451749563217163086e-01 7.425605654716491699e-01 1.000000000000000000e+00 -6.882737278938293457e-01 5.533256530761718750e-01 7.480968832969665527e-01 1.000000000000000000e+00 -6.945790052413940430e-01 5.614763498306274414e-01 7.536332011222839355e-01 1.000000000000000000e+00 -7.008842825889587402e-01 5.696270465850830078e-01 7.591695785522460938e-01 1.000000000000000000e+00 -7.071895599365234375e-01 5.777778029441833496e-01 7.647058963775634766e-01 1.000000000000000000e+00 -7.134948372840881348e-01 5.859284996986389160e-01 7.702422142028808594e-01 1.000000000000000000e+00 -7.198000550270080566e-01 5.940791964530944824e-01 7.757785320281982422e-01 1.000000000000000000e+00 -7.261053323745727539e-01 6.022298932075500488e-01 7.813148498535156250e-01 1.000000000000000000e+00 -7.324106097221374512e-01 6.103806495666503906e-01 7.868512272834777832e-01 1.000000000000000000e+00 -7.387158870697021484e-01 6.185313463211059570e-01 7.923875451087951660e-01 1.000000000000000000e+00 -7.450211644172668457e-01 6.266820430755615234e-01 7.979238629341125488e-01 1.000000000000000000e+00 -7.513264417648315430e-01 6.348327398300170898e-01 8.034601807594299316e-01 1.000000000000000000e+00 -7.576316595077514648e-01 6.429834961891174316e-01 8.089965581893920898e-01 1.000000000000000000e+00 -7.636293768882751465e-01 6.506727933883666992e-01 8.136870265007019043e-01 1.000000000000000000e+00 -7.693194746971130371e-01 6.579008102416992188e-01 8.175317049026489258e-01 1.000000000000000000e+00 -7.750096321105957031e-01 6.651287674903869629e-01 8.213763833045959473e-01 1.000000000000000000e+00 -7.806997299194335938e-01 6.723567843437194824e-01 8.252210617065429688e-01 1.000000000000000000e+00 -7.863898277282714844e-01 6.795848011970520020e-01 8.290657401084899902e-01 1.000000000000000000e+00 -7.920799851417541504e-01 6.868127584457397461e-01 8.329104185104370117e-01 1.000000000000000000e+00 -7.977700829505920410e-01 6.940407752990722656e-01 8.367550969123840332e-01 1.000000000000000000e+00 -8.034601807594299316e-01 7.012687325477600098e-01 8.405997753143310547e-01 1.000000000000000000e+00 -8.091503381729125977e-01 7.084967494010925293e-01 8.444444537162780762e-01 1.000000000000000000e+00 -8.148404359817504883e-01 7.157247066497802734e-01 8.482891321182250977e-01 1.000000000000000000e+00 -8.205305933952331543e-01 7.229527235031127930e-01 8.521338105201721191e-01 1.000000000000000000e+00 -8.262206912040710449e-01 7.301806807518005371e-01 8.559784889221191406e-01 1.000000000000000000e+00 -8.319107890129089355e-01 7.374086976051330566e-01 8.598231673240661621e-01 1.000000000000000000e+00 -8.376009464263916016e-01 7.446366548538208008e-01 8.636678457260131836e-01 1.000000000000000000e+00 -8.432910442352294922e-01 7.518646717071533203e-01 8.675125241279602051e-01 1.000000000000000000e+00 -8.489811420440673828e-01 7.590926289558410645e-01 8.713571429252624512e-01 1.000000000000000000e+00 -8.546712994575500488e-01 7.663206458091735840e-01 8.752018213272094727e-01 1.000000000000000000e+00 -8.603613972663879395e-01 7.735486626625061035e-01 8.790464997291564941e-01 1.000000000000000000e+00 -8.660514950752258301e-01 7.807766199111938477e-01 8.828911781311035156e-01 1.000000000000000000e+00 -8.717416524887084961e-01 7.880046367645263672e-01 8.867358565330505371e-01 1.000000000000000000e+00 -8.774317502975463867e-01 7.952325940132141113e-01 8.905805349349975586e-01 1.000000000000000000e+00 -8.831218481063842773e-01 8.024606108665466309e-01 8.944252133369445801e-01 1.000000000000000000e+00 -8.888120055198669434e-01 8.096885681152343750e-01 8.982698917388916016e-01 1.000000000000000000e+00 -8.945021033287048340e-01 8.169165849685668945e-01 9.021145701408386230e-01 1.000000000000000000e+00 -9.001922607421875000e-01 8.241445422172546387e-01 9.059592485427856445e-01 1.000000000000000000e+00 -9.058823585510253906e-01 8.313725590705871582e-01 9.098039269447326660e-01 1.000000000000000000e+00 -9.083429574966430664e-01 8.367550969123840332e-01 9.121107459068298340e-01 1.000000000000000000e+00 -9.108035564422607422e-01 8.421376347541809082e-01 9.144175052642822266e-01 1.000000000000000000e+00 -9.132641553878784180e-01 8.475201725959777832e-01 9.167243242263793945e-01 1.000000000000000000e+00 -9.157246947288513184e-01 8.529027104377746582e-01 9.190311431884765625e-01 1.000000000000000000e+00 -9.181852936744689941e-01 8.582852482795715332e-01 9.213379621505737305e-01 1.000000000000000000e+00 -9.206458926200866699e-01 8.636678457260131836e-01 9.236447811126708984e-01 1.000000000000000000e+00 -9.231064915657043457e-01 8.690503835678100586e-01 9.259515404701232910e-01 1.000000000000000000e+00 -9.255670905113220215e-01 8.744329214096069336e-01 9.282583594322204590e-01 1.000000000000000000e+00 -9.280276894569396973e-01 8.798154592514038086e-01 9.305651783943176270e-01 1.000000000000000000e+00 -9.304882884025573730e-01 8.851979970932006836e-01 9.328719973564147949e-01 1.000000000000000000e+00 -9.329488873481750488e-01 8.905805349349975586e-01 9.351787567138671875e-01 1.000000000000000000e+00 -9.354094862937927246e-01 8.959630727767944336e-01 9.374855756759643555e-01 1.000000000000000000e+00 -9.378700256347656250e-01 9.013456106185913086e-01 9.397923946380615234e-01 1.000000000000000000e+00 -9.403306245803833008e-01 9.067282080650329590e-01 9.420992136001586914e-01 1.000000000000000000e+00 -9.427912235260009766e-01 9.121107459068298340e-01 9.444059729576110840e-01 1.000000000000000000e+00 -9.452518224716186523e-01 9.174932837486267090e-01 9.467127919197082520e-01 1.000000000000000000e+00 -9.477124214172363281e-01 9.228758215904235840e-01 9.490196108818054199e-01 1.000000000000000000e+00 -9.501730203628540039e-01 9.282583594322204590e-01 9.513264298439025879e-01 1.000000000000000000e+00 -9.526336193084716797e-01 9.336408972740173340e-01 9.536331892013549805e-01 1.000000000000000000e+00 -9.550942182540893555e-01 9.390234351158142090e-01 9.559400081634521484e-01 1.000000000000000000e+00 -9.575547575950622559e-01 9.444059729576110840e-01 9.582468271255493164e-01 1.000000000000000000e+00 -9.600153565406799316e-01 9.497885704040527344e-01 9.605536460876464844e-01 1.000000000000000000e+00 -9.624759554862976074e-01 9.551711082458496094e-01 9.628604650497436523e-01 1.000000000000000000e+00 -9.649365544319152832e-01 9.605536460876464844e-01 9.651672244071960449e-01 1.000000000000000000e+00 -9.673971533775329590e-01 9.659361839294433594e-01 9.674740433692932129e-01 1.000000000000000000e+00 -9.663206338882446289e-01 9.680892229080200195e-01 9.658592939376831055e-01 1.000000000000000000e+00 -9.617070555686950684e-01 9.670127034187316895e-01 9.603229761123657227e-01 1.000000000000000000e+00 -9.570934176445007324e-01 9.659361839294433594e-01 9.547865986824035645e-01 1.000000000000000000e+00 -9.524798393249511719e-01 9.648596644401550293e-01 9.492502808570861816e-01 1.000000000000000000e+00 -9.478662014007568359e-01 9.637831449508666992e-01 9.437139630317687988e-01 1.000000000000000000e+00 -9.432526230812072754e-01 9.627066254615783691e-01 9.381776452064514160e-01 1.000000000000000000e+00 -9.386389851570129395e-01 9.616301655769348145e-01 9.326412677764892578e-01 1.000000000000000000e+00 -9.340253472328186035e-01 9.605536460876464844e-01 9.271049499511718750e-01 1.000000000000000000e+00 -9.294117689132690430e-01 9.594771265983581543e-01 9.215686321258544922e-01 1.000000000000000000e+00 -9.247981309890747070e-01 9.584006071090698242e-01 9.160323143005371094e-01 1.000000000000000000e+00 -9.201845526695251465e-01 9.573240876197814941e-01 9.104959368705749512e-01 1.000000000000000000e+00 -9.155709147453308105e-01 9.562475681304931641e-01 9.049596190452575684e-01 1.000000000000000000e+00 -9.109573364257812500e-01 9.551711082458496094e-01 8.994233012199401855e-01 1.000000000000000000e+00 -9.063436985015869141e-01 9.540945887565612793e-01 8.938869833946228027e-01 1.000000000000000000e+00 -9.017301201820373535e-01 9.530180692672729492e-01 8.883506059646606445e-01 1.000000000000000000e+00 -8.971164822578430176e-01 9.519415497779846191e-01 8.828142881393432617e-01 1.000000000000000000e+00 -8.925029039382934570e-01 9.508650302886962891e-01 8.772779703140258789e-01 1.000000000000000000e+00 -8.878892660140991211e-01 9.497885704040527344e-01 8.717416524887084961e-01 1.000000000000000000e+00 -8.832756876945495605e-01 9.487120509147644043e-01 8.662053346633911133e-01 1.000000000000000000e+00 -8.786620497703552246e-01 9.476355314254760742e-01 8.606689572334289551e-01 1.000000000000000000e+00 -8.740484714508056641e-01 9.465590119361877441e-01 8.551326394081115723e-01 1.000000000000000000e+00 -8.694348335266113281e-01 9.454824924468994141e-01 8.495963215827941895e-01 1.000000000000000000e+00 -8.648211956024169922e-01 9.444059729576110840e-01 8.440600037574768066e-01 1.000000000000000000e+00 -8.602076172828674316e-01 9.433295130729675293e-01 8.385236263275146484e-01 1.000000000000000000e+00 -8.555939793586730957e-01 9.422529935836791992e-01 8.329873085021972656e-01 1.000000000000000000e+00 -8.509804010391235352e-01 9.411764740943908691e-01 8.274509906768798828e-01 1.000000000000000000e+00 -8.431372642517089844e-01 9.379469156265258789e-01 8.196078538894653320e-01 1.000000000000000000e+00 -8.352941274642944336e-01 9.347174167633056641e-01 8.117647171020507812e-01 1.000000000000000000e+00 -8.274509906768798828e-01 9.314879179000854492e-01 8.039215803146362305e-01 1.000000000000000000e+00 -8.196078538894653320e-01 9.282583594322204590e-01 7.960784435272216797e-01 1.000000000000000000e+00 -8.117647171020507812e-01 9.250288605690002441e-01 7.882353067398071289e-01 1.000000000000000000e+00 -8.039215803146362305e-01 9.217993021011352539e-01 7.803921699523925781e-01 1.000000000000000000e+00 -7.960784435272216797e-01 9.185698032379150391e-01 7.725490331649780273e-01 1.000000000000000000e+00 -7.882353067398071289e-01 9.153402447700500488e-01 7.647058963775634766e-01 1.000000000000000000e+00 -7.803921699523925781e-01 9.121107459068298340e-01 7.568627595901489258e-01 1.000000000000000000e+00 -7.725490331649780273e-01 9.088811874389648438e-01 7.490196228027343750e-01 1.000000000000000000e+00 -7.647058963775634766e-01 9.056516885757446289e-01 7.411764860153198242e-01 1.000000000000000000e+00 -7.568627595901489258e-01 9.024221301078796387e-01 7.333333492279052734e-01 1.000000000000000000e+00 -7.490196228027343750e-01 8.991926312446594238e-01 7.254902124404907227e-01 1.000000000000000000e+00 -7.411764860153198242e-01 8.959630727767944336e-01 7.176470756530761719e-01 1.000000000000000000e+00 -7.333333492279052734e-01 8.927335739135742188e-01 7.098039388656616211e-01 1.000000000000000000e+00 -7.254902124404907227e-01 8.895040154457092285e-01 7.019608020782470703e-01 1.000000000000000000e+00 -7.176470756530761719e-01 8.862745165824890137e-01 6.941176652908325195e-01 1.000000000000000000e+00 -7.098039388656616211e-01 8.830449581146240234e-01 6.862745285034179688e-01 1.000000000000000000e+00 -7.019608020782470703e-01 8.798154592514038086e-01 6.784313917160034180e-01 1.000000000000000000e+00 -6.941176652908325195e-01 8.765859007835388184e-01 6.705882549285888672e-01 1.000000000000000000e+00 -6.862745285034179688e-01 8.733564019203186035e-01 6.627451181411743164e-01 1.000000000000000000e+00 -6.784313917160034180e-01 8.701269030570983887e-01 6.549019813537597656e-01 1.000000000000000000e+00 -6.705882549285888672e-01 8.668973445892333984e-01 6.470588445663452148e-01 1.000000000000000000e+00 -6.627451181411743164e-01 8.636678457260131836e-01 6.392157077789306641e-01 1.000000000000000000e+00 -6.549019813537597656e-01 8.604382872581481934e-01 6.313725709915161133e-01 1.000000000000000000e+00 -6.451364755630493164e-01 8.553633093833923340e-01 6.226066946983337402e-01 1.000000000000000000e+00 -6.334486603736877441e-01 8.484429121017456055e-01 6.129180788993835449e-01 1.000000000000000000e+00 -6.217608451843261719e-01 8.415225148200988770e-01 6.032295227050781250e-01 1.000000000000000000e+00 -6.100730299949645996e-01 8.346020579338073730e-01 5.935409665107727051e-01 1.000000000000000000e+00 -5.983852148056030273e-01 8.276816606521606445e-01 5.838523507118225098e-01 1.000000000000000000e+00 -5.866973996162414551e-01 8.207612633705139160e-01 5.741637945175170898e-01 1.000000000000000000e+00 -5.750095844268798828e-01 8.138408064842224121e-01 5.644751787185668945e-01 1.000000000000000000e+00 -5.633218288421630859e-01 8.069204092025756836e-01 5.547866225242614746e-01 1.000000000000000000e+00 -5.516340136528015137e-01 8.000000119209289551e-01 5.450980663299560547e-01 1.000000000000000000e+00 -5.399461984634399414e-01 7.930795550346374512e-01 5.354094505310058594e-01 1.000000000000000000e+00 -5.282583832740783691e-01 7.861591577529907227e-01 5.257208943367004395e-01 1.000000000000000000e+00 -5.165705680847167969e-01 7.792387604713439941e-01 5.160322785377502441e-01 1.000000000000000000e+00 -5.048827528953552246e-01 7.723183631896972656e-01 5.063437223434448242e-01 1.000000000000000000e+00 -4.931949377059936523e-01 7.653979063034057617e-01 4.966551363468170166e-01 1.000000000000000000e+00 -4.815071225166320801e-01 7.584775090217590332e-01 4.869665503501892090e-01 1.000000000000000000e+00 -4.698193073272705078e-01 7.515571117401123047e-01 4.772779643535614014e-01 1.000000000000000000e+00 -4.581314921379089355e-01 7.446366548538208008e-01 4.675893783569335938e-01 1.000000000000000000e+00 -4.464436769485473633e-01 7.377162575721740723e-01 4.579008221626281738e-01 1.000000000000000000e+00 -4.347558617591857910e-01 7.307958602905273438e-01 4.482122361660003662e-01 1.000000000000000000e+00 -4.230680465698242188e-01 7.238754034042358398e-01 4.385236501693725586e-01 1.000000000000000000e+00 -4.113802313804626465e-01 7.169550061225891113e-01 4.288350641727447510e-01 1.000000000000000000e+00 -3.996924161911010742e-01 7.100346088409423828e-01 4.191464781761169434e-01 1.000000000000000000e+00 -3.880046010017395020e-01 7.031142115592956543e-01 4.094578921794891357e-01 1.000000000000000000e+00 -3.763168156147003174e-01 6.961937546730041504e-01 3.997693061828613281e-01 1.000000000000000000e+00 -3.646290004253387451e-01 6.892733573913574219e-01 3.900807499885559082e-01 1.000000000000000000e+00 -3.529411852359771729e-01 6.823529601097106934e-01 3.803921639919281006e-01 1.000000000000000000e+00 -3.432525992393493652e-01 6.740484237670898438e-01 3.739331066608428955e-01 1.000000000000000000e+00 -3.335640132427215576e-01 6.657439470291137695e-01 3.674740493297576904e-01 1.000000000000000000e+00 -3.238754272460937500e-01 6.574394702911376953e-01 3.610149919986724854e-01 1.000000000000000000e+00 -3.141868412494659424e-01 6.491349339485168457e-01 3.545559346675872803e-01 1.000000000000000000e+00 -3.044982552528381348e-01 6.408304572105407715e-01 3.480968773365020752e-01 1.000000000000000000e+00 -2.948096990585327148e-01 6.325259804725646973e-01 3.416378200054168701e-01 1.000000000000000000e+00 -2.851211130619049072e-01 6.242214441299438477e-01 3.351787626743316650e-01 1.000000000000000000e+00 -2.754325270652770996e-01 6.159169673919677734e-01 3.287197351455688477e-01 1.000000000000000000e+00 -2.657439410686492920e-01 6.076124310493469238e-01 3.222606778144836426e-01 1.000000000000000000e+00 -2.560553550720214844e-01 5.993079543113708496e-01 3.158016204833984375e-01 1.000000000000000000e+00 -2.463667839765548706e-01 5.910034775733947754e-01 3.093425631523132324e-01 1.000000000000000000e+00 -2.366781979799270630e-01 5.826989412307739258e-01 3.028835058212280273e-01 1.000000000000000000e+00 -2.269896119832992554e-01 5.743944644927978516e-01 2.964244484901428223e-01 1.000000000000000000e+00 -2.173010408878326416e-01 5.660899877548217773e-01 2.899653911590576172e-01 1.000000000000000000e+00 -2.076124548912048340e-01 5.577854514122009277e-01 2.835063338279724121e-01 1.000000000000000000e+00 -1.979238688945770264e-01 5.494809746742248535e-01 2.770472764968872070e-01 1.000000000000000000e+00 -1.882352977991104126e-01 5.411764979362487793e-01 2.705882489681243896e-01 1.000000000000000000e+00 -1.785467118024826050e-01 5.328719615936279297e-01 2.641291916370391846e-01 1.000000000000000000e+00 -1.688581258058547974e-01 5.245674848556518555e-01 2.576701343059539795e-01 1.000000000000000000e+00 -1.591695547103881836e-01 5.162629485130310059e-01 2.512110769748687744e-01 1.000000000000000000e+00 -1.494809687137603760e-01 5.079584717750549316e-01 2.447520196437835693e-01 1.000000000000000000e+00 -1.397923827171325684e-01 4.996539652347564697e-01 2.382929623126983643e-01 1.000000000000000000e+00 -1.301038116216659546e-01 4.913494884967803955e-01 2.318339049816131592e-01 1.000000000000000000e+00 -1.204152256250381470e-01 4.830449819564819336e-01 2.253748625516891479e-01 1.000000000000000000e+00 -1.107266470789909363e-01 4.747404754161834717e-01 2.189158052206039429e-01 1.000000000000000000e+00 -1.038062274456024170e-01 4.665897786617279053e-01 2.135332524776458740e-01 1.000000000000000000e+00 -9.965398162603378296e-02 4.585928618907928467e-01 2.092272192239761353e-01 1.000000000000000000e+00 -9.550172835588455200e-02 4.505959153175354004e-01 2.049211859703063965e-01 1.000000000000000000e+00 -9.134948253631591797e-02 4.425989985466003418e-01 2.006151527166366577e-01 1.000000000000000000e+00 -8.719722926616668701e-02 4.346020817756652832e-01 1.963091045618057251e-01 1.000000000000000000e+00 -8.304498344659805298e-02 4.266051650047302246e-01 1.920030713081359863e-01 1.000000000000000000e+00 -7.889273017644882202e-02 4.186082184314727783e-01 1.876970380544662476e-01 1.000000000000000000e+00 -7.474048435688018799e-02 4.106113016605377197e-01 1.833910048007965088e-01 1.000000000000000000e+00 -7.058823853731155396e-02 4.026143848896026611e-01 1.790849715471267700e-01 1.000000000000000000e+00 -6.643598526716232300e-02 3.946174681186676025e-01 1.747789382934570312e-01 1.000000000000000000e+00 -6.228373572230339050e-02 3.866205215454101562e-01 1.704728901386260986e-01 1.000000000000000000e+00 -5.813148617744445801e-02 3.786236047744750977e-01 1.661668568849563599e-01 1.000000000000000000e+00 -5.397924035787582397e-02 3.706266880035400391e-01 1.618608236312866211e-01 1.000000000000000000e+00 -4.982699081301689148e-02 3.626297712326049805e-01 1.575547903776168823e-01 1.000000000000000000e+00 -4.567474126815795898e-02 3.546328246593475342e-01 1.532487571239471436e-01 1.000000000000000000e+00 -4.152249172329902649e-02 3.466359078884124756e-01 1.489427089691162109e-01 1.000000000000000000e+00 -3.737024217844009399e-02 3.386389911174774170e-01 1.446366757154464722e-01 1.000000000000000000e+00 -3.321799263358116150e-02 3.306420743465423584e-01 1.403306424617767334e-01 1.000000000000000000e+00 -2.906574308872222900e-02 3.226451277732849121e-01 1.360246092081069946e-01 1.000000000000000000e+00 -2.491349540650844574e-02 3.146482110023498535e-01 1.317185759544372559e-01 1.000000000000000000e+00 -2.076124586164951324e-02 3.066512942314147949e-01 1.274125277996063232e-01 1.000000000000000000e+00 -1.660899631679058075e-02 2.986543774604797363e-01 1.231064945459365845e-01 1.000000000000000000e+00 -1.245674770325422287e-02 2.906574308872222900e-01 1.188004612922668457e-01 1.000000000000000000e+00 -8.304498158395290375e-03 2.826605141162872314e-01 1.144944280385971069e-01 1.000000000000000000e+00 -4.152249079197645187e-03 2.746635973453521729e-01 1.101883873343467712e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.666666805744171143e-01 1.058823540806770325e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/Paired b/fastplotlib/utils/colormaps/Paired deleted file mode 100644 index 710d68bf4..000000000 --- a/fastplotlib/utils/colormaps/Paired +++ /dev/null @@ -1,12 +0,0 @@ -6.509804129600524902e-01 8.078431487083435059e-01 8.901960849761962891e-01 1.000000000000000000e+00 -1.215686276555061340e-01 4.705882370471954346e-01 7.058823704719543457e-01 1.000000000000000000e+00 -6.980392336845397949e-01 8.745098114013671875e-01 5.411764979362487793e-01 1.000000000000000000e+00 -2.000000029802322388e-01 6.274510025978088379e-01 1.725490242242813110e-01 1.000000000000000000e+00 -9.843137264251708984e-01 6.039215922355651855e-01 6.000000238418579102e-01 1.000000000000000000e+00 -8.901960849761962891e-01 1.019607856869697571e-01 1.098039224743843079e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.490196228027343750e-01 4.352941215038299561e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.980392158031463623e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.921568751335144043e-01 6.980392336845397949e-01 8.392156958580017090e-01 1.000000000000000000e+00 -4.156862795352935791e-01 2.392156869173049927e-01 6.039215922355651855e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 6.000000238418579102e-01 1.000000000000000000e+00 -6.941176652908325195e-01 3.490196168422698975e-01 1.568627506494522095e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/Pastel1 b/fastplotlib/utils/colormaps/Pastel1 deleted file mode 100644 index 9f1a1eb66..000000000 --- a/fastplotlib/utils/colormaps/Pastel1 +++ /dev/null @@ -1,9 +0,0 @@ -9.843137264251708984e-01 7.058823704719543457e-01 6.823529601097106934e-01 1.000000000000000000e+00 -7.019608020782470703e-01 8.039215803146362305e-01 8.901960849761962891e-01 1.000000000000000000e+00 -8.000000119209289551e-01 9.215686321258544922e-01 7.725490331649780273e-01 1.000000000000000000e+00 -8.705882430076599121e-01 7.960784435272216797e-01 8.941176533699035645e-01 1.000000000000000000e+00 -9.960784316062927246e-01 8.509804010391235352e-01 6.509804129600524902e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 8.000000119209289551e-01 1.000000000000000000e+00 -8.980392217636108398e-01 8.470588326454162598e-01 7.411764860153198242e-01 1.000000000000000000e+00 -9.921568632125854492e-01 8.549019694328308105e-01 9.254902005195617676e-01 1.000000000000000000e+00 -9.490196108818054199e-01 9.490196108818054199e-01 9.490196108818054199e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/Pastel2 b/fastplotlib/utils/colormaps/Pastel2 deleted file mode 100644 index adcb77b02..000000000 --- a/fastplotlib/utils/colormaps/Pastel2 +++ /dev/null @@ -1,8 +0,0 @@ -7.019608020782470703e-01 8.862745165824890137e-01 8.039215803146362305e-01 1.000000000000000000e+00 -9.921568632125854492e-01 8.039215803146362305e-01 6.745098233222961426e-01 1.000000000000000000e+00 -7.960784435272216797e-01 8.352941274642944336e-01 9.098039269447326660e-01 1.000000000000000000e+00 -9.568627476692199707e-01 7.921568751335144043e-01 8.941176533699035645e-01 1.000000000000000000e+00 -9.019607901573181152e-01 9.607843160629272461e-01 7.882353067398071289e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.490196108818054199e-01 6.823529601097106934e-01 1.000000000000000000e+00 -9.450980424880981445e-01 8.862745165824890137e-01 8.000000119209289551e-01 1.000000000000000000e+00 -8.000000119209289551e-01 8.000000119209289551e-01 8.000000119209289551e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/PiYG b/fastplotlib/utils/colormaps/PiYG deleted file mode 100644 index 5e8a656fb..000000000 --- a/fastplotlib/utils/colormaps/PiYG +++ /dev/null @@ -1,256 +0,0 @@ -5.568627715110778809e-01 3.921568859368562698e-03 3.215686380863189697e-01 1.000000000000000000e+00 -5.653210282325744629e-01 7.920030504465103149e-03 3.281814754009246826e-01 1.000000000000000000e+00 -5.737793445587158203e-01 1.191849261522293091e-02 3.347943127155303955e-01 1.000000000000000000e+00 -5.822376012802124023e-01 1.591695472598075867e-02 3.414071500301361084e-01 1.000000000000000000e+00 -5.906958580017089844e-01 1.991541683673858643e-02 3.480199873447418213e-01 1.000000000000000000e+00 -5.991541743278503418e-01 2.391387894749641418e-02 3.546328246593475342e-01 1.000000000000000000e+00 -6.076124310493469238e-01 2.791234105825424194e-02 3.612456619739532471e-01 1.000000000000000000e+00 -6.160707473754882812e-01 3.191080316901206970e-02 3.678585290908813477e-01 1.000000000000000000e+00 -6.245290040969848633e-01 3.590926527976989746e-02 3.744713664054870605e-01 1.000000000000000000e+00 -6.329873204231262207e-01 3.990772739052772522e-02 3.810842037200927734e-01 1.000000000000000000e+00 -6.414455771446228027e-01 4.390618950128555298e-02 3.876970410346984863e-01 1.000000000000000000e+00 -6.499038934707641602e-01 4.790465161204338074e-02 3.943098783493041992e-01 1.000000000000000000e+00 -6.583621501922607422e-01 5.190311372280120850e-02 4.009227156639099121e-01 1.000000000000000000e+00 -6.668204665184020996e-01 5.590157583355903625e-02 4.075355529785156250e-01 1.000000000000000000e+00 -6.752787232398986816e-01 5.990003794431686401e-02 4.141483902931213379e-01 1.000000000000000000e+00 -6.837370395660400391e-01 6.389850378036499023e-02 4.207612574100494385e-01 1.000000000000000000e+00 -6.921952962875366211e-01 6.789696216583251953e-02 4.273740947246551514e-01 1.000000000000000000e+00 -7.006536126136779785e-01 7.189542800188064575e-02 4.339869320392608643e-01 1.000000000000000000e+00 -7.091118693351745605e-01 7.589388638734817505e-02 4.405997693538665771e-01 1.000000000000000000e+00 -7.175701856613159180e-01 7.989235222339630127e-02 4.472126066684722900e-01 1.000000000000000000e+00 -7.260284423828125000e-01 8.389081060886383057e-02 4.538254439830780029e-01 1.000000000000000000e+00 -7.344867587089538574e-01 8.788927644491195679e-02 4.604382812976837158e-01 1.000000000000000000e+00 -7.429450154304504395e-01 9.188773483037948608e-02 4.670511484146118164e-01 1.000000000000000000e+00 -7.514033317565917969e-01 9.588620066642761230e-02 4.736639857292175293e-01 1.000000000000000000e+00 -7.598615884780883789e-01 9.988465905189514160e-02 4.802768230438232422e-01 1.000000000000000000e+00 -7.683199048042297363e-01 1.038831248879432678e-01 4.868896603584289551e-01 1.000000000000000000e+00 -7.744713425636291504e-01 1.129565536975860596e-01 4.939638674259185791e-01 1.000000000000000000e+00 -7.783160209655761719e-01 1.271049529314041138e-01 5.014994144439697266e-01 1.000000000000000000e+00 -7.821606993675231934e-01 1.412533670663833618e-01 5.090349912643432617e-01 1.000000000000000000e+00 -7.860053777694702148e-01 1.554017663002014160e-01 5.165705680847167969e-01 1.000000000000000000e+00 -7.898500561714172363e-01 1.695501804351806641e-01 5.241060853004455566e-01 1.000000000000000000e+00 -7.936947345733642578e-01 1.836985796689987183e-01 5.316416621208190918e-01 1.000000000000000000e+00 -7.975394129753112793e-01 1.978469789028167725e-01 5.391772389411926270e-01 1.000000000000000000e+00 -8.013840913772583008e-01 2.119953930377960205e-01 5.467128157615661621e-01 1.000000000000000000e+00 -8.052287697792053223e-01 2.261437922716140747e-01 5.542483925819396973e-01 1.000000000000000000e+00 -8.090734481811523438e-01 2.402921915054321289e-01 5.617839097976684570e-01 1.000000000000000000e+00 -8.129181265830993652e-01 2.544406056404113770e-01 5.693194866180419922e-01 1.000000000000000000e+00 -8.167628049850463867e-01 2.685889899730682373e-01 5.768550634384155273e-01 1.000000000000000000e+00 -8.206074833869934082e-01 2.827374041080474854e-01 5.843906402587890625e-01 1.000000000000000000e+00 -8.244521617889404297e-01 2.968858182430267334e-01 5.919261574745178223e-01 1.000000000000000000e+00 -8.282967805862426758e-01 3.110342323780059814e-01 5.994617342948913574e-01 1.000000000000000000e+00 -8.321414589881896973e-01 3.251826167106628418e-01 6.069973111152648926e-01 1.000000000000000000e+00 -8.359861373901367188e-01 3.393310308456420898e-01 6.145328879356384277e-01 1.000000000000000000e+00 -8.398308157920837402e-01 3.534794449806213379e-01 6.220684647560119629e-01 1.000000000000000000e+00 -8.436754941940307617e-01 3.676278293132781982e-01 6.296039819717407227e-01 1.000000000000000000e+00 -8.475201725959777832e-01 3.817762434482574463e-01 6.371395587921142578e-01 1.000000000000000000e+00 -8.513648509979248047e-01 3.959246575832366943e-01 6.446751356124877930e-01 1.000000000000000000e+00 -8.552095293998718262e-01 4.100730419158935547e-01 6.522107124328613281e-01 1.000000000000000000e+00 -8.590542078018188477e-01 4.242214560508728027e-01 6.597462296485900879e-01 1.000000000000000000e+00 -8.628988862037658691e-01 4.383698701858520508e-01 6.672818064689636230e-01 1.000000000000000000e+00 -8.667435646057128906e-01 4.525182545185089111e-01 6.748173832893371582e-01 1.000000000000000000e+00 -8.705882430076599121e-01 4.666666686534881592e-01 6.823529601097106934e-01 1.000000000000000000e+00 -8.735101819038391113e-01 4.763552546501159668e-01 6.891195774078369141e-01 1.000000000000000000e+00 -8.764321208000183105e-01 4.860438406467437744e-01 6.958861947059631348e-01 1.000000000000000000e+00 -8.793541193008422852e-01 4.957323968410491943e-01 7.026528120040893555e-01 1.000000000000000000e+00 -8.822760581970214844e-01 5.054209828376770020e-01 7.094194293022155762e-01 1.000000000000000000e+00 -8.851979970932006836e-01 5.151095986366271973e-01 7.161861062049865723e-01 1.000000000000000000e+00 -8.881199359893798828e-01 5.247981548309326172e-01 7.229527235031127930e-01 1.000000000000000000e+00 -8.910419344902038574e-01 5.344867110252380371e-01 7.297193408012390137e-01 1.000000000000000000e+00 -8.939638733863830566e-01 5.441753268241882324e-01 7.364859580993652344e-01 1.000000000000000000e+00 -8.968858122825622559e-01 5.538638830184936523e-01 7.432525753974914551e-01 1.000000000000000000e+00 -8.998077511787414551e-01 5.635524988174438477e-01 7.500192523002624512e-01 1.000000000000000000e+00 -9.027296900749206543e-01 5.732410550117492676e-01 7.567858695983886719e-01 1.000000000000000000e+00 -9.056516885757446289e-01 5.829296708106994629e-01 7.635524868965148926e-01 1.000000000000000000e+00 -9.085736274719238281e-01 5.926182270050048828e-01 7.703191041946411133e-01 1.000000000000000000e+00 -9.114955663681030273e-01 6.023067831993103027e-01 7.770857214927673340e-01 1.000000000000000000e+00 -9.144175052642822266e-01 6.119953989982604980e-01 7.838523387908935547e-01 1.000000000000000000e+00 -9.173395037651062012e-01 6.216839551925659180e-01 7.906190156936645508e-01 1.000000000000000000e+00 -9.202614426612854004e-01 6.313725709915161133e-01 7.973856329917907715e-01 1.000000000000000000e+00 -9.231833815574645996e-01 6.410611271858215332e-01 8.041522502899169922e-01 1.000000000000000000e+00 -9.261053204536437988e-01 6.507496833801269531e-01 8.109188675880432129e-01 1.000000000000000000e+00 -9.290273189544677734e-01 6.604382991790771484e-01 8.176854848861694336e-01 1.000000000000000000e+00 -9.319492578506469727e-01 6.701268553733825684e-01 8.244521617889404297e-01 1.000000000000000000e+00 -9.348711967468261719e-01 6.798154711723327637e-01 8.312187790870666504e-01 1.000000000000000000e+00 -9.377931356430053711e-01 6.895040273666381836e-01 8.379853963851928711e-01 1.000000000000000000e+00 -9.407151341438293457e-01 6.991926431655883789e-01 8.447520136833190918e-01 1.000000000000000000e+00 -9.436370730400085449e-01 7.088811993598937988e-01 8.515186309814453125e-01 1.000000000000000000e+00 -9.460207819938659668e-01 7.169550061225891113e-01 8.565167188644409180e-01 1.000000000000000000e+00 -9.478662014007568359e-01 7.234140634536743164e-01 8.597462773323059082e-01 1.000000000000000000e+00 -9.497116208076477051e-01 7.298731207847595215e-01 8.629757761955261230e-01 1.000000000000000000e+00 -9.515570998191833496e-01 7.363321781158447266e-01 8.662053346633911133e-01 1.000000000000000000e+00 -9.534025192260742188e-01 7.427912354469299316e-01 8.694348335266113281e-01 1.000000000000000000e+00 -9.552479982376098633e-01 7.492502927780151367e-01 8.726643323898315430e-01 1.000000000000000000e+00 -9.570934176445007324e-01 7.557093501091003418e-01 8.758938908576965332e-01 1.000000000000000000e+00 -9.589388966560363770e-01 7.621684074401855469e-01 8.791233897209167480e-01 1.000000000000000000e+00 -9.607843160629272461e-01 7.686274647712707520e-01 8.823529481887817383e-01 1.000000000000000000e+00 -9.626297354698181152e-01 7.750865221023559570e-01 8.855824470520019531e-01 1.000000000000000000e+00 -9.644752144813537598e-01 7.815455794334411621e-01 8.888120055198669434e-01 1.000000000000000000e+00 -9.663206338882446289e-01 7.880046367645263672e-01 8.920415043830871582e-01 1.000000000000000000e+00 -9.681661128997802734e-01 7.944636940956115723e-01 8.952710628509521484e-01 1.000000000000000000e+00 -9.700115323066711426e-01 8.009227514266967773e-01 8.985005617141723633e-01 1.000000000000000000e+00 -9.718569517135620117e-01 8.073817491531372070e-01 9.017301201820373535e-01 1.000000000000000000e+00 -9.737024307250976562e-01 8.138408064842224121e-01 9.049596190452575684e-01 1.000000000000000000e+00 -9.755478501319885254e-01 8.202998638153076172e-01 9.081891775131225586e-01 1.000000000000000000e+00 -9.773933291435241699e-01 8.267589211463928223e-01 9.114186763763427734e-01 1.000000000000000000e+00 -9.792387485504150391e-01 8.332179784774780273e-01 9.146482348442077637e-01 1.000000000000000000e+00 -9.810842275619506836e-01 8.396770358085632324e-01 9.178777337074279785e-01 1.000000000000000000e+00 -9.829296469688415527e-01 8.461360931396484375e-01 9.211072921752929688e-01 1.000000000000000000e+00 -9.847750663757324219e-01 8.525951504707336426e-01 9.243367910385131836e-01 1.000000000000000000e+00 -9.866205453872680664e-01 8.590542078018188477e-01 9.275663495063781738e-01 1.000000000000000000e+00 -9.884659647941589355e-01 8.655132651329040527e-01 9.307958483695983887e-01 1.000000000000000000e+00 -9.903114438056945801e-01 8.719723224639892578e-01 9.340253472328186035e-01 1.000000000000000000e+00 -9.921568632125854492e-01 8.784313797950744629e-01 9.372549057006835938e-01 1.000000000000000000e+00 -9.912341237068176270e-01 8.819684982299804688e-01 9.384852051734924316e-01 1.000000000000000000e+00 -9.903114438056945801e-01 8.855055570602416992e-01 9.397155046463012695e-01 1.000000000000000000e+00 -9.893887042999267578e-01 8.890426754951477051e-01 9.409458041191101074e-01 1.000000000000000000e+00 -9.884659647941589355e-01 8.925797939300537109e-01 9.421761035919189453e-01 1.000000000000000000e+00 -9.875432252883911133e-01 8.961168527603149414e-01 9.434064030647277832e-01 1.000000000000000000e+00 -9.866205453872680664e-01 8.996539711952209473e-01 9.446367025375366211e-01 1.000000000000000000e+00 -9.856978058815002441e-01 9.031910896301269531e-01 9.458670020103454590e-01 1.000000000000000000e+00 -9.847750663757324219e-01 9.067282080650329590e-01 9.470972418785095215e-01 1.000000000000000000e+00 -9.838523864746093750e-01 9.102652668952941895e-01 9.483275413513183594e-01 1.000000000000000000e+00 -9.829296469688415527e-01 9.138023853302001953e-01 9.495578408241271973e-01 1.000000000000000000e+00 -9.820069074630737305e-01 9.173395037651062012e-01 9.507881402969360352e-01 1.000000000000000000e+00 -9.810842275619506836e-01 9.208765625953674316e-01 9.520184397697448730e-01 1.000000000000000000e+00 -9.801614880561828613e-01 9.244136810302734375e-01 9.532487392425537109e-01 1.000000000000000000e+00 -9.792387485504150391e-01 9.279507994651794434e-01 9.544790387153625488e-01 1.000000000000000000e+00 -9.783160090446472168e-01 9.314879179000854492e-01 9.557093381881713867e-01 1.000000000000000000e+00 -9.773933291435241699e-01 9.350249767303466797e-01 9.569396376609802246e-01 1.000000000000000000e+00 -9.764705896377563477e-01 9.385620951652526855e-01 9.581699371337890625e-01 1.000000000000000000e+00 -9.755478501319885254e-01 9.420992136001586914e-01 9.594002366065979004e-01 1.000000000000000000e+00 -9.746251702308654785e-01 9.456362724304199219e-01 9.606305360794067383e-01 1.000000000000000000e+00 -9.737024307250976562e-01 9.491733908653259277e-01 9.618608355522155762e-01 1.000000000000000000e+00 -9.727796912193298340e-01 9.527105093002319336e-01 9.630911350250244141e-01 1.000000000000000000e+00 -9.718569517135620117e-01 9.562475681304931641e-01 9.643214344978332520e-01 1.000000000000000000e+00 -9.709342718124389648e-01 9.597846865653991699e-01 9.655517339706420898e-01 1.000000000000000000e+00 -9.700115323066711426e-01 9.633218050003051758e-01 9.667820334434509277e-01 1.000000000000000000e+00 -9.690887928009033203e-01 9.668589234352111816e-01 9.680122733116149902e-01 1.000000000000000000e+00 -9.673202633857727051e-01 9.684736728668212891e-01 9.656286239624023438e-01 1.000000000000000000e+00 -9.647058844566345215e-01 9.681661128997802734e-01 9.596309065818786621e-01 1.000000000000000000e+00 -9.620915055274963379e-01 9.678584933280944824e-01 9.536331892013549805e-01 1.000000000000000000e+00 -9.594771265983581543e-01 9.675509333610534668e-01 9.476355314254760742e-01 1.000000000000000000e+00 -9.568627476692199707e-01 9.672433733940124512e-01 9.416378140449523926e-01 1.000000000000000000e+00 -9.542483687400817871e-01 9.669358134269714355e-01 9.356401562690734863e-01 1.000000000000000000e+00 -9.516339898109436035e-01 9.666281938552856445e-01 9.296424388885498047e-01 1.000000000000000000e+00 -9.490196108818054199e-01 9.663206338882446289e-01 9.236447811126708984e-01 1.000000000000000000e+00 -9.464052319526672363e-01 9.660130739212036133e-01 9.176470637321472168e-01 1.000000000000000000e+00 -9.437908530235290527e-01 9.657055139541625977e-01 9.116493463516235352e-01 1.000000000000000000e+00 -9.411764740943908691e-01 9.653978943824768066e-01 9.056516885757446289e-01 1.000000000000000000e+00 -9.385620951652526855e-01 9.650903344154357910e-01 8.996539711952209473e-01 1.000000000000000000e+00 -9.359477162361145020e-01 9.647827744483947754e-01 8.936563134193420410e-01 1.000000000000000000e+00 -9.333333373069763184e-01 9.644752144813537598e-01 8.876585960388183594e-01 1.000000000000000000e+00 -9.307189583778381348e-01 9.641676545143127441e-01 8.816608786582946777e-01 1.000000000000000000e+00 -9.281045794486999512e-01 9.638600349426269531e-01 8.756632208824157715e-01 1.000000000000000000e+00 -9.254902005195617676e-01 9.635524749755859375e-01 8.696655035018920898e-01 1.000000000000000000e+00 -9.228758215904235840e-01 9.632449150085449219e-01 8.636678457260131836e-01 1.000000000000000000e+00 -9.202614426612854004e-01 9.629373550415039062e-01 8.576701283454895020e-01 1.000000000000000000e+00 -9.176470637321472168e-01 9.626297354698181152e-01 8.516724109649658203e-01 1.000000000000000000e+00 -9.150326848030090332e-01 9.623221755027770996e-01 8.456747531890869141e-01 1.000000000000000000e+00 -9.124183058738708496e-01 9.620146155357360840e-01 8.396770358085632324e-01 1.000000000000000000e+00 -9.098039269447326660e-01 9.617070555686950684e-01 8.336793780326843262e-01 1.000000000000000000e+00 -9.071895480155944824e-01 9.613994359970092773e-01 8.276816606521606445e-01 1.000000000000000000e+00 -9.045751690864562988e-01 9.610918760299682617e-01 8.216839432716369629e-01 1.000000000000000000e+00 -9.019607901573181152e-01 9.607843160629272461e-01 8.156862854957580566e-01 1.000000000000000000e+00 -8.948865532875061035e-01 9.577085971832275391e-01 8.043060302734375000e-01 1.000000000000000000e+00 -8.878123760223388672e-01 9.546328186988830566e-01 7.929257750511169434e-01 1.000000000000000000e+00 -8.807381987571716309e-01 9.515570998191833496e-01 7.815455794334411621e-01 1.000000000000000000e+00 -8.736639618873596191e-01 9.484813809394836426e-01 7.701653242111206055e-01 1.000000000000000000e+00 -8.665897846221923828e-01 9.454056024551391602e-01 7.587850689888000488e-01 1.000000000000000000e+00 -8.595155477523803711e-01 9.423298835754394531e-01 7.474048733711242676e-01 1.000000000000000000e+00 -8.524413704872131348e-01 9.392541050910949707e-01 7.360246181488037109e-01 1.000000000000000000e+00 -8.453671932220458984e-01 9.361783862113952637e-01 7.246443629264831543e-01 1.000000000000000000e+00 -8.382929563522338867e-01 9.331026673316955566e-01 7.132641077041625977e-01 1.000000000000000000e+00 -8.312187790870666504e-01 9.300268888473510742e-01 7.018839120864868164e-01 1.000000000000000000e+00 -8.241445422172546387e-01 9.269511699676513672e-01 6.905036568641662598e-01 1.000000000000000000e+00 -8.170703649520874023e-01 9.238754510879516602e-01 6.791234016418457031e-01 1.000000000000000000e+00 -8.099961280822753906e-01 9.207996726036071777e-01 6.677431464195251465e-01 1.000000000000000000e+00 -8.029219508171081543e-01 9.177239537239074707e-01 6.563629508018493652e-01 1.000000000000000000e+00 -7.958477735519409180e-01 9.146482348442077637e-01 6.449826955795288086e-01 1.000000000000000000e+00 -7.887735366821289062e-01 9.115724563598632812e-01 6.336024403572082520e-01 1.000000000000000000e+00 -7.816993594169616699e-01 9.084967374801635742e-01 6.222222447395324707e-01 1.000000000000000000e+00 -7.746251225471496582e-01 9.054210186004638672e-01 6.108419895172119141e-01 1.000000000000000000e+00 -7.675509452819824219e-01 9.023452401161193848e-01 5.994617342948913574e-01 1.000000000000000000e+00 -7.604767680168151855e-01 8.992695212364196777e-01 5.880814790725708008e-01 1.000000000000000000e+00 -7.534025311470031738e-01 8.961937427520751953e-01 5.767012834548950195e-01 1.000000000000000000e+00 -7.463283538818359375e-01 8.931180238723754883e-01 5.653210282325744629e-01 1.000000000000000000e+00 -7.392541170120239258e-01 8.900423049926757812e-01 5.539407730102539062e-01 1.000000000000000000e+00 -7.321799397468566895e-01 8.869665265083312988e-01 5.425605773925781250e-01 1.000000000000000000e+00 -7.251057028770446777e-01 8.838908076286315918e-01 5.311803221702575684e-01 1.000000000000000000e+00 -7.171856760978698730e-01 8.795078992843627930e-01 5.201845169067382812e-01 1.000000000000000000e+00 -7.084198594093322754e-01 8.738177418708801270e-01 5.095732212066650391e-01 1.000000000000000000e+00 -6.996539831161499023e-01 8.681276440620422363e-01 4.989619255065917969e-01 1.000000000000000000e+00 -6.908881068229675293e-01 8.624375462532043457e-01 4.883506298065185547e-01 1.000000000000000000e+00 -6.821222901344299316e-01 8.567473888397216797e-01 4.777393341064453125e-01 1.000000000000000000e+00 -6.733564138412475586e-01 8.510572910308837891e-01 4.671280384063720703e-01 1.000000000000000000e+00 -6.645905375480651855e-01 8.453671932220458984e-01 4.565167129039764404e-01 1.000000000000000000e+00 -6.558246612548828125e-01 8.396770358085632324e-01 4.459054172039031982e-01 1.000000000000000000e+00 -6.470588445663452148e-01 8.339869379997253418e-01 4.352941215038299561e-01 1.000000000000000000e+00 -6.382929682731628418e-01 8.282967805862426758e-01 4.246828258037567139e-01 1.000000000000000000e+00 -6.295270919799804688e-01 8.226066827774047852e-01 4.140715003013610840e-01 1.000000000000000000e+00 -6.207612752914428711e-01 8.169165849685668945e-01 4.034602046012878418e-01 1.000000000000000000e+00 -6.119953989982604980e-01 8.112264275550842285e-01 3.928489089012145996e-01 1.000000000000000000e+00 -6.032295227050781250e-01 8.055363297462463379e-01 3.822376132011413574e-01 1.000000000000000000e+00 -5.944636464118957520e-01 7.998462319374084473e-01 3.716262876987457275e-01 1.000000000000000000e+00 -5.856978297233581543e-01 7.941560745239257812e-01 3.610149919986724854e-01 1.000000000000000000e+00 -5.769319534301757812e-01 7.884659767150878906e-01 3.504036962985992432e-01 1.000000000000000000e+00 -5.681660771369934082e-01 7.827758789062500000e-01 3.397924005985260010e-01 1.000000000000000000e+00 -5.594002604484558105e-01 7.770857214927673340e-01 3.291810750961303711e-01 1.000000000000000000e+00 -5.506343841552734375e-01 7.713956236839294434e-01 3.185697793960571289e-01 1.000000000000000000e+00 -5.418685078620910645e-01 7.657055258750915527e-01 3.079584836959838867e-01 1.000000000000000000e+00 -5.331026315689086914e-01 7.600153684616088867e-01 2.973471879959106445e-01 1.000000000000000000e+00 -5.243368148803710938e-01 7.543252706527709961e-01 2.867358624935150146e-01 1.000000000000000000e+00 -5.155709385871887207e-01 7.486351132392883301e-01 2.761245667934417725e-01 1.000000000000000000e+00 -5.068050622940063477e-01 7.429450154304504395e-01 2.655132710933685303e-01 1.000000000000000000e+00 -4.980392158031463623e-01 7.372549176216125488e-01 2.549019753932952881e-01 1.000000000000000000e+00 -4.903498589992523193e-01 7.307958602905273438e-01 2.499807775020599365e-01 1.000000000000000000e+00 -4.826605021953582764e-01 7.243368029594421387e-01 2.450595945119857788e-01 1.000000000000000000e+00 -4.749711751937866211e-01 7.178777456283569336e-01 2.401384115219116211e-01 1.000000000000000000e+00 -4.672818183898925781e-01 7.114186882972717285e-01 2.352172285318374634e-01 1.000000000000000000e+00 -4.595924615859985352e-01 7.049596309661865234e-01 2.302960455417633057e-01 1.000000000000000000e+00 -4.519031047821044922e-01 6.985005736351013184e-01 2.253748625516891479e-01 1.000000000000000000e+00 -4.442137777805328369e-01 6.920415163040161133e-01 2.204536646604537964e-01 1.000000000000000000e+00 -4.365244209766387939e-01 6.855824589729309082e-01 2.155324816703796387e-01 1.000000000000000000e+00 -4.288350641727447510e-01 6.791234016418457031e-01 2.106112986803054810e-01 1.000000000000000000e+00 -4.211457073688507080e-01 6.726643443107604980e-01 2.056901156902313232e-01 1.000000000000000000e+00 -4.134563505649566650e-01 6.662052869796752930e-01 2.007689327001571655e-01 1.000000000000000000e+00 -4.057670235633850098e-01 6.597462296485900879e-01 1.958477497100830078e-01 1.000000000000000000e+00 -3.980776667594909668e-01 6.532871723175048828e-01 1.909265667200088501e-01 1.000000000000000000e+00 -3.903883099555969238e-01 6.468281149864196777e-01 1.860053837299346924e-01 1.000000000000000000e+00 -3.826989531517028809e-01 6.403691172599792480e-01 1.810842007398605347e-01 1.000000000000000000e+00 -3.750096261501312256e-01 6.339100599288940430e-01 1.761630177497863770e-01 1.000000000000000000e+00 -3.673202693462371826e-01 6.274510025978088379e-01 1.712418347597122192e-01 1.000000000000000000e+00 -3.596309125423431396e-01 6.209919452667236328e-01 1.663206517696380615e-01 1.000000000000000000e+00 -3.519415557384490967e-01 6.145328879356384277e-01 1.613994687795639038e-01 1.000000000000000000e+00 -3.442521989345550537e-01 6.080738306045532227e-01 1.564782708883285522e-01 1.000000000000000000e+00 -3.365628719329833984e-01 6.016147732734680176e-01 1.515570878982543945e-01 1.000000000000000000e+00 -3.288735151290893555e-01 5.951557159423828125e-01 1.466359049081802368e-01 1.000000000000000000e+00 -3.211841583251953125e-01 5.886966586112976074e-01 1.417147219181060791e-01 1.000000000000000000e+00 -3.134948015213012695e-01 5.822376012802124023e-01 1.367935389280319214e-01 1.000000000000000000e+00 -3.058054447174072266e-01 5.757785439491271973e-01 1.318723559379577637e-01 1.000000000000000000e+00 -2.990388274192810059e-01 5.690119266510009766e-01 1.287966221570968628e-01 1.000000000000000000e+00 -2.931949198246002197e-01 5.619376897811889648e-01 1.275663226842880249e-01 1.000000000000000000e+00 -2.873510122299194336e-01 5.548635125160217285e-01 1.263360232114791870e-01 1.000000000000000000e+00 -2.815071046352386475e-01 5.477893352508544922e-01 1.251057237386703491e-01 1.000000000000000000e+00 -2.756631970405578613e-01 5.407150983810424805e-01 1.238754317164421082e-01 1.000000000000000000e+00 -2.698192894458770752e-01 5.336409211158752441e-01 1.226451396942138672e-01 1.000000000000000000e+00 -2.639753818511962891e-01 5.265666842460632324e-01 1.214148402214050293e-01 1.000000000000000000e+00 -2.581314742565155029e-01 5.194925069808959961e-01 1.201845407485961914e-01 1.000000000000000000e+00 -2.522875964641571045e-01 5.124183297157287598e-01 1.189542487263679504e-01 1.000000000000000000e+00 -2.464436739683151245e-01 5.053440928459167480e-01 1.177239492535591125e-01 1.000000000000000000e+00 -2.405997663736343384e-01 4.982698857784271240e-01 1.164936572313308716e-01 1.000000000000000000e+00 -2.347558587789535522e-01 4.911957085132598877e-01 1.152633577585220337e-01 1.000000000000000000e+00 -2.289119511842727661e-01 4.841215014457702637e-01 1.140330657362937927e-01 1.000000000000000000e+00 -2.230680435895919800e-01 4.770472943782806396e-01 1.128027662634849548e-01 1.000000000000000000e+00 -2.172241508960723877e-01 4.699730873107910156e-01 1.115724742412567139e-01 1.000000000000000000e+00 -2.113802433013916016e-01 4.628988802433013916e-01 1.103421747684478760e-01 1.000000000000000000e+00 -2.055363357067108154e-01 4.558246731758117676e-01 1.091118827462196350e-01 1.000000000000000000e+00 -1.996924281120300293e-01 4.487504661083221436e-01 1.078815832734107971e-01 1.000000000000000000e+00 -1.938485205173492432e-01 4.416762888431549072e-01 1.066512912511825562e-01 1.000000000000000000e+00 -1.880046129226684570e-01 4.346020817756652832e-01 1.054209917783737183e-01 1.000000000000000000e+00 -1.821607053279876709e-01 4.275278747081756592e-01 1.041906923055648804e-01 1.000000000000000000e+00 -1.763167977333068848e-01 4.204536676406860352e-01 1.029604002833366394e-01 1.000000000000000000e+00 -1.704728901386260986e-01 4.133794605731964111e-01 1.017301008105278015e-01 1.000000000000000000e+00 -1.646289825439453125e-01 4.063052535057067871e-01 1.004998087882995605e-01 1.000000000000000000e+00 -1.587850898504257202e-01 3.992310762405395508e-01 9.926950931549072266e-02 1.000000000000000000e+00 -1.529411822557449341e-01 3.921568691730499268e-01 9.803921729326248169e-02 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/PuBu b/fastplotlib/utils/colormaps/PuBu deleted file mode 100644 index 92c3dd7e9..000000000 --- a/fastplotlib/utils/colormaps/PuBu +++ /dev/null @@ -1,256 +0,0 @@ -1.000000000000000000e+00 9.686274528503417969e-01 9.843137264251708984e-01 1.000000000000000000e+00 -9.976624250411987305e-01 9.666589498519897461e-01 9.832064509391784668e-01 1.000000000000000000e+00 -9.953248500823974609e-01 9.646905064582824707e-01 9.820991754531860352e-01 1.000000000000000000e+00 -9.929873347282409668e-01 9.627220034599304199e-01 9.809918999671936035e-01 1.000000000000000000e+00 -9.906497597694396973e-01 9.607535600662231445e-01 9.798846840858459473e-01 1.000000000000000000e+00 -9.883121848106384277e-01 9.587850570678710938e-01 9.787774085998535156e-01 1.000000000000000000e+00 -9.859746098518371582e-01 9.568166136741638184e-01 9.776701331138610840e-01 1.000000000000000000e+00 -9.836370348930358887e-01 9.548481106758117676e-01 9.765628576278686523e-01 1.000000000000000000e+00 -9.812995195388793945e-01 9.528796672821044922e-01 9.754555821418762207e-01 1.000000000000000000e+00 -9.789619445800781250e-01 9.509111642837524414e-01 9.743483066558837891e-01 1.000000000000000000e+00 -9.766243696212768555e-01 9.489427208900451660e-01 9.732410907745361328e-01 1.000000000000000000e+00 -9.742867946624755859e-01 9.469742178916931152e-01 9.721338152885437012e-01 1.000000000000000000e+00 -9.719492793083190918e-01 9.450057744979858398e-01 9.710265398025512695e-01 1.000000000000000000e+00 -9.696117043495178223e-01 9.430372714996337891e-01 9.699192643165588379e-01 1.000000000000000000e+00 -9.672741293907165527e-01 9.410688281059265137e-01 9.688119888305664062e-01 1.000000000000000000e+00 -9.649365544319152832e-01 9.391003251075744629e-01 9.677047133445739746e-01 1.000000000000000000e+00 -9.625989794731140137e-01 9.371318817138671875e-01 9.665974378585815430e-01 1.000000000000000000e+00 -9.602614641189575195e-01 9.351633787155151367e-01 9.654902219772338867e-01 1.000000000000000000e+00 -9.579238891601562500e-01 9.331949353218078613e-01 9.643829464912414551e-01 1.000000000000000000e+00 -9.555863142013549805e-01 9.312264323234558105e-01 9.632756710052490234e-01 1.000000000000000000e+00 -9.532487392425537109e-01 9.292579889297485352e-01 9.621683955192565918e-01 1.000000000000000000e+00 -9.509111642837524414e-01 9.272894859313964844e-01 9.610611200332641602e-01 1.000000000000000000e+00 -9.485736489295959473e-01 9.253210425376892090e-01 9.599538445472717285e-01 1.000000000000000000e+00 -9.462360739707946777e-01 9.233525395393371582e-01 9.588465690612792969e-01 1.000000000000000000e+00 -9.438984990119934082e-01 9.213840961456298828e-01 9.577393531799316406e-01 1.000000000000000000e+00 -9.415609240531921387e-01 9.194155931472778320e-01 9.566320776939392090e-01 1.000000000000000000e+00 -9.392233490943908691e-01 9.174471497535705566e-01 9.555248022079467773e-01 1.000000000000000000e+00 -9.368858337402343750e-01 9.154786467552185059e-01 9.544175267219543457e-01 1.000000000000000000e+00 -9.345482587814331055e-01 9.135102033615112305e-01 9.533102512359619141e-01 1.000000000000000000e+00 -9.322106838226318359e-01 9.115417003631591797e-01 9.522029757499694824e-01 1.000000000000000000e+00 -9.298731088638305664e-01 9.095732569694519043e-01 9.510957598686218262e-01 1.000000000000000000e+00 -9.275355339050292969e-01 9.076047539710998535e-01 9.499884843826293945e-01 1.000000000000000000e+00 -9.250596165657043457e-01 9.055440425872802734e-01 9.488350749015808105e-01 1.000000000000000000e+00 -9.216147661209106445e-01 9.028373956680297852e-01 9.473587274551391602e-01 1.000000000000000000e+00 -9.181699156761169434e-01 9.001307487487792969e-01 9.458823800086975098e-01 1.000000000000000000e+00 -9.147251248359680176e-01 8.974240422248840332e-01 9.444059729576110840e-01 1.000000000000000000e+00 -9.112802743911743164e-01 8.947173953056335449e-01 9.429296255111694336e-01 1.000000000000000000e+00 -9.078354239463806152e-01 8.920107483863830566e-01 9.414532780647277832e-01 1.000000000000000000e+00 -9.043906331062316895e-01 8.893041014671325684e-01 9.399769306182861328e-01 1.000000000000000000e+00 -9.009457826614379883e-01 8.865974545478820801e-01 9.385005831718444824e-01 1.000000000000000000e+00 -8.975009322166442871e-01 8.838908076286315918e-01 9.370242357254028320e-01 1.000000000000000000e+00 -8.940561413764953613e-01 8.811841607093811035e-01 9.355478882789611816e-01 1.000000000000000000e+00 -8.906112909317016602e-01 8.784775137901306152e-01 9.340714812278747559e-01 1.000000000000000000e+00 -8.871665000915527344e-01 8.757708668708801270e-01 9.325951337814331055e-01 1.000000000000000000e+00 -8.837216496467590332e-01 8.730642199516296387e-01 9.311187863349914551e-01 1.000000000000000000e+00 -8.802767992019653320e-01 8.703575730323791504e-01 9.296424388885498047e-01 1.000000000000000000e+00 -8.768320083618164062e-01 8.676509261131286621e-01 9.281660914421081543e-01 1.000000000000000000e+00 -8.733871579170227051e-01 8.649442791938781738e-01 9.266897439956665039e-01 1.000000000000000000e+00 -8.699423074722290039e-01 8.622375726699829102e-01 9.252133965492248535e-01 1.000000000000000000e+00 -8.664975166320800781e-01 8.595309257507324219e-01 9.237370491027832031e-01 1.000000000000000000e+00 -8.630526661872863770e-01 8.568242788314819336e-01 9.222606420516967773e-01 1.000000000000000000e+00 -8.596078157424926758e-01 8.541176319122314453e-01 9.207842946052551270e-01 1.000000000000000000e+00 -8.561630249023437500e-01 8.514109849929809570e-01 9.193079471588134766e-01 1.000000000000000000e+00 -8.527181744575500488e-01 8.487043380737304688e-01 9.178315997123718262e-01 1.000000000000000000e+00 -8.492733836174011230e-01 8.459976911544799805e-01 9.163552522659301758e-01 1.000000000000000000e+00 -8.458285331726074219e-01 8.432910442352294922e-01 9.148789048194885254e-01 1.000000000000000000e+00 -8.423836827278137207e-01 8.405843973159790039e-01 9.134025573730468750e-01 1.000000000000000000e+00 -8.389388918876647949e-01 8.378777503967285156e-01 9.119262099266052246e-01 1.000000000000000000e+00 -8.354940414428710938e-01 8.351711034774780273e-01 9.104498028755187988e-01 1.000000000000000000e+00 -8.320491909980773926e-01 8.324644565582275391e-01 9.089734554290771484e-01 1.000000000000000000e+00 -8.286044001579284668e-01 8.297578096389770508e-01 9.074971079826354980e-01 1.000000000000000000e+00 -8.251595497131347656e-01 8.270511627197265625e-01 9.060207605361938477e-01 1.000000000000000000e+00 -8.217146992683410645e-01 8.243444561958312988e-01 9.045444130897521973e-01 1.000000000000000000e+00 -8.182699084281921387e-01 8.216378092765808105e-01 9.030680656433105469e-01 1.000000000000000000e+00 -8.143944740295410156e-01 8.189926743507385254e-01 9.016224741935729980e-01 1.000000000000000000e+00 -8.092272281646728516e-01 8.165320754051208496e-01 9.002691507339477539e-01 1.000000000000000000e+00 -8.040599822998046875e-01 8.140715360641479492e-01 8.989158272743225098e-01 1.000000000000000000e+00 -7.988927364349365234e-01 8.116109371185302734e-01 8.975625038146972656e-01 1.000000000000000000e+00 -7.937254905700683594e-01 8.091503381729125977e-01 8.962091207504272461e-01 1.000000000000000000e+00 -7.885582447052001953e-01 8.066897392272949219e-01 8.948557972908020020e-01 1.000000000000000000e+00 -7.833909988403320312e-01 8.042291402816772461e-01 8.935024738311767578e-01 1.000000000000000000e+00 -7.782237529754638672e-01 8.017685413360595703e-01 8.921491503715515137e-01 1.000000000000000000e+00 -7.730565071105957031e-01 7.993079423904418945e-01 8.907958269119262695e-01 1.000000000000000000e+00 -7.678892612457275391e-01 7.968473434448242188e-01 8.894425034523010254e-01 1.000000000000000000e+00 -7.627220153808593750e-01 7.943868041038513184e-01 8.880891799926757812e-01 1.000000000000000000e+00 -7.575547695159912109e-01 7.919262051582336426e-01 8.867358565330505371e-01 1.000000000000000000e+00 -7.523875236511230469e-01 7.894656062126159668e-01 8.853825330734252930e-01 1.000000000000000000e+00 -7.472202777862548828e-01 7.870050072669982910e-01 8.840292096138000488e-01 1.000000000000000000e+00 -7.420530319213867188e-01 7.845444083213806152e-01 8.826758861541748047e-01 1.000000000000000000e+00 -7.368857860565185547e-01 7.820838093757629395e-01 8.813225626945495605e-01 1.000000000000000000e+00 -7.317185401916503906e-01 7.796232104301452637e-01 8.799692392349243164e-01 1.000000000000000000e+00 -7.265513539314270020e-01 7.771626114845275879e-01 8.786159157752990723e-01 1.000000000000000000e+00 -7.213841080665588379e-01 7.747020125389099121e-01 8.772625923156738281e-01 1.000000000000000000e+00 -7.162168622016906738e-01 7.722414731979370117e-01 8.759092688560485840e-01 1.000000000000000000e+00 -7.110496163368225098e-01 7.697808742523193359e-01 8.745559453964233398e-01 1.000000000000000000e+00 -7.058823704719543457e-01 7.673202753067016602e-01 8.732026219367980957e-01 1.000000000000000000e+00 -7.007151246070861816e-01 7.648596763610839844e-01 8.718492984771728516e-01 1.000000000000000000e+00 -6.955478787422180176e-01 7.623990774154663086e-01 8.704959750175476074e-01 1.000000000000000000e+00 -6.903806328773498535e-01 7.599384784698486328e-01 8.691426515579223633e-01 1.000000000000000000e+00 -6.852133870124816895e-01 7.574778795242309570e-01 8.677893280982971191e-01 1.000000000000000000e+00 -6.800461411476135254e-01 7.550172805786132812e-01 8.664360046386718750e-01 1.000000000000000000e+00 -6.748788952827453613e-01 7.525566816329956055e-01 8.650826811790466309e-01 1.000000000000000000e+00 -6.697116494178771973e-01 7.500961422920227051e-01 8.637293577194213867e-01 1.000000000000000000e+00 -6.645444035530090332e-01 7.476355433464050293e-01 8.623760342597961426e-01 1.000000000000000000e+00 -6.593771576881408691e-01 7.451749444007873535e-01 8.610227108001708984e-01 1.000000000000000000e+00 -6.542099118232727051e-01 7.427143454551696777e-01 8.596693873405456543e-01 1.000000000000000000e+00 -6.486735939979553223e-01 7.402537465095520020e-01 8.582698702812194824e-01 1.000000000000000000e+00 -6.425220966339111328e-01 7.377931475639343262e-01 8.567935228347778320e-01 1.000000000000000000e+00 -6.363705992698669434e-01 7.353325486183166504e-01 8.553171753883361816e-01 1.000000000000000000e+00 -6.302191615104675293e-01 7.328719496726989746e-01 8.538408279418945312e-01 1.000000000000000000e+00 -6.240676641464233398e-01 7.304113507270812988e-01 8.523644804954528809e-01 1.000000000000000000e+00 -6.179161667823791504e-01 7.279508113861083984e-01 8.508881330490112305e-01 1.000000000000000000e+00 -6.117647290229797363e-01 7.254902124404907227e-01 8.494117856025695801e-01 1.000000000000000000e+00 -6.056132316589355469e-01 7.230296134948730469e-01 8.479354381561279297e-01 1.000000000000000000e+00 -5.994617342948913574e-01 7.205690145492553711e-01 8.464590311050415039e-01 1.000000000000000000e+00 -5.933102369308471680e-01 7.181084156036376953e-01 8.449826836585998535e-01 1.000000000000000000e+00 -5.871587991714477539e-01 7.156478166580200195e-01 8.435063362121582031e-01 1.000000000000000000e+00 -5.810073018074035645e-01 7.131872177124023438e-01 8.420299887657165527e-01 1.000000000000000000e+00 -5.748558044433593750e-01 7.107266187667846680e-01 8.405536413192749023e-01 1.000000000000000000e+00 -5.687043666839599609e-01 7.082660794258117676e-01 8.390772938728332520e-01 1.000000000000000000e+00 -5.625528693199157715e-01 7.058054804801940918e-01 8.376009464263916016e-01 1.000000000000000000e+00 -5.564013719558715820e-01 7.033448815345764160e-01 8.361245393753051758e-01 1.000000000000000000e+00 -5.502498745918273926e-01 7.008842825889587402e-01 8.346481919288635254e-01 1.000000000000000000e+00 -5.440984368324279785e-01 6.984236836433410645e-01 8.331718444824218750e-01 1.000000000000000000e+00 -5.379469394683837891e-01 6.959630846977233887e-01 8.316954970359802246e-01 1.000000000000000000e+00 -5.317954421043395996e-01 6.935024857521057129e-01 8.302191495895385742e-01 1.000000000000000000e+00 -5.256440043449401855e-01 6.910418868064880371e-01 8.287428021430969238e-01 1.000000000000000000e+00 -5.194925069808959961e-01 6.885812878608703613e-01 8.272664546966552734e-01 1.000000000000000000e+00 -5.133410096168518066e-01 6.861207485198974609e-01 8.257901072502136230e-01 1.000000000000000000e+00 -5.071895718574523926e-01 6.836601495742797852e-01 8.243137001991271973e-01 1.000000000000000000e+00 -5.010380744934082031e-01 6.811995506286621094e-01 8.228373527526855469e-01 1.000000000000000000e+00 -4.948865771293640137e-01 6.787389516830444336e-01 8.213610053062438965e-01 1.000000000000000000e+00 -4.887351095676422119e-01 6.762783527374267578e-01 8.198846578598022461e-01 1.000000000000000000e+00 -4.825836122035980225e-01 6.738177537918090820e-01 8.184083104133605957e-01 1.000000000000000000e+00 -4.764321446418762207e-01 6.713571548461914062e-01 8.169319629669189453e-01 1.000000000000000000e+00 -4.702806472778320312e-01 6.688965559005737305e-01 8.154556155204772949e-01 1.000000000000000000e+00 -4.641291797161102295e-01 6.664359569549560547e-01 8.139792680740356445e-01 1.000000000000000000e+00 -4.579777121543884277e-01 6.639754176139831543e-01 8.125028610229492188e-01 1.000000000000000000e+00 -4.510880410671234131e-01 6.612071990966796875e-01 8.108419775962829590e-01 1.000000000000000000e+00 -4.434601962566375732e-01 6.581314802169799805e-01 8.089965581893920898e-01 1.000000000000000000e+00 -4.358323812484741211e-01 6.550557613372802734e-01 8.071510791778564453e-01 1.000000000000000000e+00 -4.282045364379882812e-01 6.519799828529357910e-01 8.053056597709655762e-01 1.000000000000000000e+00 -4.205766916275024414e-01 6.489042639732360840e-01 8.034601807594299316e-01 1.000000000000000000e+00 -4.129488766193389893e-01 6.458285450935363770e-01 8.016147613525390625e-01 1.000000000000000000e+00 -4.053210318088531494e-01 6.427527666091918945e-01 7.997693419456481934e-01 1.000000000000000000e+00 -3.976931869983673096e-01 6.396770477294921875e-01 7.979238629341125488e-01 1.000000000000000000e+00 -3.900653719902038574e-01 6.366013288497924805e-01 7.960784435272216797e-01 1.000000000000000000e+00 -3.824375271797180176e-01 6.335255503654479980e-01 7.942329645156860352e-01 1.000000000000000000e+00 -3.748096823692321777e-01 6.304498314857482910e-01 7.923875451087951660e-01 1.000000000000000000e+00 -3.671818673610687256e-01 6.273741126060485840e-01 7.905421257019042969e-01 1.000000000000000000e+00 -3.595540225505828857e-01 6.242983341217041016e-01 7.886966466903686523e-01 1.000000000000000000e+00 -3.519261777400970459e-01 6.212226152420043945e-01 7.868512272834777832e-01 1.000000000000000000e+00 -3.442983329296112061e-01 6.181468963623046875e-01 7.850057482719421387e-01 1.000000000000000000e+00 -3.366705179214477539e-01 6.150711178779602051e-01 7.831603288650512695e-01 1.000000000000000000e+00 -3.290426731109619141e-01 6.119953989982604980e-01 7.813148498535156250e-01 1.000000000000000000e+00 -3.214148283004760742e-01 6.089196205139160156e-01 7.794694304466247559e-01 1.000000000000000000e+00 -3.137870132923126221e-01 6.058439016342163086e-01 7.776240110397338867e-01 1.000000000000000000e+00 -3.061591684818267822e-01 6.027681827545166016e-01 7.757785320281982422e-01 1.000000000000000000e+00 -2.985313236713409424e-01 5.996924042701721191e-01 7.739331126213073730e-01 1.000000000000000000e+00 -2.909035086631774902e-01 5.966166853904724121e-01 7.720876336097717285e-01 1.000000000000000000e+00 -2.832756638526916504e-01 5.935409665107727051e-01 7.702422142028808594e-01 1.000000000000000000e+00 -2.756478190422058105e-01 5.904651880264282227e-01 7.683967947959899902e-01 1.000000000000000000e+00 -2.680200040340423584e-01 5.873894691467285156e-01 7.665513157844543457e-01 1.000000000000000000e+00 -2.603921592235565186e-01 5.843137502670288086e-01 7.647058963775634766e-01 1.000000000000000000e+00 -2.527643144130706787e-01 5.812379717826843262e-01 7.628604173660278320e-01 1.000000000000000000e+00 -2.451364845037460327e-01 5.781622529029846191e-01 7.610149979591369629e-01 1.000000000000000000e+00 -2.375086545944213867e-01 5.750865340232849121e-01 7.591695785522460938e-01 1.000000000000000000e+00 -2.298808097839355469e-01 5.720107555389404297e-01 7.573240995407104492e-01 1.000000000000000000e+00 -2.222529798746109009e-01 5.689350366592407227e-01 7.554786801338195801e-01 1.000000000000000000e+00 -2.146251499652862549e-01 5.658592581748962402e-01 7.536332011222839355e-01 1.000000000000000000e+00 -2.079969197511672974e-01 5.622453093528747559e-01 7.517108917236328125e-01 1.000000000000000000e+00 -2.019684761762619019e-01 5.583083629608154297e-01 7.497423887252807617e-01 1.000000000000000000e+00 -1.959400177001953125e-01 5.543714165687561035e-01 7.477739453315734863e-01 1.000000000000000000e+00 -1.899115741252899170e-01 5.504344701766967773e-01 7.458054423332214355e-01 1.000000000000000000e+00 -1.838831156492233276e-01 5.464975237846374512e-01 7.438369989395141602e-01 1.000000000000000000e+00 -1.778546720743179321e-01 5.425605773925781250e-01 7.418684959411621094e-01 1.000000000000000000e+00 -1.718262135982513428e-01 5.386236310005187988e-01 7.399000525474548340e-01 1.000000000000000000e+00 -1.657977700233459473e-01 5.346866846084594727e-01 7.379315495491027832e-01 1.000000000000000000e+00 -1.597693264484405518e-01 5.307497382164001465e-01 7.359631061553955078e-01 1.000000000000000000e+00 -1.537408679723739624e-01 5.268127918243408203e-01 7.339946031570434570e-01 1.000000000000000000e+00 -1.477124243974685669e-01 5.228758454322814941e-01 7.320261597633361816e-01 1.000000000000000000e+00 -1.416839659214019775e-01 5.189388990402221680e-01 7.300576567649841309e-01 1.000000000000000000e+00 -1.356555223464965820e-01 5.150018930435180664e-01 7.280892133712768555e-01 1.000000000000000000e+00 -1.296270638704299927e-01 5.110649466514587402e-01 7.261207103729248047e-01 1.000000000000000000e+00 -1.235986128449440002e-01 5.071280002593994141e-01 7.241522669792175293e-01 1.000000000000000000e+00 -1.175701618194580078e-01 5.031910538673400879e-01 7.221837639808654785e-01 1.000000000000000000e+00 -1.115417182445526123e-01 4.992541372776031494e-01 7.202153205871582031e-01 1.000000000000000000e+00 -1.055132672190666199e-01 4.953171908855438232e-01 7.182468175888061523e-01 1.000000000000000000e+00 -9.948481619358062744e-02 4.913802444934844971e-01 7.162783741950988770e-01 1.000000000000000000e+00 -9.345636516809463501e-02 4.874432981014251709e-01 7.143098711967468262e-01 1.000000000000000000e+00 -8.742791414260864258e-02 4.835063517093658447e-01 7.123414278030395508e-01 1.000000000000000000e+00 -8.139946311712265015e-02 4.795694053173065186e-01 7.103729248046875000e-01 1.000000000000000000e+00 -7.537101209163665771e-02 4.756324589252471924e-01 7.084044814109802246e-01 1.000000000000000000e+00 -6.934256106615066528e-02 4.716955125331878662e-01 7.064359784126281738e-01 1.000000000000000000e+00 -6.331411004066467285e-02 4.677585661411285400e-01 7.044675350189208984e-01 1.000000000000000000e+00 -5.728565901517868042e-02 4.638216197490692139e-01 7.024990320205688477e-01 1.000000000000000000e+00 -5.125720798969268799e-02 4.598846733570098877e-01 7.005305886268615723e-01 1.000000000000000000e+00 -4.522875696420669556e-02 4.559477269649505615e-01 6.985620856285095215e-01 1.000000000000000000e+00 -3.920030593872070312e-02 4.520107507705688477e-01 6.965936422348022461e-01 1.000000000000000000e+00 -3.317185863852500916e-02 4.480738043785095215e-01 6.946251392364501953e-01 1.000000000000000000e+00 -2.714340575039386749e-02 4.441368579864501953e-01 6.926566958427429199e-01 1.000000000000000000e+00 -2.111495658755302429e-02 4.401999115943908691e-01 6.906881928443908691e-01 1.000000000000000000e+00 -1.951557025313377380e-02 4.371857047080993652e-01 6.869665384292602539e-01 1.000000000000000000e+00 -1.939254067838191986e-02 4.344790577888488770e-01 6.826605200767517090e-01 1.000000000000000000e+00 -1.926951110363006592e-02 4.317723810672760010e-01 6.783545017242431641e-01 1.000000000000000000e+00 -1.914648152887821198e-02 4.290657341480255127e-01 6.740484237670898438e-01 1.000000000000000000e+00 -1.902345195412635803e-02 4.263590872287750244e-01 6.697424054145812988e-01 1.000000000000000000e+00 -1.890042237937450409e-02 4.236524403095245361e-01 6.654363870620727539e-01 1.000000000000000000e+00 -1.877739280462265015e-02 4.209457933902740479e-01 6.611303091049194336e-01 1.000000000000000000e+00 -1.865436322987079620e-02 4.182391464710235596e-01 6.568242907524108887e-01 1.000000000000000000e+00 -1.853133365511894226e-02 4.155324995517730713e-01 6.525182723999023438e-01 1.000000000000000000e+00 -1.840830408036708832e-02 4.128258228302001953e-01 6.482122540473937988e-01 1.000000000000000000e+00 -1.828527450561523438e-02 4.101191759109497070e-01 6.439061760902404785e-01 1.000000000000000000e+00 -1.816224493086338043e-02 4.074125289916992188e-01 6.396001577377319336e-01 1.000000000000000000e+00 -1.803921535611152649e-02 4.047058820724487305e-01 6.352941393852233887e-01 1.000000000000000000e+00 -1.791618578135967255e-02 4.019992351531982422e-01 6.309880614280700684e-01 1.000000000000000000e+00 -1.779315620660781860e-02 3.992925882339477539e-01 6.266820430755615234e-01 1.000000000000000000e+00 -1.767012663185596466e-02 3.965859413146972656e-01 6.223760247230529785e-01 1.000000000000000000e+00 -1.754709705710411072e-02 3.938792645931243896e-01 6.180699467658996582e-01 1.000000000000000000e+00 -1.742406748235225677e-02 3.911726176738739014e-01 6.137639284133911133e-01 1.000000000000000000e+00 -1.730103790760040283e-02 3.884659707546234131e-01 6.094579100608825684e-01 1.000000000000000000e+00 -1.717800833284854889e-02 3.857593238353729248e-01 6.051518917083740234e-01 1.000000000000000000e+00 -1.705497875809669495e-02 3.830526769161224365e-01 6.008458137512207031e-01 1.000000000000000000e+00 -1.693194918334484100e-02 3.803460299968719482e-01 5.965397953987121582e-01 1.000000000000000000e+00 -1.680891960859298706e-02 3.776393830776214600e-01 5.922337770462036133e-01 1.000000000000000000e+00 -1.668589003384113312e-02 3.749327063560485840e-01 5.879276990890502930e-01 1.000000000000000000e+00 -1.656286045908927917e-02 3.722260594367980957e-01 5.836216807365417480e-01 1.000000000000000000e+00 -1.643983088433742523e-02 3.695194125175476074e-01 5.793156623840332031e-01 1.000000000000000000e+00 -1.631680130958557129e-02 3.668127655982971191e-01 5.750095844268798828e-01 1.000000000000000000e+00 -1.619377173483371735e-02 3.641061186790466309e-01 5.707035660743713379e-01 1.000000000000000000e+00 -1.607074216008186340e-02 3.613994717597961426e-01 5.663975477218627930e-01 1.000000000000000000e+00 -1.594771258533000946e-02 3.586928248405456543e-01 5.620915293693542480e-01 1.000000000000000000e+00 -1.582468301057815552e-02 3.559861481189727783e-01 5.577854514122009277e-01 1.000000000000000000e+00 -1.570165343582630157e-02 3.532795011997222900e-01 5.534794330596923828e-01 1.000000000000000000e+00 -1.547097228467464447e-02 3.492810428142547607e-01 5.472356677055358887e-01 1.000000000000000000e+00 -1.522491313517093658e-02 3.450980484485626221e-01 5.407150983810424805e-01 1.000000000000000000e+00 -1.497885398566722870e-02 3.409150242805480957e-01 5.341945290565490723e-01 1.000000000000000000e+00 -1.473279483616352081e-02 3.367320299148559570e-01 5.276739597320556641e-01 1.000000000000000000e+00 -1.448673568665981293e-02 3.325490057468414307e-01 5.211533904075622559e-01 1.000000000000000000e+00 -1.424067653715610504e-02 3.283660113811492920e-01 5.146328210830688477e-01 1.000000000000000000e+00 -1.399461738765239716e-02 3.241830170154571533e-01 5.081122517585754395e-01 1.000000000000000000e+00 -1.374855823814868927e-02 3.199999928474426270e-01 5.015916824340820312e-01 1.000000000000000000e+00 -1.350249908864498138e-02 3.158169984817504883e-01 4.950711131095886230e-01 1.000000000000000000e+00 -1.325643993914127350e-02 3.116339743137359619e-01 4.885505437850952148e-01 1.000000000000000000e+00 -1.301038078963756561e-02 3.074509799480438232e-01 4.820299744606018066e-01 1.000000000000000000e+00 -1.276432164013385773e-02 3.032679855823516846e-01 4.755094051361083984e-01 1.000000000000000000e+00 -1.251826249063014984e-02 2.990849614143371582e-01 4.689888358116149902e-01 1.000000000000000000e+00 -1.227220334112644196e-02 2.949019670486450195e-01 4.624682962894439697e-01 1.000000000000000000e+00 -1.202614419162273407e-02 2.907189428806304932e-01 4.559477269649505615e-01 1.000000000000000000e+00 -1.178008504211902618e-02 2.865359485149383545e-01 4.494271576404571533e-01 1.000000000000000000e+00 -1.153402496129274368e-02 2.823529541492462158e-01 4.429065883159637451e-01 1.000000000000000000e+00 -1.128796581178903580e-02 2.781699299812316895e-01 4.363860189914703369e-01 1.000000000000000000e+00 -1.104190666228532791e-02 2.739869356155395508e-01 4.298654496669769287e-01 1.000000000000000000e+00 -1.079584751278162003e-02 2.698039114475250244e-01 4.233448803424835205e-01 1.000000000000000000e+00 -1.054978836327791214e-02 2.656209170818328857e-01 4.168243110179901123e-01 1.000000000000000000e+00 -1.030372921377420425e-02 2.614379227161407471e-01 4.103037416934967041e-01 1.000000000000000000e+00 -1.005767006427049637e-02 2.572548985481262207e-01 4.037831723690032959e-01 1.000000000000000000e+00 -9.811610914766788483e-03 2.530719041824340820e-01 3.972626030445098877e-01 1.000000000000000000e+00 -9.565551765263080597e-03 2.488888949155807495e-01 3.907420337200164795e-01 1.000000000000000000e+00 -9.319492615759372711e-03 2.447058856487274170e-01 3.842214643955230713e-01 1.000000000000000000e+00 -9.073433466255664825e-03 2.405228763818740845e-01 3.777008950710296631e-01 1.000000000000000000e+00 -8.827374316751956940e-03 2.363398671150207520e-01 3.711803257465362549e-01 1.000000000000000000e+00 -8.581315167248249054e-03 2.321568578481674194e-01 3.646597564220428467e-01 1.000000000000000000e+00 -8.335256017744541168e-03 2.279738634824752808e-01 3.581391870975494385e-01 1.000000000000000000e+00 -8.089196868240833282e-03 2.237908542156219482e-01 3.516186177730560303e-01 1.000000000000000000e+00 -7.843137718737125397e-03 2.196078449487686157e-01 3.450980484485626221e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/PuBuGn b/fastplotlib/utils/colormaps/PuBuGn deleted file mode 100644 index 1c70e5147..000000000 --- a/fastplotlib/utils/colormaps/PuBuGn +++ /dev/null @@ -1,256 +0,0 @@ -1.000000000000000000e+00 9.686274528503417969e-01 9.843137264251708984e-01 1.000000000000000000e+00 -9.976624250411987305e-01 9.660438299179077148e-01 9.829604029655456543e-01 1.000000000000000000e+00 -9.953248500823974609e-01 9.634602069854736328e-01 9.816070795059204102e-01 1.000000000000000000e+00 -9.929873347282409668e-01 9.608765840530395508e-01 9.802537560462951660e-01 1.000000000000000000e+00 -9.906497597694396973e-01 9.582929611206054688e-01 9.789004325866699219e-01 1.000000000000000000e+00 -9.883121848106384277e-01 9.557093381881713867e-01 9.775471091270446777e-01 1.000000000000000000e+00 -9.859746098518371582e-01 9.531257152557373047e-01 9.761937856674194336e-01 1.000000000000000000e+00 -9.836370348930358887e-01 9.505420923233032227e-01 9.748404622077941895e-01 1.000000000000000000e+00 -9.812995195388793945e-01 9.479584693908691406e-01 9.734871387481689453e-01 1.000000000000000000e+00 -9.789619445800781250e-01 9.453748464584350586e-01 9.721338152885437012e-01 1.000000000000000000e+00 -9.766243696212768555e-01 9.427912235260009766e-01 9.707804918289184570e-01 1.000000000000000000e+00 -9.742867946624755859e-01 9.402076005935668945e-01 9.694271683692932129e-01 1.000000000000000000e+00 -9.719492793083190918e-01 9.376239776611328125e-01 9.680738449096679688e-01 1.000000000000000000e+00 -9.696117043495178223e-01 9.350403547286987305e-01 9.667205214500427246e-01 1.000000000000000000e+00 -9.672741293907165527e-01 9.324567317962646484e-01 9.653671383857727051e-01 1.000000000000000000e+00 -9.649365544319152832e-01 9.298731088638305664e-01 9.640138149261474609e-01 1.000000000000000000e+00 -9.625989794731140137e-01 9.272894859313964844e-01 9.626604914665222168e-01 1.000000000000000000e+00 -9.602614641189575195e-01 9.247058629989624023e-01 9.613071680068969727e-01 1.000000000000000000e+00 -9.579238891601562500e-01 9.221222400665283203e-01 9.599538445472717285e-01 1.000000000000000000e+00 -9.555863142013549805e-01 9.195386171340942383e-01 9.586005210876464844e-01 1.000000000000000000e+00 -9.532487392425537109e-01 9.169549942016601562e-01 9.572471976280212402e-01 1.000000000000000000e+00 -9.509111642837524414e-01 9.143713712692260742e-01 9.558938741683959961e-01 1.000000000000000000e+00 -9.485736489295959473e-01 9.117877483367919922e-01 9.545405507087707520e-01 1.000000000000000000e+00 -9.462360739707946777e-01 9.092041254043579102e-01 9.531872272491455078e-01 1.000000000000000000e+00 -9.438984990119934082e-01 9.066205024719238281e-01 9.518339037895202637e-01 1.000000000000000000e+00 -9.415609240531921387e-01 9.040368795394897461e-01 9.504805803298950195e-01 1.000000000000000000e+00 -9.392233490943908691e-01 9.014533162117004395e-01 9.491272568702697754e-01 1.000000000000000000e+00 -9.368858337402343750e-01 8.988696932792663574e-01 9.477739334106445312e-01 1.000000000000000000e+00 -9.345482587814331055e-01 8.962860703468322754e-01 9.464206099510192871e-01 1.000000000000000000e+00 -9.322106838226318359e-01 8.937024474143981934e-01 9.450672864913940430e-01 1.000000000000000000e+00 -9.298731088638305664e-01 8.911188244819641113e-01 9.437139630317687988e-01 1.000000000000000000e+00 -9.275355339050292969e-01 8.885352015495300293e-01 9.423606395721435547e-01 1.000000000000000000e+00 -9.250596165657043457e-01 8.860130906105041504e-01 9.410226941108703613e-01 1.000000000000000000e+00 -9.216147661209106445e-01 8.839215636253356934e-01 9.397923946380615234e-01 1.000000000000000000e+00 -9.181699156761169434e-01 8.818300366401672363e-01 9.385620951652526855e-01 1.000000000000000000e+00 -9.147251248359680176e-01 8.797385692596435547e-01 9.373317956924438477e-01 1.000000000000000000e+00 -9.112802743911743164e-01 8.776470422744750977e-01 9.361014962196350098e-01 1.000000000000000000e+00 -9.078354239463806152e-01 8.755555748939514160e-01 9.348711967468261719e-01 1.000000000000000000e+00 -9.043906331062316895e-01 8.734640479087829590e-01 9.336408972740173340e-01 1.000000000000000000e+00 -9.009457826614379883e-01 8.713725209236145020e-01 9.324105978012084961e-01 1.000000000000000000e+00 -8.975009322166442871e-01 8.692810535430908203e-01 9.311802983283996582e-01 1.000000000000000000e+00 -8.940561413764953613e-01 8.671895265579223633e-01 9.299499988555908203e-01 1.000000000000000000e+00 -8.906112909317016602e-01 8.650980591773986816e-01 9.287196993827819824e-01 1.000000000000000000e+00 -8.871665000915527344e-01 8.630065321922302246e-01 9.274893999099731445e-01 1.000000000000000000e+00 -8.837216496467590332e-01 8.609150052070617676e-01 9.262591600418090820e-01 1.000000000000000000e+00 -8.802767992019653320e-01 8.588235378265380859e-01 9.250288605690002441e-01 1.000000000000000000e+00 -8.768320083618164062e-01 8.567320108413696289e-01 9.237985610961914062e-01 1.000000000000000000e+00 -8.733871579170227051e-01 8.546405434608459473e-01 9.225682616233825684e-01 1.000000000000000000e+00 -8.699423074722290039e-01 8.525490164756774902e-01 9.213379621505737305e-01 1.000000000000000000e+00 -8.664975166320800781e-01 8.504574894905090332e-01 9.201076626777648926e-01 1.000000000000000000e+00 -8.630526661872863770e-01 8.483660221099853516e-01 9.188773632049560547e-01 1.000000000000000000e+00 -8.596078157424926758e-01 8.462744951248168945e-01 9.176470637321472168e-01 1.000000000000000000e+00 -8.561630249023437500e-01 8.441830277442932129e-01 9.164167642593383789e-01 1.000000000000000000e+00 -8.527181744575500488e-01 8.420915007591247559e-01 9.151864647865295410e-01 1.000000000000000000e+00 -8.492733836174011230e-01 8.399999737739562988e-01 9.139561653137207031e-01 1.000000000000000000e+00 -8.458285331726074219e-01 8.379085063934326172e-01 9.127258658409118652e-01 1.000000000000000000e+00 -8.423836827278137207e-01 8.358169794082641602e-01 9.114955663681030273e-01 1.000000000000000000e+00 -8.389388918876647949e-01 8.337255120277404785e-01 9.102652668952941895e-01 1.000000000000000000e+00 -8.354940414428710938e-01 8.316339850425720215e-01 9.090349674224853516e-01 1.000000000000000000e+00 -8.320491909980773926e-01 8.295424580574035645e-01 9.078046679496765137e-01 1.000000000000000000e+00 -8.286044001579284668e-01 8.274509906768798828e-01 9.065743684768676758e-01 1.000000000000000000e+00 -8.251595497131347656e-01 8.253594636917114258e-01 9.053440690040588379e-01 1.000000000000000000e+00 -8.217146992683410645e-01 8.232679963111877441e-01 9.041138291358947754e-01 1.000000000000000000e+00 -8.182699084281921387e-01 8.211764693260192871e-01 9.028835296630859375e-01 1.000000000000000000e+00 -8.143944740295410156e-01 8.189926743507385254e-01 9.016224741935729980e-01 1.000000000000000000e+00 -8.092272281646728516e-01 8.165320754051208496e-01 9.002691507339477539e-01 1.000000000000000000e+00 -8.040599822998046875e-01 8.140715360641479492e-01 8.989158272743225098e-01 1.000000000000000000e+00 -7.988927364349365234e-01 8.116109371185302734e-01 8.975625038146972656e-01 1.000000000000000000e+00 -7.937254905700683594e-01 8.091503381729125977e-01 8.962091207504272461e-01 1.000000000000000000e+00 -7.885582447052001953e-01 8.066897392272949219e-01 8.948557972908020020e-01 1.000000000000000000e+00 -7.833909988403320312e-01 8.042291402816772461e-01 8.935024738311767578e-01 1.000000000000000000e+00 -7.782237529754638672e-01 8.017685413360595703e-01 8.921491503715515137e-01 1.000000000000000000e+00 -7.730565071105957031e-01 7.993079423904418945e-01 8.907958269119262695e-01 1.000000000000000000e+00 -7.678892612457275391e-01 7.968473434448242188e-01 8.894425034523010254e-01 1.000000000000000000e+00 -7.627220153808593750e-01 7.943868041038513184e-01 8.880891799926757812e-01 1.000000000000000000e+00 -7.575547695159912109e-01 7.919262051582336426e-01 8.867358565330505371e-01 1.000000000000000000e+00 -7.523875236511230469e-01 7.894656062126159668e-01 8.853825330734252930e-01 1.000000000000000000e+00 -7.472202777862548828e-01 7.870050072669982910e-01 8.840292096138000488e-01 1.000000000000000000e+00 -7.420530319213867188e-01 7.845444083213806152e-01 8.826758861541748047e-01 1.000000000000000000e+00 -7.368857860565185547e-01 7.820838093757629395e-01 8.813225626945495605e-01 1.000000000000000000e+00 -7.317185401916503906e-01 7.796232104301452637e-01 8.799692392349243164e-01 1.000000000000000000e+00 -7.265513539314270020e-01 7.771626114845275879e-01 8.786159157752990723e-01 1.000000000000000000e+00 -7.213841080665588379e-01 7.747020125389099121e-01 8.772625923156738281e-01 1.000000000000000000e+00 -7.162168622016906738e-01 7.722414731979370117e-01 8.759092688560485840e-01 1.000000000000000000e+00 -7.110496163368225098e-01 7.697808742523193359e-01 8.745559453964233398e-01 1.000000000000000000e+00 -7.058823704719543457e-01 7.673202753067016602e-01 8.732026219367980957e-01 1.000000000000000000e+00 -7.007151246070861816e-01 7.648596763610839844e-01 8.718492984771728516e-01 1.000000000000000000e+00 -6.955478787422180176e-01 7.623990774154663086e-01 8.704959750175476074e-01 1.000000000000000000e+00 -6.903806328773498535e-01 7.599384784698486328e-01 8.691426515579223633e-01 1.000000000000000000e+00 -6.852133870124816895e-01 7.574778795242309570e-01 8.677893280982971191e-01 1.000000000000000000e+00 -6.800461411476135254e-01 7.550172805786132812e-01 8.664360046386718750e-01 1.000000000000000000e+00 -6.748788952827453613e-01 7.525566816329956055e-01 8.650826811790466309e-01 1.000000000000000000e+00 -6.697116494178771973e-01 7.500961422920227051e-01 8.637293577194213867e-01 1.000000000000000000e+00 -6.645444035530090332e-01 7.476355433464050293e-01 8.623760342597961426e-01 1.000000000000000000e+00 -6.593771576881408691e-01 7.451749444007873535e-01 8.610227108001708984e-01 1.000000000000000000e+00 -6.542099118232727051e-01 7.427143454551696777e-01 8.596693873405456543e-01 1.000000000000000000e+00 -6.480737924575805664e-01 7.402537465095520020e-01 8.582698702812194824e-01 1.000000000000000000e+00 -6.403229236602783203e-01 7.377931475639343262e-01 8.567935228347778320e-01 1.000000000000000000e+00 -6.325721144676208496e-01 7.353325486183166504e-01 8.553171753883361816e-01 1.000000000000000000e+00 -6.248212456703186035e-01 7.328719496726989746e-01 8.538408279418945312e-01 1.000000000000000000e+00 -6.170703768730163574e-01 7.304113507270812988e-01 8.523644804954528809e-01 1.000000000000000000e+00 -6.093195080757141113e-01 7.279508113861083984e-01 8.508881330490112305e-01 1.000000000000000000e+00 -6.015686392784118652e-01 7.254902124404907227e-01 8.494117856025695801e-01 1.000000000000000000e+00 -5.938177704811096191e-01 7.230296134948730469e-01 8.479354381561279297e-01 1.000000000000000000e+00 -5.860669016838073730e-01 7.205690145492553711e-01 8.464590311050415039e-01 1.000000000000000000e+00 -5.783160328865051270e-01 7.181084156036376953e-01 8.449826836585998535e-01 1.000000000000000000e+00 -5.705651640892028809e-01 7.156478166580200195e-01 8.435063362121582031e-01 1.000000000000000000e+00 -5.628142952919006348e-01 7.131872177124023438e-01 8.420299887657165527e-01 1.000000000000000000e+00 -5.550634264945983887e-01 7.107266187667846680e-01 8.405536413192749023e-01 1.000000000000000000e+00 -5.473125576972961426e-01 7.082660794258117676e-01 8.390772938728332520e-01 1.000000000000000000e+00 -5.395616888999938965e-01 7.058054804801940918e-01 8.376009464263916016e-01 1.000000000000000000e+00 -5.318108201026916504e-01 7.033448815345764160e-01 8.361245393753051758e-01 1.000000000000000000e+00 -5.240599513053894043e-01 7.008842825889587402e-01 8.346481919288635254e-01 1.000000000000000000e+00 -5.163090825080871582e-01 6.984236836433410645e-01 8.331718444824218750e-01 1.000000000000000000e+00 -5.085582733154296875e-01 6.959630846977233887e-01 8.316954970359802246e-01 1.000000000000000000e+00 -5.008074045181274414e-01 6.935024857521057129e-01 8.302191495895385742e-01 1.000000000000000000e+00 -4.930565059185028076e-01 6.910418868064880371e-01 8.287428021430969238e-01 1.000000000000000000e+00 -4.853056371212005615e-01 6.885812878608703613e-01 8.272664546966552734e-01 1.000000000000000000e+00 -4.775547981262207031e-01 6.861207485198974609e-01 8.257901072502136230e-01 1.000000000000000000e+00 -4.698039293289184570e-01 6.836601495742797852e-01 8.243137001991271973e-01 1.000000000000000000e+00 -4.620530605316162109e-01 6.811995506286621094e-01 8.228373527526855469e-01 1.000000000000000000e+00 -4.543021917343139648e-01 6.787389516830444336e-01 8.213610053062438965e-01 1.000000000000000000e+00 -4.465513229370117188e-01 6.762783527374267578e-01 8.198846578598022461e-01 1.000000000000000000e+00 -4.388004541397094727e-01 6.738177537918090820e-01 8.184083104133605957e-01 1.000000000000000000e+00 -4.310495853424072266e-01 6.713571548461914062e-01 8.169319629669189453e-01 1.000000000000000000e+00 -4.232987165451049805e-01 6.688965559005737305e-01 8.154556155204772949e-01 1.000000000000000000e+00 -4.155478775501251221e-01 6.664359569549560547e-01 8.139792680740356445e-01 1.000000000000000000e+00 -4.077970087528228760e-01 6.639754176139831543e-01 8.125028610229492188e-01 1.000000000000000000e+00 -4.009073376655578613e-01 6.612071990966796875e-01 8.108419775962829590e-01 1.000000000000000000e+00 -3.948788940906524658e-01 6.581314802169799805e-01 8.089965581893920898e-01 1.000000000000000000e+00 -3.888504505157470703e-01 6.550557613372802734e-01 8.071510791778564453e-01 1.000000000000000000e+00 -3.828219771385192871e-01 6.519799828529357910e-01 8.053056597709655762e-01 1.000000000000000000e+00 -3.767935335636138916e-01 6.489042639732360840e-01 8.034601807594299316e-01 1.000000000000000000e+00 -3.707650899887084961e-01 6.458285450935363770e-01 8.016147613525390625e-01 1.000000000000000000e+00 -3.647366464138031006e-01 6.427527666091918945e-01 7.997693419456481934e-01 1.000000000000000000e+00 -3.587082028388977051e-01 6.396770477294921875e-01 7.979238629341125488e-01 1.000000000000000000e+00 -3.526797294616699219e-01 6.366013288497924805e-01 7.960784435272216797e-01 1.000000000000000000e+00 -3.466512858867645264e-01 6.335255503654479980e-01 7.942329645156860352e-01 1.000000000000000000e+00 -3.406228423118591309e-01 6.304498314857482910e-01 7.923875451087951660e-01 1.000000000000000000e+00 -3.345943987369537354e-01 6.273741126060485840e-01 7.905421257019042969e-01 1.000000000000000000e+00 -3.285659253597259521e-01 6.242983341217041016e-01 7.886966466903686523e-01 1.000000000000000000e+00 -3.225374817848205566e-01 6.212226152420043945e-01 7.868512272834777832e-01 1.000000000000000000e+00 -3.165090382099151611e-01 6.181468963623046875e-01 7.850057482719421387e-01 1.000000000000000000e+00 -3.104805946350097656e-01 6.150711178779602051e-01 7.831603288650512695e-01 1.000000000000000000e+00 -3.044521212577819824e-01 6.119953989982604980e-01 7.813148498535156250e-01 1.000000000000000000e+00 -2.984236776828765869e-01 6.089196205139160156e-01 7.794694304466247559e-01 1.000000000000000000e+00 -2.923952341079711914e-01 6.058439016342163086e-01 7.776240110397338867e-01 1.000000000000000000e+00 -2.863667905330657959e-01 6.027681827545166016e-01 7.757785320281982422e-01 1.000000000000000000e+00 -2.803383171558380127e-01 5.996924042701721191e-01 7.739331126213073730e-01 1.000000000000000000e+00 -2.743098735809326172e-01 5.966166853904724121e-01 7.720876336097717285e-01 1.000000000000000000e+00 -2.682814300060272217e-01 5.935409665107727051e-01 7.702422142028808594e-01 1.000000000000000000e+00 -2.622529864311218262e-01 5.904651880264282227e-01 7.683967947959899902e-01 1.000000000000000000e+00 -2.562245428562164307e-01 5.873894691467285156e-01 7.665513157844543457e-01 1.000000000000000000e+00 -2.501960694789886475e-01 5.843137502670288086e-01 7.647058963775634766e-01 1.000000000000000000e+00 -2.441676259040832520e-01 5.812379717826843262e-01 7.628604173660278320e-01 1.000000000000000000e+00 -2.381391823291778564e-01 5.781622529029846191e-01 7.610149979591369629e-01 1.000000000000000000e+00 -2.321107238531112671e-01 5.750865340232849121e-01 7.591695785522460938e-01 1.000000000000000000e+00 -2.260822802782058716e-01 5.720107555389404297e-01 7.573240995407104492e-01 1.000000000000000000e+00 -2.200538218021392822e-01 5.689350366592407227e-01 7.554786801338195801e-01 1.000000000000000000e+00 -2.140253782272338867e-01 5.658592581748962402e-01 7.536332011222839355e-01 1.000000000000000000e+00 -2.077662497758865356e-01 5.635524988174438477e-01 7.487889528274536133e-01 1.000000000000000000e+00 -2.013687044382095337e-01 5.617070198059082031e-01 7.421452999114990234e-01 1.000000000000000000e+00 -1.949711591005325317e-01 5.598616003990173340e-01 7.355017066001892090e-01 1.000000000000000000e+00 -1.885736286640167236e-01 5.580161213874816895e-01 7.288581132888793945e-01 1.000000000000000000e+00 -1.821760833263397217e-01 5.561707019805908203e-01 7.222145199775695801e-01 1.000000000000000000e+00 -1.757785528898239136e-01 5.543252825736999512e-01 7.155709266662597656e-01 1.000000000000000000e+00 -1.693810075521469116e-01 5.524798035621643066e-01 7.089273333549499512e-01 1.000000000000000000e+00 -1.629834622144699097e-01 5.506343841552734375e-01 7.022837400436401367e-01 1.000000000000000000e+00 -1.565859317779541016e-01 5.487889051437377930e-01 6.956401467323303223e-01 1.000000000000000000e+00 -1.501883864402770996e-01 5.469434857368469238e-01 6.889965534210205078e-01 1.000000000000000000e+00 -1.437908560037612915e-01 5.450980663299560547e-01 6.823529601097106934e-01 1.000000000000000000e+00 -1.373933106660842896e-01 5.432525873184204102e-01 6.757093667984008789e-01 1.000000000000000000e+00 -1.309957653284072876e-01 5.414071679115295410e-01 6.690657734870910645e-01 1.000000000000000000e+00 -1.245982348918914795e-01 5.395616888999938965e-01 6.624221205711364746e-01 1.000000000000000000e+00 -1.182006895542144775e-01 5.377162694931030273e-01 6.557785272598266602e-01 1.000000000000000000e+00 -1.118031516671180725e-01 5.358707904815673828e-01 6.491349339485168457e-01 1.000000000000000000e+00 -1.054056137800216675e-01 5.340253710746765137e-01 6.424913406372070312e-01 1.000000000000000000e+00 -9.900807589292526245e-02 5.321799516677856445e-01 6.358477473258972168e-01 1.000000000000000000e+00 -9.261053800582885742e-02 5.303344726562500000e-01 6.292041540145874023e-01 1.000000000000000000e+00 -8.621299266815185547e-02 5.284890532493591309e-01 6.225605607032775879e-01 1.000000000000000000e+00 -7.981545478105545044e-02 5.266435742378234863e-01 6.159169673919677734e-01 1.000000000000000000e+00 -7.341791689395904541e-02 5.247981548309326172e-01 6.092733740806579590e-01 1.000000000000000000e+00 -6.702037900686264038e-02 5.229527354240417480e-01 6.026297807693481445e-01 1.000000000000000000e+00 -6.062283739447593689e-02 5.211072564125061035e-01 5.959861874580383301e-01 1.000000000000000000e+00 -5.422529950737953186e-02 5.192618370056152344e-01 5.893425345420837402e-01 1.000000000000000000e+00 -4.782775789499282837e-02 5.174163579940795898e-01 5.826989412307739258e-01 1.000000000000000000e+00 -4.143022000789642334e-02 5.155709385871887207e-01 5.760553479194641113e-01 1.000000000000000000e+00 -3.503267839550971985e-02 5.137255191802978516e-01 5.694117546081542969e-01 1.000000000000000000e+00 -2.863514050841331482e-02 5.118800401687622070e-01 5.627681612968444824e-01 1.000000000000000000e+00 -2.223760075867176056e-02 5.100346207618713379e-01 5.561245679855346680e-01 1.000000000000000000e+00 -1.584006100893020630e-02 5.081891417503356934e-01 5.494809746742248535e-01 1.000000000000000000e+00 -9.442522190511226654e-03 5.063437223434448242e-01 5.428373813629150391e-01 1.000000000000000000e+00 -7.750865072011947632e-03 5.039446353912353516e-01 5.366551280021667480e-01 1.000000000000000000e+00 -7.627835497260093689e-03 5.013610124588012695e-01 5.306266546249389648e-01 1.000000000000000000e+00 -7.504805922508239746e-03 4.987773895263671875e-01 5.245982408523559570e-01 1.000000000000000000e+00 -7.381776347756385803e-03 4.961937665939331055e-01 5.185697674751281738e-01 1.000000000000000000e+00 -7.258746773004531860e-03 4.936101436614990234e-01 5.125413537025451660e-01 1.000000000000000000e+00 -7.135717198252677917e-03 4.910265207290649414e-01 5.065128803253173828e-01 1.000000000000000000e+00 -7.012687623500823975e-03 4.884428977966308594e-01 5.004844069480895996e-01 1.000000000000000000e+00 -6.889658048748970032e-03 4.858592748641967773e-01 4.944559931755065918e-01 1.000000000000000000e+00 -6.766628008335828781e-03 4.832756519317626953e-01 4.884275197982788086e-01 1.000000000000000000e+00 -6.643598433583974838e-03 4.806920289993286133e-01 4.823990762233734131e-01 1.000000000000000000e+00 -6.520568858832120895e-03 4.781084060668945312e-01 4.763706326484680176e-01 1.000000000000000000e+00 -6.397539284080266953e-03 4.755248129367828369e-01 4.703421890735626221e-01 1.000000000000000000e+00 -6.274509709328413010e-03 4.729411900043487549e-01 4.643137156963348389e-01 1.000000000000000000e+00 -6.151480134576559067e-03 4.703575670719146729e-01 4.582852721214294434e-01 1.000000000000000000e+00 -6.028450559824705124e-03 4.677739441394805908e-01 4.522568285465240479e-01 1.000000000000000000e+00 -5.905420985072851181e-03 4.651903212070465088e-01 4.462283849716186523e-01 1.000000000000000000e+00 -5.782391410320997238e-03 4.626066982746124268e-01 4.401999115943908691e-01 1.000000000000000000e+00 -5.659361835569143295e-03 4.600230753421783447e-01 4.341714680194854736e-01 1.000000000000000000e+00 -5.536332260817289352e-03 4.574394524097442627e-01 4.281430244445800781e-01 1.000000000000000000e+00 -5.413302686065435410e-03 4.548558294773101807e-01 4.221145808696746826e-01 1.000000000000000000e+00 -5.290273111313581467e-03 4.522722065448760986e-01 4.160861074924468994e-01 1.000000000000000000e+00 -5.167243536561727524e-03 4.496885836124420166e-01 4.100576639175415039e-01 1.000000000000000000e+00 -5.044213961809873581e-03 4.471049606800079346e-01 4.040292203426361084e-01 1.000000000000000000e+00 -4.921184387058019638e-03 4.445213377475738525e-01 3.980007767677307129e-01 1.000000000000000000e+00 -4.798154346644878387e-03 4.419377148151397705e-01 3.919723331928253174e-01 1.000000000000000000e+00 -4.675124771893024445e-03 4.393540918827056885e-01 3.859438598155975342e-01 1.000000000000000000e+00 -4.552095197141170502e-03 4.367704689502716064e-01 3.799154162406921387e-01 1.000000000000000000e+00 -4.429065622389316559e-03 4.341868460178375244e-01 3.738869726657867432e-01 1.000000000000000000e+00 -4.306036047637462616e-03 4.316032230854034424e-01 3.678585290908813477e-01 1.000000000000000000e+00 -4.183006472885608673e-03 4.290196001529693604e-01 3.618300557136535645e-01 1.000000000000000000e+00 -4.059976898133754730e-03 4.264359772205352783e-01 3.558016121387481689e-01 1.000000000000000000e+00 -3.936947323381900787e-03 4.238523542881011963e-01 3.497731685638427734e-01 1.000000000000000000e+00 -3.921568859368562698e-03 4.194386899471282959e-01 3.452518284320831299e-01 1.000000000000000000e+00 -3.921568859368562698e-03 4.147635400295257568e-01 3.409457802772521973e-01 1.000000000000000000e+00 -3.921568859368562698e-03 4.100884199142456055e-01 3.366397619247436523e-01 1.000000000000000000e+00 -3.921568859368562698e-03 4.054132997989654541e-01 3.323337137699127197e-01 1.000000000000000000e+00 -3.921568859368562698e-03 4.007381796836853027e-01 3.280276954174041748e-01 1.000000000000000000e+00 -3.921568859368562698e-03 3.960630595684051514e-01 3.237216472625732422e-01 1.000000000000000000e+00 -3.921568859368562698e-03 3.913879394531250000e-01 3.194155991077423096e-01 1.000000000000000000e+00 -3.921568859368562698e-03 3.867127895355224609e-01 3.151095807552337646e-01 1.000000000000000000e+00 -3.921568859368562698e-03 3.820376694202423096e-01 3.108035326004028320e-01 1.000000000000000000e+00 -3.921568859368562698e-03 3.773625493049621582e-01 3.064975142478942871e-01 1.000000000000000000e+00 -3.921568859368562698e-03 3.726874291896820068e-01 3.021914660930633545e-01 1.000000000000000000e+00 -3.921568859368562698e-03 3.680123090744018555e-01 2.978854179382324219e-01 1.000000000000000000e+00 -3.921568859368562698e-03 3.633371889591217041e-01 2.935793995857238770e-01 1.000000000000000000e+00 -3.921568859368562698e-03 3.586620390415191650e-01 2.892733514308929443e-01 1.000000000000000000e+00 -3.921568859368562698e-03 3.539869189262390137e-01 2.849673330783843994e-01 1.000000000000000000e+00 -3.921568859368562698e-03 3.493117988109588623e-01 2.806612849235534668e-01 1.000000000000000000e+00 -3.921568859368562698e-03 3.446366786956787109e-01 2.763552367687225342e-01 1.000000000000000000e+00 -3.921568859368562698e-03 3.399615585803985596e-01 2.720492184162139893e-01 1.000000000000000000e+00 -3.921568859368562698e-03 3.352864384651184082e-01 2.677431702613830566e-01 1.000000000000000000e+00 -3.921568859368562698e-03 3.306112885475158691e-01 2.634371519088745117e-01 1.000000000000000000e+00 -3.921568859368562698e-03 3.259361684322357178e-01 2.591311037540435791e-01 1.000000000000000000e+00 -3.921568859368562698e-03 3.212610483169555664e-01 2.548250555992126465e-01 1.000000000000000000e+00 -3.921568859368562698e-03 3.165859282016754150e-01 2.505190372467041016e-01 1.000000000000000000e+00 -3.921568859368562698e-03 3.119108080863952637e-01 2.462129890918731689e-01 1.000000000000000000e+00 -3.921568859368562698e-03 3.072356879711151123e-01 2.419069558382034302e-01 1.000000000000000000e+00 -3.921568859368562698e-03 3.025605678558349609e-01 2.376009225845336914e-01 1.000000000000000000e+00 -3.921568859368562698e-03 2.978854179382324219e-01 2.332948893308639526e-01 1.000000000000000000e+00 -3.921568859368562698e-03 2.932102978229522705e-01 2.289888560771942139e-01 1.000000000000000000e+00 -3.921568859368562698e-03 2.885351777076721191e-01 2.246828079223632812e-01 1.000000000000000000e+00 -3.921568859368562698e-03 2.838600575923919678e-01 2.203767746686935425e-01 1.000000000000000000e+00 -3.921568859368562698e-03 2.791849374771118164e-01 2.160707414150238037e-01 1.000000000000000000e+00 -3.921568859368562698e-03 2.745098173618316650e-01 2.117647081613540649e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/PuOr b/fastplotlib/utils/colormaps/PuOr deleted file mode 100644 index 9f95d83c9..000000000 --- a/fastplotlib/utils/colormaps/PuOr +++ /dev/null @@ -1,256 +0,0 @@ -4.980392158031463623e-01 2.313725501298904419e-01 3.137255087494850159e-02 1.000000000000000000e+00 -5.060361623764038086e-01 2.358323782682418823e-01 3.106497414410114288e-02 1.000000000000000000e+00 -5.140330791473388672e-01 2.402921915054321289e-01 3.075740113854408264e-02 1.000000000000000000e+00 -5.220299959182739258e-01 2.447520196437835693e-01 3.044982627034187317e-02 1.000000000000000000e+00 -5.300269126892089844e-01 2.492118477821350098e-01 3.014225326478481293e-02 1.000000000000000000e+00 -5.380238294601440430e-01 2.536716759204864502e-01 2.983467839658260345e-02 1.000000000000000000e+00 -5.460207462310791016e-01 2.581314742565155029e-01 2.952710539102554321e-02 1.000000000000000000e+00 -5.540176630020141602e-01 2.625913023948669434e-01 2.921953052282333374e-02 1.000000000000000000e+00 -5.620146393775939941e-01 2.670511305332183838e-01 2.891195751726627350e-02 1.000000000000000000e+00 -5.700115561485290527e-01 2.715109586715698242e-01 2.860438264906406403e-02 1.000000000000000000e+00 -5.780084729194641113e-01 2.759707868099212646e-01 2.829680964350700378e-02 1.000000000000000000e+00 -5.860053896903991699e-01 2.804306149482727051e-01 2.798923477530479431e-02 1.000000000000000000e+00 -5.940023064613342285e-01 2.848904132843017578e-01 2.768166176974773407e-02 1.000000000000000000e+00 -6.019992232322692871e-01 2.893502414226531982e-01 2.737408690154552460e-02 1.000000000000000000e+00 -6.099961400032043457e-01 2.938100695610046387e-01 2.706651203334331512e-02 1.000000000000000000e+00 -6.179930567741394043e-01 2.982698976993560791e-01 2.675893902778625488e-02 1.000000000000000000e+00 -6.259900331497192383e-01 3.027297258377075195e-01 2.645136415958404541e-02 1.000000000000000000e+00 -6.339869499206542969e-01 3.071895539760589600e-01 2.614379115402698517e-02 1.000000000000000000e+00 -6.419838666915893555e-01 3.116493523120880127e-01 2.583621628582477570e-02 1.000000000000000000e+00 -6.499807834625244141e-01 3.161091804504394531e-01 2.552864328026771545e-02 1.000000000000000000e+00 -6.579777002334594727e-01 3.205690085887908936e-01 2.522106841206550598e-02 1.000000000000000000e+00 -6.659746170043945312e-01 3.250288367271423340e-01 2.491349540650844574e-02 1.000000000000000000e+00 -6.739715337753295898e-01 3.294886648654937744e-01 2.460592053830623627e-02 1.000000000000000000e+00 -6.819684505462646484e-01 3.339484930038452148e-01 2.429834753274917603e-02 1.000000000000000000e+00 -6.899654269218444824e-01 3.384082913398742676e-01 2.399077266454696655e-02 1.000000000000000000e+00 -6.979623436927795410e-01 3.428681194782257080e-01 2.368319965898990631e-02 1.000000000000000000e+00 -7.054209709167480469e-01 3.483275771141052246e-01 2.460592053830623627e-02 1.000000000000000000e+00 -7.123414278030395508e-01 3.547866344451904297e-01 2.675893902778625488e-02 1.000000000000000000e+00 -7.192618250846862793e-01 3.612456619739532471e-01 2.891195751726627350e-02 1.000000000000000000e+00 -7.261822223663330078e-01 3.677047193050384521e-01 3.106497414410114288e-02 1.000000000000000000e+00 -7.331026792526245117e-01 3.741637766361236572e-01 3.321799263358116150e-02 1.000000000000000000e+00 -7.400230765342712402e-01 3.806228339672088623e-01 3.537101298570632935e-02 1.000000000000000000e+00 -7.469434738159179688e-01 3.870818912982940674e-01 3.752402961254119873e-02 1.000000000000000000e+00 -7.538638710975646973e-01 3.935409486293792725e-01 3.967704623937606812e-02 1.000000000000000000e+00 -7.607843279838562012e-01 4.000000059604644775e-01 4.183006659150123596e-02 1.000000000000000000e+00 -7.677047252655029297e-01 4.064590632915496826e-01 4.398308321833610535e-02 1.000000000000000000e+00 -7.746251225471496582e-01 4.129181206226348877e-01 4.613609984517097473e-02 1.000000000000000000e+00 -7.815455794334411621e-01 4.193771481513977051e-01 4.828912019729614258e-02 1.000000000000000000e+00 -7.884659767150878906e-01 4.258362054824829102e-01 5.044213682413101196e-02 1.000000000000000000e+00 -7.953863739967346191e-01 4.322952628135681152e-01 5.259515717625617981e-02 1.000000000000000000e+00 -8.023068308830261230e-01 4.387543201446533203e-01 5.474817380309104919e-02 1.000000000000000000e+00 -8.092272281646728516e-01 4.452133774757385254e-01 5.690119042992591858e-02 1.000000000000000000e+00 -8.161476254463195801e-01 4.516724348068237305e-01 5.905421078205108643e-02 1.000000000000000000e+00 -8.230680227279663086e-01 4.581314921379089355e-01 6.120722740888595581e-02 1.000000000000000000e+00 -8.299884796142578125e-01 4.645905494689941406e-01 6.336024403572082520e-02 1.000000000000000000e+00 -8.369088768959045410e-01 4.710496068000793457e-01 6.551326066255569458e-02 1.000000000000000000e+00 -8.438292741775512695e-01 4.775086641311645508e-01 6.766628473997116089e-02 1.000000000000000000e+00 -8.507497310638427734e-01 4.839676916599273682e-01 6.981930136680603027e-02 1.000000000000000000e+00 -8.576701283454895020e-01 4.904267489910125732e-01 7.197231799364089966e-02 1.000000000000000000e+00 -8.645905256271362305e-01 4.968858063220977783e-01 7.412533462047576904e-02 1.000000000000000000e+00 -8.715109825134277344e-01 5.033448934555053711e-01 7.627835124731063843e-02 1.000000000000000000e+00 -8.784313797950744629e-01 5.098039507865905762e-01 7.843137532472610474e-02 1.000000000000000000e+00 -8.828911781311035156e-01 5.181084275245666504e-01 9.058054536581039429e-02 1.000000000000000000e+00 -8.873510360717773438e-01 5.264129042625427246e-01 1.027297228574752808e-01 1.000000000000000000e+00 -8.918108344078063965e-01 5.347174406051635742e-01 1.148788928985595703e-01 1.000000000000000000e+00 -8.962706923484802246e-01 5.430219173431396484e-01 1.270280629396438599e-01 1.000000000000000000e+00 -9.007304906845092773e-01 5.513263940811157227e-01 1.391772329807281494e-01 1.000000000000000000e+00 -9.051902890205383301e-01 5.596309304237365723e-01 1.513264179229736328e-01 1.000000000000000000e+00 -9.096501469612121582e-01 5.679354071617126465e-01 1.634755879640579224e-01 1.000000000000000000e+00 -9.141099452972412109e-01 5.762398838996887207e-01 1.756247580051422119e-01 1.000000000000000000e+00 -9.185698032379150391e-01 5.845444202423095703e-01 1.877739280462265015e-01 1.000000000000000000e+00 -9.230296015739440918e-01 5.928488969802856445e-01 1.999231129884719849e-01 1.000000000000000000e+00 -9.274893999099731445e-01 6.011533737182617188e-01 2.120722830295562744e-01 1.000000000000000000e+00 -9.319492578506469727e-01 6.094579100608825684e-01 2.242214530706405640e-01 1.000000000000000000e+00 -9.364090561866760254e-01 6.177623867988586426e-01 2.363706231117248535e-01 1.000000000000000000e+00 -9.408689141273498535e-01 6.260669231414794922e-01 2.485197931528091431e-01 1.000000000000000000e+00 -9.453287124633789062e-01 6.343713998794555664e-01 2.606689631938934326e-01 1.000000000000000000e+00 -9.497885704040527344e-01 6.426758766174316406e-01 2.728181481361389160e-01 1.000000000000000000e+00 -9.542483687400817871e-01 6.509804129600524902e-01 2.849673330783843994e-01 1.000000000000000000e+00 -9.587081670761108398e-01 6.592848896980285645e-01 2.971164882183074951e-01 1.000000000000000000e+00 -9.631680250167846680e-01 6.675893664360046387e-01 3.092656731605529785e-01 1.000000000000000000e+00 -9.676278233528137207e-01 6.758939027786254883e-01 3.214148283004760742e-01 1.000000000000000000e+00 -9.720876812934875488e-01 6.841983795166015625e-01 3.335640132427215576e-01 1.000000000000000000e+00 -9.765474796295166016e-01 6.925028562545776367e-01 3.457131981849670410e-01 1.000000000000000000e+00 -9.810072779655456543e-01 7.008073925971984863e-01 3.578623533248901367e-01 1.000000000000000000e+00 -9.854671359062194824e-01 7.091118693351745605e-01 3.700115382671356201e-01 1.000000000000000000e+00 -9.899269342422485352e-01 7.174164056777954102e-01 3.821606934070587158e-01 1.000000000000000000e+00 -9.922337532043457031e-01 7.246443629264831543e-01 3.946174681186676025e-01 1.000000000000000000e+00 -9.923875331878662109e-01 7.307958602905273438e-01 4.073817729949951172e-01 1.000000000000000000e+00 -9.925413131713867188e-01 7.369473576545715332e-01 4.201461076736450195e-01 1.000000000000000000e+00 -9.926950931549072266e-01 7.430987954139709473e-01 4.329104125499725342e-01 1.000000000000000000e+00 -9.928489327430725098e-01 7.492502927780151367e-01 4.456747472286224365e-01 1.000000000000000000e+00 -9.930027127265930176e-01 7.554017901420593262e-01 4.584390521049499512e-01 1.000000000000000000e+00 -9.931564927101135254e-01 7.615532279014587402e-01 4.712033867835998535e-01 1.000000000000000000e+00 -9.933102726936340332e-01 7.677047252655029297e-01 4.839676916599273682e-01 1.000000000000000000e+00 -9.934640526771545410e-01 7.738562226295471191e-01 4.967320263385772705e-01 1.000000000000000000e+00 -9.936178326606750488e-01 7.800076603889465332e-01 5.094963312149047852e-01 1.000000000000000000e+00 -9.937716126441955566e-01 7.861591577529907227e-01 5.222606658935546875e-01 1.000000000000000000e+00 -9.939253926277160645e-01 7.923106551170349121e-01 5.350250005722045898e-01 1.000000000000000000e+00 -9.940791726112365723e-01 7.984621524810791016e-01 5.477893352508544922e-01 1.000000000000000000e+00 -9.942330121994018555e-01 8.046135902404785156e-01 5.605536103248596191e-01 1.000000000000000000e+00 -9.943867921829223633e-01 8.107650876045227051e-01 5.733179450035095215e-01 1.000000000000000000e+00 -9.945405721664428711e-01 8.169165849685668945e-01 5.860822796821594238e-01 1.000000000000000000e+00 -9.946943521499633789e-01 8.230680227279663086e-01 5.988466143608093262e-01 1.000000000000000000e+00 -9.948481321334838867e-01 8.292195200920104980e-01 6.116108894348144531e-01 1.000000000000000000e+00 -9.950019121170043945e-01 8.353710174560546875e-01 6.243752241134643555e-01 1.000000000000000000e+00 -9.951556921005249023e-01 8.415225148200988770e-01 6.371395587921142578e-01 1.000000000000000000e+00 -9.953094720840454102e-01 8.476739525794982910e-01 6.499038934707641602e-01 1.000000000000000000e+00 -9.954633116722106934e-01 8.538254499435424805e-01 6.626682281494140625e-01 1.000000000000000000e+00 -9.956170916557312012e-01 8.599769473075866699e-01 6.754325032234191895e-01 1.000000000000000000e+00 -9.957708716392517090e-01 8.661283850669860840e-01 6.881968379020690918e-01 1.000000000000000000e+00 -9.959246516227722168e-01 8.722798824310302734e-01 7.009611725807189941e-01 1.000000000000000000e+00 -9.960784316062927246e-01 8.784313797950744629e-01 7.137255072593688965e-01 1.000000000000000000e+00 -9.950019121170043945e-01 8.819684982299804688e-01 7.237216234207153320e-01 1.000000000000000000e+00 -9.939253926277160645e-01 8.855055570602416992e-01 7.337177991867065430e-01 1.000000000000000000e+00 -9.928489327430725098e-01 8.890426754951477051e-01 7.437139749526977539e-01 1.000000000000000000e+00 -9.917724132537841797e-01 8.925797939300537109e-01 7.537100911140441895e-01 1.000000000000000000e+00 -9.906958937644958496e-01 8.961168527603149414e-01 7.637062668800354004e-01 1.000000000000000000e+00 -9.896193742752075195e-01 8.996539711952209473e-01 7.737024426460266113e-01 1.000000000000000000e+00 -9.885428547859191895e-01 9.031910896301269531e-01 7.836985588073730469e-01 1.000000000000000000e+00 -9.874663352966308594e-01 9.067282080650329590e-01 7.936947345733642578e-01 1.000000000000000000e+00 -9.863898754119873047e-01 9.102652668952941895e-01 8.036909103393554688e-01 1.000000000000000000e+00 -9.853133559226989746e-01 9.138023853302001953e-01 8.136870265007019043e-01 1.000000000000000000e+00 -9.842368364334106445e-01 9.173395037651062012e-01 8.236832022666931152e-01 1.000000000000000000e+00 -9.831603169441223145e-01 9.208765625953674316e-01 8.336793780326843262e-01 1.000000000000000000e+00 -9.820837974548339844e-01 9.244136810302734375e-01 8.436754941940307617e-01 1.000000000000000000e+00 -9.810072779655456543e-01 9.279507994651794434e-01 8.536716699600219727e-01 1.000000000000000000e+00 -9.799308180809020996e-01 9.314879179000854492e-01 8.636678457260131836e-01 1.000000000000000000e+00 -9.788542985916137695e-01 9.350249767303466797e-01 8.736639618873596191e-01 1.000000000000000000e+00 -9.777777791023254395e-01 9.385620951652526855e-01 8.836601376533508301e-01 1.000000000000000000e+00 -9.767012596130371094e-01 9.420992136001586914e-01 8.936563134193420410e-01 1.000000000000000000e+00 -9.756247401237487793e-01 9.456362724304199219e-01 9.036524295806884766e-01 1.000000000000000000e+00 -9.745482802391052246e-01 9.491733908653259277e-01 9.136486053466796875e-01 1.000000000000000000e+00 -9.734717607498168945e-01 9.527105093002319336e-01 9.236447811126708984e-01 1.000000000000000000e+00 -9.723952412605285645e-01 9.562475681304931641e-01 9.336408972740173340e-01 1.000000000000000000e+00 -9.713187217712402344e-01 9.597846865653991699e-01 9.436370730400085449e-01 1.000000000000000000e+00 -9.702422022819519043e-01 9.633218050003051758e-01 9.536331892013549805e-01 1.000000000000000000e+00 -9.691656827926635742e-01 9.668589234352111816e-01 9.636293649673461914e-01 1.000000000000000000e+00 -9.662437438964843750e-01 9.663975238800048828e-01 9.677047133445739746e-01 1.000000000000000000e+00 -9.614763259887695312e-01 9.619377255439758301e-01 9.658592939376831055e-01 1.000000000000000000e+00 -9.567089676856994629e-01 9.574778676033020020e-01 9.640138149261474609e-01 1.000000000000000000e+00 -9.519415497779846191e-01 9.530180692672729492e-01 9.621683955192565918e-01 1.000000000000000000e+00 -9.471741914749145508e-01 9.485582709312438965e-01 9.603229761123657227e-01 1.000000000000000000e+00 -9.424067735671997070e-01 9.440984129905700684e-01 9.584774971008300781e-01 1.000000000000000000e+00 -9.376393556594848633e-01 9.396386146545410156e-01 9.566320776939392090e-01 1.000000000000000000e+00 -9.328719973564147949e-01 9.351787567138671875e-01 9.547865986824035645e-01 1.000000000000000000e+00 -9.281045794486999512e-01 9.307189583778381348e-01 9.529411792755126953e-01 1.000000000000000000e+00 -9.233371615409851074e-01 9.262591600418090820e-01 9.510957598686218262e-01 1.000000000000000000e+00 -9.185698032379150391e-01 9.217993021011352539e-01 9.492502808570861816e-01 1.000000000000000000e+00 -9.138023853302001953e-01 9.173395037651062012e-01 9.474048614501953125e-01 1.000000000000000000e+00 -9.090349674224853516e-01 9.128796458244323730e-01 9.455593824386596680e-01 1.000000000000000000e+00 -9.042676091194152832e-01 9.084198474884033203e-01 9.437139630317687988e-01 1.000000000000000000e+00 -8.995001912117004395e-01 9.039599895477294922e-01 9.418684840202331543e-01 1.000000000000000000e+00 -8.947327733039855957e-01 8.995001912117004395e-01 9.400230646133422852e-01 1.000000000000000000e+00 -8.899654150009155273e-01 8.950403928756713867e-01 9.381776452064514160e-01 1.000000000000000000e+00 -8.851979970932006836e-01 8.905805349349975586e-01 9.363321661949157715e-01 1.000000000000000000e+00 -8.804305791854858398e-01 8.861207365989685059e-01 9.344867467880249023e-01 1.000000000000000000e+00 -8.756632208824157715e-01 8.816608786582946777e-01 9.326412677764892578e-01 1.000000000000000000e+00 -8.708958029747009277e-01 8.772010803222656250e-01 9.307958483695983887e-01 1.000000000000000000e+00 -8.661283850669860840e-01 8.727412819862365723e-01 9.289504289627075195e-01 1.000000000000000000e+00 -8.613610267639160156e-01 8.682814240455627441e-01 9.271049499511718750e-01 1.000000000000000000e+00 -8.565936088562011719e-01 8.638216257095336914e-01 9.252595305442810059e-01 1.000000000000000000e+00 -8.518261909484863281e-01 8.593617677688598633e-01 9.234140515327453613e-01 1.000000000000000000e+00 -8.470588326454162598e-01 8.549019694328308105e-01 9.215686321258544922e-01 1.000000000000000000e+00 -8.412148952484130859e-01 8.476739525794982910e-01 9.177239537239074707e-01 1.000000000000000000e+00 -8.353710174560546875e-01 8.404459953308105469e-01 9.138792753219604492e-01 1.000000000000000000e+00 -8.295270800590515137e-01 8.332179784774780273e-01 9.100345969200134277e-01 1.000000000000000000e+00 -8.236832022666931152e-01 8.259900212287902832e-01 9.061899185180664062e-01 1.000000000000000000e+00 -8.178392648696899414e-01 8.187620043754577637e-01 9.023452401161193848e-01 1.000000000000000000e+00 -8.119953870773315430e-01 8.115340471267700195e-01 8.985005617141723633e-01 1.000000000000000000e+00 -8.061515092849731445e-01 8.043060302734375000e-01 8.946558833122253418e-01 1.000000000000000000e+00 -8.003075718879699707e-01 7.970780730247497559e-01 8.908112049102783203e-01 1.000000000000000000e+00 -7.944636940956115723e-01 7.898500561714172363e-01 8.869665265083312988e-01 1.000000000000000000e+00 -7.886197566986083984e-01 7.826220393180847168e-01 8.831218481063842773e-01 1.000000000000000000e+00 -7.827758789062500000e-01 7.753940820693969727e-01 8.792772293090820312e-01 1.000000000000000000e+00 -7.769319415092468262e-01 7.681660652160644531e-01 8.754325509071350098e-01 1.000000000000000000e+00 -7.710880637168884277e-01 7.609381079673767090e-01 8.715878725051879883e-01 1.000000000000000000e+00 -7.652441263198852539e-01 7.537100911140441895e-01 8.677431941032409668e-01 1.000000000000000000e+00 -7.594002485275268555e-01 7.464821338653564453e-01 8.638985157012939453e-01 1.000000000000000000e+00 -7.535563111305236816e-01 7.392541170120239258e-01 8.600538372993469238e-01 1.000000000000000000e+00 -7.477124333381652832e-01 7.320261597633361816e-01 8.562091588973999023e-01 1.000000000000000000e+00 -7.418684959411621094e-01 7.247981429100036621e-01 8.523644804954528809e-01 1.000000000000000000e+00 -7.360246181488037109e-01 7.175701856613159180e-01 8.485198020935058594e-01 1.000000000000000000e+00 -7.301806807518005371e-01 7.103421688079833984e-01 8.446751236915588379e-01 1.000000000000000000e+00 -7.243368029594421387e-01 7.031142115592956543e-01 8.408304452896118164e-01 1.000000000000000000e+00 -7.184928655624389648e-01 6.958861947059631348e-01 8.369857668876647949e-01 1.000000000000000000e+00 -7.126489877700805664e-01 6.886582374572753906e-01 8.331410884857177734e-01 1.000000000000000000e+00 -7.068050503730773926e-01 6.814302206039428711e-01 8.292964100837707520e-01 1.000000000000000000e+00 -7.009611725807189941e-01 6.742022037506103516e-01 8.254517316818237305e-01 1.000000000000000000e+00 -6.941945552825927734e-01 6.662821769714355469e-01 8.206074833869934082e-01 1.000000000000000000e+00 -6.865051984786987305e-01 6.576701402664184570e-01 8.147635459899902344e-01 1.000000000000000000e+00 -6.788158416748046875e-01 6.490580439567565918e-01 8.089196681976318359e-01 1.000000000000000000e+00 -6.711264848709106445e-01 6.404460072517395020e-01 8.030757308006286621e-01 1.000000000000000000e+00 -6.634371280670166016e-01 6.318339109420776367e-01 7.972318530082702637e-01 1.000000000000000000e+00 -6.557477712631225586e-01 6.232218146324157715e-01 7.913879156112670898e-01 1.000000000000000000e+00 -6.480584144592285156e-01 6.146097779273986816e-01 7.855440378189086914e-01 1.000000000000000000e+00 -6.403691172599792480e-01 6.059976816177368164e-01 7.797001004219055176e-01 1.000000000000000000e+00 -6.326797604560852051e-01 5.973856449127197266e-01 7.738562226295471191e-01 1.000000000000000000e+00 -6.249904036521911621e-01 5.887735486030578613e-01 7.680122852325439453e-01 1.000000000000000000e+00 -6.173010468482971191e-01 5.801614522933959961e-01 7.621684074401855469e-01 1.000000000000000000e+00 -6.096116900444030762e-01 5.715494155883789062e-01 7.563244700431823730e-01 1.000000000000000000e+00 -6.019223332405090332e-01 5.629373192787170410e-01 7.504805922508239746e-01 1.000000000000000000e+00 -5.942329764366149902e-01 5.543252825736999512e-01 7.446366548538208008e-01 1.000000000000000000e+00 -5.865436196327209473e-01 5.457131862640380859e-01 7.387927770614624023e-01 1.000000000000000000e+00 -5.788542628288269043e-01 5.371010899543762207e-01 7.329488396644592285e-01 1.000000000000000000e+00 -5.711649656295776367e-01 5.284890532493591309e-01 7.271049618721008301e-01 1.000000000000000000e+00 -5.634756088256835938e-01 5.198769569396972656e-01 7.212610244750976562e-01 1.000000000000000000e+00 -5.557862520217895508e-01 5.112649202346801758e-01 7.154171466827392578e-01 1.000000000000000000e+00 -5.480968952178955078e-01 5.026528239250183105e-01 7.095732688903808594e-01 1.000000000000000000e+00 -5.404075384140014648e-01 4.940407574176788330e-01 7.037293314933776855e-01 1.000000000000000000e+00 -5.327181816101074219e-01 4.854286909103393555e-01 6.978854537010192871e-01 1.000000000000000000e+00 -5.250288248062133789e-01 4.768165946006774902e-01 6.920415163040161133e-01 1.000000000000000000e+00 -5.173394680023193359e-01 4.682045280933380127e-01 6.861976385116577148e-01 1.000000000000000000e+00 -5.096501111984252930e-01 4.595924615859985352e-01 6.803537011146545410e-01 1.000000000000000000e+00 -5.019608139991760254e-01 4.509803950786590576e-01 6.745098233222961426e-01 1.000000000000000000e+00 -4.951941668987274170e-01 4.392925798892974854e-01 6.689734458923339844e-01 1.000000000000000000e+00 -4.884275197982788086e-01 4.276047646999359131e-01 6.634371280670166016e-01 1.000000000000000000e+00 -4.816609025001525879e-01 4.159169495105743408e-01 6.579008102416992188e-01 1.000000000000000000e+00 -4.748942852020263672e-01 4.042291343212127686e-01 6.523644924163818359e-01 1.000000000000000000e+00 -4.681276381015777588e-01 3.925413191318511963e-01 6.468281149864196777e-01 1.000000000000000000e+00 -4.613610208034515381e-01 3.808535039424896240e-01 6.412917971611022949e-01 1.000000000000000000e+00 -4.545943737030029297e-01 3.691657185554504395e-01 6.357554793357849121e-01 1.000000000000000000e+00 -4.478277564048767090e-01 3.574779033660888672e-01 6.302191615104675293e-01 1.000000000000000000e+00 -4.410611391067504883e-01 3.457900881767272949e-01 6.246828436851501465e-01 1.000000000000000000e+00 -4.342944920063018799e-01 3.341022729873657227e-01 6.191464662551879883e-01 1.000000000000000000e+00 -4.275278747081756592e-01 3.224144577980041504e-01 6.136101484298706055e-01 1.000000000000000000e+00 -4.207612574100494385e-01 3.107266426086425781e-01 6.080738306045532227e-01 1.000000000000000000e+00 -4.139946103096008301e-01 2.990388274192810059e-01 6.025375127792358398e-01 1.000000000000000000e+00 -4.072279930114746094e-01 2.873510122299194336e-01 5.970011353492736816e-01 1.000000000000000000e+00 -4.004613757133483887e-01 2.756631970405578613e-01 5.914648175239562988e-01 1.000000000000000000e+00 -3.936947286128997803e-01 2.639753818511962891e-01 5.859284996986389160e-01 1.000000000000000000e+00 -3.869281113147735596e-01 2.522875964641571045e-01 5.803921818733215332e-01 1.000000000000000000e+00 -3.801614642143249512e-01 2.405997663736343384e-01 5.748558044433593750e-01 1.000000000000000000e+00 -3.733948469161987305e-01 2.289119511842727661e-01 5.693194866180419922e-01 1.000000000000000000e+00 -3.666282296180725098e-01 2.172241508960723877e-01 5.637831687927246094e-01 1.000000000000000000e+00 -3.598615825176239014e-01 2.055363357067108154e-01 5.582468509674072266e-01 1.000000000000000000e+00 -3.530949652194976807e-01 1.938485205173492432e-01 5.527104735374450684e-01 1.000000000000000000e+00 -3.463283479213714600e-01 1.821607053279876709e-01 5.471741557121276855e-01 1.000000000000000000e+00 -3.395617008209228516e-01 1.704728901386260986e-01 5.416378378868103027e-01 1.000000000000000000e+00 -3.327950835227966309e-01 1.587850898504257202e-01 5.361015200614929199e-01 1.000000000000000000e+00 -3.264129161834716797e-01 1.499423235654830933e-01 5.286428332328796387e-01 1.000000000000000000e+00 -3.204152286052703857e-01 1.439446359872817993e-01 5.192618370056152344e-01 1.000000000000000000e+00 -3.144175410270690918e-01 1.379469484090805054e-01 5.098808407783508301e-01 1.000000000000000000e+00 -3.084198236465454102e-01 1.319492459297180176e-01 5.004997849464416504e-01 1.000000000000000000e+00 -3.024221360683441162e-01 1.259515583515167236e-01 4.911187887191772461e-01 1.000000000000000000e+00 -2.964244484901428223e-01 1.199538633227348328e-01 4.817377924919128418e-01 1.000000000000000000e+00 -2.904267609119415283e-01 1.139561682939529419e-01 4.723567962646484375e-01 1.000000000000000000e+00 -2.844290733337402344e-01 1.079584807157516479e-01 4.629757702350616455e-01 1.000000000000000000e+00 -2.784313857555389404e-01 1.019607856869697571e-01 4.535947740077972412e-01 1.000000000000000000e+00 -2.724336683750152588e-01 9.596309065818786621e-02 4.442137777805328369e-01 1.000000000000000000e+00 -2.664359807968139648e-01 8.996539562940597534e-02 4.348327517509460449e-01 1.000000000000000000e+00 -2.604382932186126709e-01 8.396770805120468140e-02 4.254517555236816406e-01 1.000000000000000000e+00 -2.544406056404113770e-01 7.797001302242279053e-02 4.160707294940948486e-01 1.000000000000000000e+00 -2.484429031610488892e-01 7.197231799364089966e-02 4.066897332668304443e-01 1.000000000000000000e+00 -2.424452155828475952e-01 6.597462296485900879e-02 3.973087370395660400e-01 1.000000000000000000e+00 -2.364475131034851074e-01 5.997693166136741638e-02 3.879277110099792480e-01 1.000000000000000000e+00 -2.304498255252838135e-01 5.397924035787582397e-02 3.785467147827148438e-01 1.000000000000000000e+00 -2.244521379470825195e-01 4.798154532909393311e-02 3.691657185554504395e-01 1.000000000000000000e+00 -2.184544354677200317e-01 4.198385402560234070e-02 3.597846925258636475e-01 1.000000000000000000e+00 -2.124567478895187378e-01 3.598615899682044983e-02 3.504036962985992432e-01 1.000000000000000000e+00 -2.064590603113174438e-01 2.998846583068370819e-02 3.410226702690124512e-01 1.000000000000000000e+00 -2.004613578319549561e-01 2.399077266454696655e-02 3.316416740417480469e-01 1.000000000000000000e+00 -1.944636702537536621e-01 1.799307949841022491e-02 3.222606778144836426e-01 1.000000000000000000e+00 -1.884659677743911743e-01 1.199538633227348328e-02 3.128796517848968506e-01 1.000000000000000000e+00 -1.824682801961898804e-01 5.997693166136741638e-03 3.034986555576324463e-01 1.000000000000000000e+00 -1.764705926179885864e-01 0.000000000000000000e+00 2.941176593303680420e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/PuRd b/fastplotlib/utils/colormaps/PuRd deleted file mode 100644 index 0c5014f78..000000000 --- a/fastplotlib/utils/colormaps/PuRd +++ /dev/null @@ -1,256 +0,0 @@ -9.686274528503417969e-01 9.568627476692199707e-01 9.764705896377563477e-01 1.000000000000000000e+00 -9.666589498519897461e-01 9.545251727104187012e-01 9.752402901649475098e-01 1.000000000000000000e+00 -9.646905064582824707e-01 9.521875977516174316e-01 9.740099906921386719e-01 1.000000000000000000e+00 -9.627220034599304199e-01 9.498500823974609375e-01 9.727796912193298340e-01 1.000000000000000000e+00 -9.607535600662231445e-01 9.475125074386596680e-01 9.715493917465209961e-01 1.000000000000000000e+00 -9.587850570678710938e-01 9.451749324798583984e-01 9.703190922737121582e-01 1.000000000000000000e+00 -9.568166136741638184e-01 9.428373575210571289e-01 9.690887928009033203e-01 1.000000000000000000e+00 -9.548481106758117676e-01 9.404997825622558594e-01 9.678584933280944824e-01 1.000000000000000000e+00 -9.528796672821044922e-01 9.381622672080993652e-01 9.666281938552856445e-01 1.000000000000000000e+00 -9.509111642837524414e-01 9.358246922492980957e-01 9.653978943824768066e-01 1.000000000000000000e+00 -9.489427208900451660e-01 9.334871172904968262e-01 9.641676545143127441e-01 1.000000000000000000e+00 -9.469742178916931152e-01 9.311495423316955566e-01 9.629373550415039062e-01 1.000000000000000000e+00 -9.450057744979858398e-01 9.288119673728942871e-01 9.617070555686950684e-01 1.000000000000000000e+00 -9.430372714996337891e-01 9.264744520187377930e-01 9.604767560958862305e-01 1.000000000000000000e+00 -9.410688281059265137e-01 9.241368770599365234e-01 9.592464566230773926e-01 1.000000000000000000e+00 -9.391003251075744629e-01 9.217993021011352539e-01 9.580161571502685547e-01 1.000000000000000000e+00 -9.371318817138671875e-01 9.194617271423339844e-01 9.567858576774597168e-01 1.000000000000000000e+00 -9.351633787155151367e-01 9.171242117881774902e-01 9.555555582046508789e-01 1.000000000000000000e+00 -9.331949353218078613e-01 9.147866368293762207e-01 9.543252587318420410e-01 1.000000000000000000e+00 -9.312264323234558105e-01 9.124490618705749512e-01 9.530949592590332031e-01 1.000000000000000000e+00 -9.292579889297485352e-01 9.101114869117736816e-01 9.518646597862243652e-01 1.000000000000000000e+00 -9.272894859313964844e-01 9.077739119529724121e-01 9.506343603134155273e-01 1.000000000000000000e+00 -9.253210425376892090e-01 9.054363965988159180e-01 9.494040608406066895e-01 1.000000000000000000e+00 -9.233525395393371582e-01 9.030988216400146484e-01 9.481737613677978516e-01 1.000000000000000000e+00 -9.213840961456298828e-01 9.007612466812133789e-01 9.469434618949890137e-01 1.000000000000000000e+00 -9.194155931472778320e-01 8.984236717224121094e-01 9.457131624221801758e-01 1.000000000000000000e+00 -9.174471497535705566e-01 8.960860967636108398e-01 9.444828629493713379e-01 1.000000000000000000e+00 -9.154786467552185059e-01 8.937485814094543457e-01 9.432526230812072754e-01 1.000000000000000000e+00 -9.135102033615112305e-01 8.914110064506530762e-01 9.420223236083984375e-01 1.000000000000000000e+00 -9.115417003631591797e-01 8.890734314918518066e-01 9.407920241355895996e-01 1.000000000000000000e+00 -9.095732569694519043e-01 8.867358565330505371e-01 9.395617246627807617e-01 1.000000000000000000e+00 -9.076047539710998535e-01 8.843982815742492676e-01 9.383314251899719238e-01 1.000000000000000000e+00 -9.055901765823364258e-01 8.817377686500549316e-01 9.369319677352905273e-01 1.000000000000000000e+00 -9.032526016235351562e-01 8.768166303634643555e-01 9.343483448028564453e-01 1.000000000000000000e+00 -9.009150266647338867e-01 8.718954324722290039e-01 9.317647218704223633e-01 1.000000000000000000e+00 -8.985774517059326172e-01 8.669742345809936523e-01 9.291810989379882812e-01 1.000000000000000000e+00 -8.962399363517761230e-01 8.620530366897583008e-01 9.265974760055541992e-01 1.000000000000000000e+00 -8.939023613929748535e-01 8.571318984031677246e-01 9.240138530731201172e-01 1.000000000000000000e+00 -8.915647864341735840e-01 8.522107005119323730e-01 9.214302301406860352e-01 1.000000000000000000e+00 -8.892272114753723145e-01 8.472895026206970215e-01 9.188466072082519531e-01 1.000000000000000000e+00 -8.868896365165710449e-01 8.423683047294616699e-01 9.162629842758178711e-01 1.000000000000000000e+00 -8.845521211624145508e-01 8.374471068382263184e-01 9.136793613433837891e-01 1.000000000000000000e+00 -8.822145462036132812e-01 8.325259685516357422e-01 9.110957384109497070e-01 1.000000000000000000e+00 -8.798769712448120117e-01 8.276047706604003906e-01 9.085121154785156250e-01 1.000000000000000000e+00 -8.775393962860107422e-01 8.226835727691650391e-01 9.059284925460815430e-01 1.000000000000000000e+00 -8.752018213272094727e-01 8.177623748779296875e-01 9.033448696136474609e-01 1.000000000000000000e+00 -8.728643059730529785e-01 8.128412365913391113e-01 9.007612466812133789e-01 1.000000000000000000e+00 -8.705267310142517090e-01 8.079200387001037598e-01 8.981776237487792969e-01 1.000000000000000000e+00 -8.681891560554504395e-01 8.029988408088684082e-01 8.955940008163452148e-01 1.000000000000000000e+00 -8.658515810966491699e-01 7.980776429176330566e-01 8.930103778839111328e-01 1.000000000000000000e+00 -8.635140061378479004e-01 7.931565046310424805e-01 8.904267549514770508e-01 1.000000000000000000e+00 -8.611764907836914062e-01 7.882353067398071289e-01 8.878431320190429688e-01 1.000000000000000000e+00 -8.588389158248901367e-01 7.833141088485717773e-01 8.852595090866088867e-01 1.000000000000000000e+00 -8.565013408660888672e-01 7.783929109573364258e-01 8.826758861541748047e-01 1.000000000000000000e+00 -8.541637659072875977e-01 7.734717130661010742e-01 8.800922632217407227e-01 1.000000000000000000e+00 -8.518261909484863281e-01 7.685505747795104980e-01 8.775086402893066406e-01 1.000000000000000000e+00 -8.494886755943298340e-01 7.636293768882751465e-01 8.749250173568725586e-01 1.000000000000000000e+00 -8.471511006355285645e-01 7.587081789970397949e-01 8.723413944244384766e-01 1.000000000000000000e+00 -8.448135256767272949e-01 7.537869811058044434e-01 8.697577714920043945e-01 1.000000000000000000e+00 -8.424759507179260254e-01 7.488658428192138672e-01 8.671741485595703125e-01 1.000000000000000000e+00 -8.401384353637695312e-01 7.439446449279785156e-01 8.645905256271362305e-01 1.000000000000000000e+00 -8.378008604049682617e-01 7.390234470367431641e-01 8.620069026947021484e-01 1.000000000000000000e+00 -8.354632854461669922e-01 7.341022491455078125e-01 8.594232797622680664e-01 1.000000000000000000e+00 -8.331257104873657227e-01 7.291811108589172363e-01 8.568396568298339844e-01 1.000000000000000000e+00 -8.310342431068420410e-01 7.243521809577941895e-01 8.543175458908081055e-01 1.000000000000000000e+00 -8.296809196472167969e-01 7.198000550270080566e-01 8.519800305366516113e-01 1.000000000000000000e+00 -8.283275365829467773e-01 7.152479887008666992e-01 8.496424555778503418e-01 1.000000000000000000e+00 -8.269742131233215332e-01 7.106958627700805664e-01 8.473048806190490723e-01 1.000000000000000000e+00 -8.256208896636962891e-01 7.061437964439392090e-01 8.449673056602478027e-01 1.000000000000000000e+00 -8.242675662040710449e-01 7.015916705131530762e-01 8.426297307014465332e-01 1.000000000000000000e+00 -8.229142427444458008e-01 6.970396041870117188e-01 8.402922153472900391e-01 1.000000000000000000e+00 -8.215609192848205566e-01 6.924874782562255859e-01 8.379546403884887695e-01 1.000000000000000000e+00 -8.202075958251953125e-01 6.879354119300842285e-01 8.356170654296875000e-01 1.000000000000000000e+00 -8.188542723655700684e-01 6.833832859992980957e-01 8.332794904708862305e-01 1.000000000000000000e+00 -8.175009489059448242e-01 6.788312196731567383e-01 8.309419751167297363e-01 1.000000000000000000e+00 -8.161476254463195801e-01 6.742790937423706055e-01 8.286044001579284668e-01 1.000000000000000000e+00 -8.147943019866943359e-01 6.697270274162292480e-01 8.262668251991271973e-01 1.000000000000000000e+00 -8.134409785270690918e-01 6.651749610900878906e-01 8.239292502403259277e-01 1.000000000000000000e+00 -8.120876550674438477e-01 6.606228351593017578e-01 8.215916752815246582e-01 1.000000000000000000e+00 -8.107343316078186035e-01 6.560707688331604004e-01 8.192541599273681641e-01 1.000000000000000000e+00 -8.093810081481933594e-01 6.515186429023742676e-01 8.169165849685668945e-01 1.000000000000000000e+00 -8.080276846885681152e-01 6.469665765762329102e-01 8.145790100097656250e-01 1.000000000000000000e+00 -8.066743612289428711e-01 6.424144506454467773e-01 8.122414350509643555e-01 1.000000000000000000e+00 -8.053210377693176270e-01 6.378623843193054199e-01 8.099038600921630859e-01 1.000000000000000000e+00 -8.039677143096923828e-01 6.333102583885192871e-01 8.075663447380065918e-01 1.000000000000000000e+00 -8.026143908500671387e-01 6.287581920623779297e-01 8.052287697792053223e-01 1.000000000000000000e+00 -8.012610673904418945e-01 6.242060661315917969e-01 8.028911948204040527e-01 1.000000000000000000e+00 -7.999077439308166504e-01 6.196539998054504395e-01 8.005536198616027832e-01 1.000000000000000000e+00 -7.985544204711914062e-01 6.151018738746643066e-01 7.982160449028015137e-01 1.000000000000000000e+00 -7.972010970115661621e-01 6.105498075485229492e-01 7.958785295486450195e-01 1.000000000000000000e+00 -7.958477735519409180e-01 6.059976816177368164e-01 7.935409545898437500e-01 1.000000000000000000e+00 -7.944944500923156738e-01 6.014456152915954590e-01 7.912033796310424805e-01 1.000000000000000000e+00 -7.931411266326904297e-01 5.968934893608093262e-01 7.888658046722412109e-01 1.000000000000000000e+00 -7.917878031730651855e-01 5.923414230346679688e-01 7.865282297134399414e-01 1.000000000000000000e+00 -7.904344201087951660e-01 5.877892971038818359e-01 7.841907143592834473e-01 1.000000000000000000e+00 -7.890810966491699219e-01 5.832372307777404785e-01 7.818531394004821777e-01 1.000000000000000000e+00 -7.892503142356872559e-01 5.782237648963928223e-01 7.793310284614562988e-01 1.000000000000000000e+00 -7.919569611549377441e-01 5.724413394927978516e-01 7.765013575553894043e-01 1.000000000000000000e+00 -7.946636080741882324e-01 5.666589736938476562e-01 7.736716866493225098e-01 1.000000000000000000e+00 -7.973702549934387207e-01 5.608766078948974609e-01 7.708419561386108398e-01 1.000000000000000000e+00 -8.000769019126892090e-01 5.550941824913024902e-01 7.680122852325439453e-01 1.000000000000000000e+00 -8.027835488319396973e-01 5.493118166923522949e-01 7.651826143264770508e-01 1.000000000000000000e+00 -8.054901957511901855e-01 5.435293912887573242e-01 7.623529434204101562e-01 1.000000000000000000e+00 -8.081968426704406738e-01 5.377470254898071289e-01 7.595232725143432617e-01 1.000000000000000000e+00 -8.109034895896911621e-01 5.319646000862121582e-01 7.566936016082763672e-01 1.000000000000000000e+00 -8.136101365089416504e-01 5.261822342872619629e-01 7.538638710975646973e-01 1.000000000000000000e+00 -8.163167834281921387e-01 5.203998684883117676e-01 7.510342001914978027e-01 1.000000000000000000e+00 -8.190234303474426270e-01 5.146174430847167969e-01 7.482045292854309082e-01 1.000000000000000000e+00 -8.217300772666931152e-01 5.088350772857666016e-01 7.453748583793640137e-01 1.000000000000000000e+00 -8.244367837905883789e-01 5.030526518821716309e-01 7.425451874732971191e-01 1.000000000000000000e+00 -8.271434307098388672e-01 4.972702860832214355e-01 7.397155165672302246e-01 1.000000000000000000e+00 -8.298500776290893555e-01 4.914878904819488525e-01 7.368857860565185547e-01 1.000000000000000000e+00 -8.325567245483398438e-01 4.857054948806762695e-01 7.340561151504516602e-01 1.000000000000000000e+00 -8.352633714675903320e-01 4.799230992794036865e-01 7.312264442443847656e-01 1.000000000000000000e+00 -8.379700183868408203e-01 4.741407036781311035e-01 7.283967733383178711e-01 1.000000000000000000e+00 -8.406766653060913086e-01 4.683583378791809082e-01 7.255671024322509766e-01 1.000000000000000000e+00 -8.433833122253417969e-01 4.625759422779083252e-01 7.227374315261840820e-01 1.000000000000000000e+00 -8.460899591445922852e-01 4.567935466766357422e-01 7.199077010154724121e-01 1.000000000000000000e+00 -8.487966060638427734e-01 4.510111510753631592e-01 7.170780301094055176e-01 1.000000000000000000e+00 -8.515032529830932617e-01 4.452287554740905762e-01 7.142483592033386230e-01 1.000000000000000000e+00 -8.542098999023437500e-01 4.394463598728179932e-01 7.114186882972717285e-01 1.000000000000000000e+00 -8.569165468215942383e-01 4.336639642715454102e-01 7.085890173912048340e-01 1.000000000000000000e+00 -8.596231937408447266e-01 4.278815984725952148e-01 7.057593464851379395e-01 1.000000000000000000e+00 -8.623299002647399902e-01 4.220992028713226318e-01 7.029296159744262695e-01 1.000000000000000000e+00 -8.650365471839904785e-01 4.163168072700500488e-01 7.000999450683593750e-01 1.000000000000000000e+00 -8.677431941032409668e-01 4.105344116687774658e-01 6.972702741622924805e-01 1.000000000000000000e+00 -8.704498410224914551e-01 4.047520160675048828e-01 6.944406032562255859e-01 1.000000000000000000e+00 -8.731564879417419434e-01 3.989696204662322998e-01 6.916109323501586914e-01 1.000000000000000000e+00 -8.750019073486328125e-01 3.923875391483306885e-01 6.878585219383239746e-01 1.000000000000000000e+00 -8.759861588478088379e-01 3.850057721138000488e-01 6.831833720207214355e-01 1.000000000000000000e+00 -8.769704103469848633e-01 3.776240050792694092e-01 6.785082817077636719e-01 1.000000000000000000e+00 -8.779546618461608887e-01 3.702422082424163818e-01 6.738331317901611328e-01 1.000000000000000000e+00 -8.789388537406921387e-01 3.628604412078857422e-01 6.691580414772033691e-01 1.000000000000000000e+00 -8.799231052398681641e-01 3.554786741733551025e-01 6.644828915596008301e-01 1.000000000000000000e+00 -8.809073567390441895e-01 3.480968773365020752e-01 6.598077416419982910e-01 1.000000000000000000e+00 -8.818916082382202148e-01 3.407151103019714355e-01 6.551326513290405273e-01 1.000000000000000000e+00 -8.828758001327514648e-01 3.333333432674407959e-01 6.504575014114379883e-01 1.000000000000000000e+00 -8.838600516319274902e-01 3.259515464305877686e-01 6.457824110984802246e-01 1.000000000000000000e+00 -8.848443031311035156e-01 3.185697793960571289e-01 6.411072611808776855e-01 1.000000000000000000e+00 -8.858285546302795410e-01 3.111880123615264893e-01 6.364321708679199219e-01 1.000000000000000000e+00 -8.868127465248107910e-01 3.038062155246734619e-01 6.317570209503173828e-01 1.000000000000000000e+00 -8.877969980239868164e-01 2.964244484901428223e-01 6.270818710327148438e-01 1.000000000000000000e+00 -8.887812495231628418e-01 2.890426814556121826e-01 6.224067807197570801e-01 1.000000000000000000e+00 -8.897655010223388672e-01 2.816609144210815430e-01 6.177316308021545410e-01 1.000000000000000000e+00 -8.907496929168701172e-01 2.742791175842285156e-01 6.130565404891967773e-01 1.000000000000000000e+00 -8.917339444160461426e-01 2.668973505496978760e-01 6.083813905715942383e-01 1.000000000000000000e+00 -8.927181959152221680e-01 2.595155835151672363e-01 6.037062406539916992e-01 1.000000000000000000e+00 -8.937024474143981934e-01 2.521337866783142090e-01 5.990311503410339355e-01 1.000000000000000000e+00 -8.946866393089294434e-01 2.447520196437835693e-01 5.943560004234313965e-01 1.000000000000000000e+00 -8.956708908081054688e-01 2.373702377080917358e-01 5.896809101104736328e-01 1.000000000000000000e+00 -8.966551423072814941e-01 2.299884706735610962e-01 5.850057601928710938e-01 1.000000000000000000e+00 -8.976393938064575195e-01 2.226066887378692627e-01 5.803306698799133301e-01 1.000000000000000000e+00 -8.986235857009887695e-01 2.152249068021774292e-01 5.756555199623107910e-01 1.000000000000000000e+00 -8.996078372001647949e-01 2.078431397676467896e-01 5.709803700447082520e-01 1.000000000000000000e+00 -9.005920886993408203e-01 2.004613578319549561e-01 5.663052797317504883e-01 1.000000000000000000e+00 -9.015763401985168457e-01 1.930795907974243164e-01 5.616301298141479492e-01 1.000000000000000000e+00 -9.025605320930480957e-01 1.856978088617324829e-01 5.569550395011901855e-01 1.000000000000000000e+00 -9.035447835922241211e-01 1.783160269260406494e-01 5.522798895835876465e-01 1.000000000000000000e+00 -9.045290350914001465e-01 1.709342598915100098e-01 5.476047396659851074e-01 1.000000000000000000e+00 -9.055132865905761719e-01 1.635524779558181763e-01 5.429296493530273438e-01 1.000000000000000000e+00 -9.039599895477294922e-01 1.590157598257064819e-01 5.371779799461364746e-01 1.000000000000000000e+00 -9.008842706680297852e-01 1.561860889196395874e-01 5.307804942131042480e-01 1.000000000000000000e+00 -8.978085517883300781e-01 1.533564031124114990e-01 5.243829488754272461e-01 1.000000000000000000e+00 -8.947327733039855957e-01 1.505267173051834106e-01 5.179854035377502441e-01 1.000000000000000000e+00 -8.916570544242858887e-01 1.476970463991165161e-01 5.115878582000732422e-01 1.000000000000000000e+00 -8.885813355445861816e-01 1.448673605918884277e-01 5.051903128623962402e-01 1.000000000000000000e+00 -8.855055570602416992e-01 1.420376747846603394e-01 4.987927675247192383e-01 1.000000000000000000e+00 -8.824298381805419922e-01 1.392080038785934448e-01 4.923952221870422363e-01 1.000000000000000000e+00 -8.793541193008422852e-01 1.363783180713653564e-01 4.859977066516876221e-01 1.000000000000000000e+00 -8.762783408164978027e-01 1.335486322641372681e-01 4.796001613140106201e-01 1.000000000000000000e+00 -8.732026219367980957e-01 1.307189613580703735e-01 4.732026159763336182e-01 1.000000000000000000e+00 -8.701269030570983887e-01 1.278892755508422852e-01 4.668050706386566162e-01 1.000000000000000000e+00 -8.670511245727539062e-01 1.250595897436141968e-01 4.604075253009796143e-01 1.000000000000000000e+00 -8.639754056930541992e-01 1.222299113869667053e-01 4.540100097656250000e-01 1.000000000000000000e+00 -8.608996272087097168e-01 1.194002330303192139e-01 4.476124644279479980e-01 1.000000000000000000e+00 -8.578239083290100098e-01 1.165705472230911255e-01 4.412149190902709961e-01 1.000000000000000000e+00 -8.547481894493103027e-01 1.137408688664436340e-01 4.348173737525939941e-01 1.000000000000000000e+00 -8.516724109649658203e-01 1.109111905097961426e-01 4.284198284149169922e-01 1.000000000000000000e+00 -8.485966920852661133e-01 1.080815047025680542e-01 4.220223128795623779e-01 1.000000000000000000e+00 -8.455209732055664062e-01 1.052518263459205627e-01 4.156247675418853760e-01 1.000000000000000000e+00 -8.424451947212219238e-01 1.024221479892730713e-01 4.092272222042083740e-01 1.000000000000000000e+00 -8.393694758415222168e-01 9.959246218204498291e-02 4.028296768665313721e-01 1.000000000000000000e+00 -8.362937569618225098e-01 9.676278382539749146e-02 3.964321315288543701e-01 1.000000000000000000e+00 -8.332179784774780273e-01 9.393310546875000000e-02 3.900346159934997559e-01 1.000000000000000000e+00 -8.301422595977783203e-01 9.110341966152191162e-02 3.836370706558227539e-01 1.000000000000000000e+00 -8.270665407180786133e-01 8.827374130487442017e-02 3.772395253181457520e-01 1.000000000000000000e+00 -8.239907622337341309e-01 8.544406294822692871e-02 3.708419799804687500e-01 1.000000000000000000e+00 -8.209150433540344238e-01 8.261437714099884033e-02 3.644444346427917480e-01 1.000000000000000000e+00 -8.178392648696899414e-01 7.978469878435134888e-02 3.580469191074371338e-01 1.000000000000000000e+00 -8.147635459899902344e-01 7.695502042770385742e-02 3.516493737697601318e-01 1.000000000000000000e+00 -8.116878271102905273e-01 7.412533462047576904e-02 3.452518284320831299e-01 1.000000000000000000e+00 -8.086120486259460449e-01 7.129565626382827759e-02 3.388542830944061279e-01 1.000000000000000000e+00 -8.028604388236999512e-01 6.892733275890350342e-02 3.355017304420471191e-01 1.000000000000000000e+00 -7.962168455123901367e-01 6.671280413866043091e-02 3.331641554832458496e-01 1.000000000000000000e+00 -7.895732522010803223e-01 6.449826806783676147e-02 3.308266103267669678e-01 1.000000000000000000e+00 -7.829296588897705078e-01 6.228373572230339050e-02 3.284890353679656982e-01 1.000000000000000000e+00 -7.762860655784606934e-01 6.006920337677001953e-02 3.261514902114868164e-01 1.000000000000000000e+00 -7.696424722671508789e-01 5.785467103123664856e-02 3.238139152526855469e-01 1.000000000000000000e+00 -7.629988193511962891e-01 5.564013868570327759e-02 3.214763700962066650e-01 1.000000000000000000e+00 -7.563552260398864746e-01 5.342560634016990662e-02 3.191387951374053955e-01 1.000000000000000000e+00 -7.497116327285766602e-01 5.121107399463653564e-02 3.168012201786041260e-01 1.000000000000000000e+00 -7.430680394172668457e-01 4.899654164910316467e-02 3.144636750221252441e-01 1.000000000000000000e+00 -7.364244461059570312e-01 4.678200557827949524e-02 3.121261000633239746e-01 1.000000000000000000e+00 -7.297808527946472168e-01 4.456747323274612427e-02 3.097885549068450928e-01 1.000000000000000000e+00 -7.231372594833374023e-01 4.235294088721275330e-02 3.074509799480438232e-01 1.000000000000000000e+00 -7.164936661720275879e-01 4.013840854167938232e-02 3.051134049892425537e-01 1.000000000000000000e+00 -7.098500728607177734e-01 3.792387619614601135e-02 3.027758598327636719e-01 1.000000000000000000e+00 -7.032064795494079590e-01 3.570934385061264038e-02 3.004382848739624023e-01 1.000000000000000000e+00 -6.965628862380981445e-01 3.349481150507926941e-02 2.981007397174835205e-01 1.000000000000000000e+00 -6.899192333221435547e-01 3.128027543425559998e-02 2.957631647586822510e-01 1.000000000000000000e+00 -6.832756400108337402e-01 2.906574308872222900e-02 2.934256196022033691e-01 1.000000000000000000e+00 -6.766320466995239258e-01 2.685121074318885803e-02 2.910880446434020996e-01 1.000000000000000000e+00 -6.699884533882141113e-01 2.463667839765548706e-02 2.887504696846008301e-01 1.000000000000000000e+00 -6.633448600769042969e-01 2.242214605212211609e-02 2.864129245281219482e-01 1.000000000000000000e+00 -6.567012667655944824e-01 2.020761184394359589e-02 2.840753495693206787e-01 1.000000000000000000e+00 -6.500576734542846680e-01 1.799307949841022491e-02 2.817378044128417969e-01 1.000000000000000000e+00 -6.434140801429748535e-01 1.577854715287685394e-02 2.794002294540405273e-01 1.000000000000000000e+00 -6.367704868316650391e-01 1.356401387602090836e-02 2.770626544952392578e-01 1.000000000000000000e+00 -6.301268935203552246e-01 1.134948059916496277e-02 2.747251093387603760e-01 1.000000000000000000e+00 -6.234833002090454102e-01 9.134948253631591797e-03 2.723875343799591064e-01 1.000000000000000000e+00 -6.168396472930908203e-01 6.920415442436933517e-03 2.700499892234802246e-01 1.000000000000000000e+00 -6.101960539817810059e-01 4.705882165580987930e-03 2.677124142646789551e-01 1.000000000000000000e+00 -6.035524606704711914e-01 2.491349587216973305e-03 2.653748691082000732e-01 1.000000000000000000e+00 -5.969088673591613770e-01 2.768166013993322849e-04 2.630372941493988037e-01 1.000000000000000000e+00 -5.908035635948181152e-01 0.000000000000000000e+00 2.588696777820587158e-01 1.000000000000000000e+00 -5.847750902175903320e-01 0.000000000000000000e+00 2.544406056404113770e-01 1.000000000000000000e+00 -5.787466168403625488e-01 0.000000000000000000e+00 2.500115334987640381e-01 1.000000000000000000e+00 -5.727182030677795410e-01 0.000000000000000000e+00 2.455824613571166992e-01 1.000000000000000000e+00 -5.666897296905517578e-01 0.000000000000000000e+00 2.411534041166305542e-01 1.000000000000000000e+00 -5.606612563133239746e-01 0.000000000000000000e+00 2.367243319749832153e-01 1.000000000000000000e+00 -5.546328425407409668e-01 0.000000000000000000e+00 2.322952747344970703e-01 1.000000000000000000e+00 -5.486043691635131836e-01 0.000000000000000000e+00 2.278662025928497314e-01 1.000000000000000000e+00 -5.425759553909301758e-01 0.000000000000000000e+00 2.234371453523635864e-01 1.000000000000000000e+00 -5.365474820137023926e-01 0.000000000000000000e+00 2.190080732107162476e-01 1.000000000000000000e+00 -5.305190086364746094e-01 0.000000000000000000e+00 2.145790010690689087e-01 1.000000000000000000e+00 -5.244905948638916016e-01 0.000000000000000000e+00 2.101499438285827637e-01 1.000000000000000000e+00 -5.184621214866638184e-01 0.000000000000000000e+00 2.057208716869354248e-01 1.000000000000000000e+00 -5.124337077140808105e-01 0.000000000000000000e+00 2.012918144464492798e-01 1.000000000000000000e+00 -5.064052343368530273e-01 0.000000000000000000e+00 1.968627423048019409e-01 1.000000000000000000e+00 -5.003767609596252441e-01 0.000000000000000000e+00 1.924336850643157959e-01 1.000000000000000000e+00 -4.943483173847198486e-01 0.000000000000000000e+00 1.880046129226684570e-01 1.000000000000000000e+00 -4.883198738098144531e-01 0.000000000000000000e+00 1.835755407810211182e-01 1.000000000000000000e+00 -4.822914302349090576e-01 0.000000000000000000e+00 1.791464835405349731e-01 1.000000000000000000e+00 -4.762629866600036621e-01 0.000000000000000000e+00 1.747174113988876343e-01 1.000000000000000000e+00 -4.702345132827758789e-01 0.000000000000000000e+00 1.702883541584014893e-01 1.000000000000000000e+00 -4.642060697078704834e-01 0.000000000000000000e+00 1.658592820167541504e-01 1.000000000000000000e+00 -4.581776261329650879e-01 0.000000000000000000e+00 1.614302247762680054e-01 1.000000000000000000e+00 -4.521491825580596924e-01 0.000000000000000000e+00 1.570011526346206665e-01 1.000000000000000000e+00 -4.461207091808319092e-01 0.000000000000000000e+00 1.525720804929733276e-01 1.000000000000000000e+00 -4.400922656059265137e-01 0.000000000000000000e+00 1.481430232524871826e-01 1.000000000000000000e+00 -4.340638220310211182e-01 0.000000000000000000e+00 1.437139511108398438e-01 1.000000000000000000e+00 -4.280353784561157227e-01 0.000000000000000000e+00 1.392848938703536987e-01 1.000000000000000000e+00 -4.220069348812103271e-01 0.000000000000000000e+00 1.348558217287063599e-01 1.000000000000000000e+00 -4.159784615039825439e-01 0.000000000000000000e+00 1.304267644882202148e-01 1.000000000000000000e+00 -4.099500179290771484e-01 0.000000000000000000e+00 1.259976923465728760e-01 1.000000000000000000e+00 -4.039215743541717529e-01 0.000000000000000000e+00 1.215686276555061340e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/Purples b/fastplotlib/utils/colormaps/Purples deleted file mode 100644 index 3d6079876..000000000 --- a/fastplotlib/utils/colormaps/Purples +++ /dev/null @@ -1,256 +0,0 @@ -9.882352948188781738e-01 9.843137264251708984e-01 9.921568632125854492e-01 1.000000000000000000e+00 -9.866359233856201172e-01 9.825913310050964355e-01 9.911726117134094238e-01 1.000000000000000000e+00 -9.850365519523620605e-01 9.808688759803771973e-01 9.901883602142333984e-01 1.000000000000000000e+00 -9.834371209144592285e-01 9.791464805603027344e-01 9.892041683197021484e-01 1.000000000000000000e+00 -9.818377494812011719e-01 9.774240851402282715e-01 9.882199168205261230e-01 1.000000000000000000e+00 -9.802383780479431152e-01 9.757016301155090332e-01 9.872356653213500977e-01 1.000000000000000000e+00 -9.786390066146850586e-01 9.739792346954345703e-01 9.862514138221740723e-01 1.000000000000000000e+00 -9.770395755767822266e-01 9.722568392753601074e-01 9.852672219276428223e-01 1.000000000000000000e+00 -9.754402041435241699e-01 9.705343842506408691e-01 9.842829704284667969e-01 1.000000000000000000e+00 -9.738408327102661133e-01 9.688119888305664062e-01 9.832987189292907715e-01 1.000000000000000000e+00 -9.722414612770080566e-01 9.670895934104919434e-01 9.823144674301147461e-01 1.000000000000000000e+00 -9.706420898437500000e-01 9.653671383857727051e-01 9.813302755355834961e-01 1.000000000000000000e+00 -9.690426588058471680e-01 9.636447429656982422e-01 9.803460240364074707e-01 1.000000000000000000e+00 -9.674432873725891113e-01 9.619223475456237793e-01 9.793617725372314453e-01 1.000000000000000000e+00 -9.658439159393310547e-01 9.601999521255493164e-01 9.783775210380554199e-01 1.000000000000000000e+00 -9.642445445060729980e-01 9.584774971008300781e-01 9.773933291435241699e-01 1.000000000000000000e+00 -9.626451134681701660e-01 9.567551016807556152e-01 9.764090776443481445e-01 1.000000000000000000e+00 -9.610457420349121094e-01 9.550327062606811523e-01 9.754248261451721191e-01 1.000000000000000000e+00 -9.594463706016540527e-01 9.533102512359619141e-01 9.744405746459960938e-01 1.000000000000000000e+00 -9.578469991683959961e-01 9.515878558158874512e-01 9.734563827514648438e-01 1.000000000000000000e+00 -9.562475681304931641e-01 9.498654603958129883e-01 9.724721312522888184e-01 1.000000000000000000e+00 -9.546481966972351074e-01 9.481430053710937500e-01 9.714878797531127930e-01 1.000000000000000000e+00 -9.530488252639770508e-01 9.464206099510192871e-01 9.705036282539367676e-01 1.000000000000000000e+00 -9.514494538307189941e-01 9.446982145309448242e-01 9.695194363594055176e-01 1.000000000000000000e+00 -9.498500823974609375e-01 9.429757595062255859e-01 9.685351848602294922e-01 1.000000000000000000e+00 -9.482506513595581055e-01 9.412533640861511230e-01 9.675509333610534668e-01 1.000000000000000000e+00 -9.466512799263000488e-01 9.395309686660766602e-01 9.665666818618774414e-01 1.000000000000000000e+00 -9.450519084930419922e-01 9.378085136413574219e-01 9.655824899673461914e-01 1.000000000000000000e+00 -9.434525370597839355e-01 9.360861182212829590e-01 9.645982384681701660e-01 1.000000000000000000e+00 -9.418531060218811035e-01 9.343637228012084961e-01 9.636139869689941406e-01 1.000000000000000000e+00 -9.402537345886230469e-01 9.326412677764892578e-01 9.626297354698181152e-01 1.000000000000000000e+00 -9.386543631553649902e-01 9.309188723564147949e-01 9.616455435752868652e-01 1.000000000000000000e+00 -9.369319677352905273e-01 9.291195869445800781e-01 9.606305360794067383e-01 1.000000000000000000e+00 -9.343483448028564453e-01 9.267820119857788086e-01 9.594002366065979004e-01 1.000000000000000000e+00 -9.317647218704223633e-01 9.244444370269775391e-01 9.581699371337890625e-01 1.000000000000000000e+00 -9.291810989379882812e-01 9.221068620681762695e-01 9.569396376609802246e-01 1.000000000000000000e+00 -9.265974760055541992e-01 9.197693467140197754e-01 9.557093381881713867e-01 1.000000000000000000e+00 -9.240138530731201172e-01 9.174317717552185059e-01 9.544790387153625488e-01 1.000000000000000000e+00 -9.214302301406860352e-01 9.150941967964172363e-01 9.532487392425537109e-01 1.000000000000000000e+00 -9.188466072082519531e-01 9.127566218376159668e-01 9.520184397697448730e-01 1.000000000000000000e+00 -9.162629842758178711e-01 9.104190468788146973e-01 9.507881402969360352e-01 1.000000000000000000e+00 -9.136793613433837891e-01 9.080815315246582031e-01 9.495578408241271973e-01 1.000000000000000000e+00 -9.110957384109497070e-01 9.057439565658569336e-01 9.483275413513183594e-01 1.000000000000000000e+00 -9.085121154785156250e-01 9.034063816070556641e-01 9.470972418785095215e-01 1.000000000000000000e+00 -9.059284925460815430e-01 9.010688066482543945e-01 9.458670020103454590e-01 1.000000000000000000e+00 -9.033448696136474609e-01 8.987312316894531250e-01 9.446367025375366211e-01 1.000000000000000000e+00 -9.007612466812133789e-01 8.963937163352966309e-01 9.434064030647277832e-01 1.000000000000000000e+00 -8.981776237487792969e-01 8.940561413764953613e-01 9.421761035919189453e-01 1.000000000000000000e+00 -8.955940008163452148e-01 8.917185664176940918e-01 9.409458041191101074e-01 1.000000000000000000e+00 -8.930103778839111328e-01 8.893809914588928223e-01 9.397155046463012695e-01 1.000000000000000000e+00 -8.904267549514770508e-01 8.870434165000915527e-01 9.384852051734924316e-01 1.000000000000000000e+00 -8.878431320190429688e-01 8.847059011459350586e-01 9.372549057006835938e-01 1.000000000000000000e+00 -8.852595090866088867e-01 8.823683261871337891e-01 9.360246062278747559e-01 1.000000000000000000e+00 -8.826758861541748047e-01 8.800307512283325195e-01 9.347943067550659180e-01 1.000000000000000000e+00 -8.800922632217407227e-01 8.776931762695312500e-01 9.335640072822570801e-01 1.000000000000000000e+00 -8.775086402893066406e-01 8.753556609153747559e-01 9.323337078094482422e-01 1.000000000000000000e+00 -8.749250173568725586e-01 8.730180859565734863e-01 9.311034083366394043e-01 1.000000000000000000e+00 -8.723413944244384766e-01 8.706805109977722168e-01 9.298731088638305664e-01 1.000000000000000000e+00 -8.697577714920043945e-01 8.683429360389709473e-01 9.286428093910217285e-01 1.000000000000000000e+00 -8.671741485595703125e-01 8.660053610801696777e-01 9.274125099182128906e-01 1.000000000000000000e+00 -8.645905256271362305e-01 8.636678457260131836e-01 9.261822104454040527e-01 1.000000000000000000e+00 -8.620069026947021484e-01 8.613302707672119141e-01 9.249519705772399902e-01 1.000000000000000000e+00 -8.594232797622680664e-01 8.589926958084106445e-01 9.237216711044311523e-01 1.000000000000000000e+00 -8.568396568298339844e-01 8.566551208496093750e-01 9.224913716316223145e-01 1.000000000000000000e+00 -8.539792299270629883e-01 8.540099859237670898e-01 9.211072921752929688e-01 1.000000000000000000e+00 -8.502883315086364746e-01 8.504421114921569824e-01 9.192618131637573242e-01 1.000000000000000000e+00 -8.465974330902099609e-01 8.468742966651916504e-01 9.174163937568664551e-01 1.000000000000000000e+00 -8.429065942764282227e-01 8.433064222335815430e-01 9.155709147453308105e-01 1.000000000000000000e+00 -8.392156958580017090e-01 8.397385478019714355e-01 9.137254953384399414e-01 1.000000000000000000e+00 -8.355247974395751953e-01 8.361707329750061035e-01 9.118800759315490723e-01 1.000000000000000000e+00 -8.318338990211486816e-01 8.326028585433959961e-01 9.100345969200134277e-01 1.000000000000000000e+00 -8.281430006027221680e-01 8.290349841117858887e-01 9.081891775131225586e-01 1.000000000000000000e+00 -8.244521617889404297e-01 8.254671096801757812e-01 9.063436985015869141e-01 1.000000000000000000e+00 -8.207612633705139160e-01 8.218992948532104492e-01 9.044982790946960449e-01 1.000000000000000000e+00 -8.170703649520874023e-01 8.183314204216003418e-01 9.026528000831604004e-01 1.000000000000000000e+00 -8.133794665336608887e-01 8.147635459899902344e-01 9.008073806762695312e-01 1.000000000000000000e+00 -8.096885681152343750e-01 8.111956715583801270e-01 8.989619612693786621e-01 1.000000000000000000e+00 -8.059976696968078613e-01 8.076278567314147949e-01 8.971164822578430176e-01 1.000000000000000000e+00 -8.023068308830261230e-01 8.040599822998046875e-01 8.952710628509521484e-01 1.000000000000000000e+00 -7.986159324645996094e-01 8.004921078681945801e-01 8.934255838394165039e-01 1.000000000000000000e+00 -7.949250340461730957e-01 7.969242334365844727e-01 8.915801644325256348e-01 1.000000000000000000e+00 -7.912341356277465820e-01 7.933564186096191406e-01 8.897347450256347656e-01 1.000000000000000000e+00 -7.875432372093200684e-01 7.897885441780090332e-01 8.878892660140991211e-01 1.000000000000000000e+00 -7.838523387908935547e-01 7.862206697463989258e-01 8.860438466072082520e-01 1.000000000000000000e+00 -7.801614999771118164e-01 7.826528549194335938e-01 8.841983675956726074e-01 1.000000000000000000e+00 -7.764706015586853027e-01 7.790849804878234863e-01 8.823529481887817383e-01 1.000000000000000000e+00 -7.727797031402587891e-01 7.755171060562133789e-01 8.805074691772460938e-01 1.000000000000000000e+00 -7.690888047218322754e-01 7.719492316246032715e-01 8.786620497703552246e-01 1.000000000000000000e+00 -7.653979063034057617e-01 7.683814167976379395e-01 8.768166303634643555e-01 1.000000000000000000e+00 -7.617070078849792480e-01 7.648135423660278320e-01 8.749711513519287109e-01 1.000000000000000000e+00 -7.580161690711975098e-01 7.612456679344177246e-01 8.731257319450378418e-01 1.000000000000000000e+00 -7.543252706527709961e-01 7.576777935028076172e-01 8.712802529335021973e-01 1.000000000000000000e+00 -7.506343722343444824e-01 7.541099786758422852e-01 8.694348335266113281e-01 1.000000000000000000e+00 -7.469434738159179688e-01 7.505421042442321777e-01 8.675894141197204590e-01 1.000000000000000000e+00 -7.432525753974914551e-01 7.469742298126220703e-01 8.657439351081848145e-01 1.000000000000000000e+00 -7.395617365837097168e-01 7.434063553810119629e-01 8.638985157012939453e-01 1.000000000000000000e+00 -7.358708381652832031e-01 7.395617365837097168e-01 8.618223667144775391e-01 1.000000000000000000e+00 -7.321799397468566895e-01 7.352556586265563965e-01 8.593617677688598633e-01 1.000000000000000000e+00 -7.284890413284301758e-01 7.309496402740478516e-01 8.569011688232421875e-01 1.000000000000000000e+00 -7.247981429100036621e-01 7.266436219215393066e-01 8.544406294822692871e-01 1.000000000000000000e+00 -7.211072444915771484e-01 7.223375439643859863e-01 8.519800305366516113e-01 1.000000000000000000e+00 -7.174164056777954102e-01 7.180315256118774414e-01 8.495194315910339355e-01 1.000000000000000000e+00 -7.137255072593688965e-01 7.137255072593688965e-01 8.470588326454162598e-01 1.000000000000000000e+00 -7.100346088409423828e-01 7.094194293022155762e-01 8.445982336997985840e-01 1.000000000000000000e+00 -7.063437104225158691e-01 7.051134109497070312e-01 8.421376347541809082e-01 1.000000000000000000e+00 -7.026528120040893555e-01 7.008073925971984863e-01 8.396770358085632324e-01 1.000000000000000000e+00 -6.989619135856628418e-01 6.965013742446899414e-01 8.372164368629455566e-01 1.000000000000000000e+00 -6.952710747718811035e-01 6.921952962875366211e-01 8.347558379173278809e-01 1.000000000000000000e+00 -6.915801763534545898e-01 6.878892779350280762e-01 8.322952985763549805e-01 1.000000000000000000e+00 -6.878892779350280762e-01 6.835832595825195312e-01 8.298346996307373047e-01 1.000000000000000000e+00 -6.841983795166015625e-01 6.792771816253662109e-01 8.273741006851196289e-01 1.000000000000000000e+00 -6.805074810981750488e-01 6.749711632728576660e-01 8.249135017395019531e-01 1.000000000000000000e+00 -6.768165826797485352e-01 6.706651449203491211e-01 8.224529027938842773e-01 1.000000000000000000e+00 -6.731257438659667969e-01 6.663590669631958008e-01 8.199923038482666016e-01 1.000000000000000000e+00 -6.694348454475402832e-01 6.620530486106872559e-01 8.175317049026489258e-01 1.000000000000000000e+00 -6.657439470291137695e-01 6.577470302581787109e-01 8.150711059570312500e-01 1.000000000000000000e+00 -6.620530486106872559e-01 6.534410119056701660e-01 8.126105070114135742e-01 1.000000000000000000e+00 -6.583621501922607422e-01 6.491349339485168457e-01 8.101499676704406738e-01 1.000000000000000000e+00 -6.546712517738342285e-01 6.448289155960083008e-01 8.076893687248229980e-01 1.000000000000000000e+00 -6.509804129600524902e-01 6.405228972434997559e-01 8.052287697792053223e-01 1.000000000000000000e+00 -6.472895145416259766e-01 6.362168192863464355e-01 8.027681708335876465e-01 1.000000000000000000e+00 -6.435986161231994629e-01 6.319108009338378906e-01 8.003075718879699707e-01 1.000000000000000000e+00 -6.399077177047729492e-01 6.276047825813293457e-01 7.978469729423522949e-01 1.000000000000000000e+00 -6.362168192863464355e-01 6.232987046241760254e-01 7.953863739967346191e-01 1.000000000000000000e+00 -6.325259804725646973e-01 6.189926862716674805e-01 7.929257750511169434e-01 1.000000000000000000e+00 -6.288350820541381836e-01 6.146866679191589355e-01 7.904651761054992676e-01 1.000000000000000000e+00 -6.251441836357116699e-01 6.103806495666503906e-01 7.880046367645263672e-01 1.000000000000000000e+00 -6.214532852172851562e-01 6.060745716094970703e-01 7.855440378189086914e-01 1.000000000000000000e+00 -6.177623867988586426e-01 6.021376252174377441e-01 7.834525108337402344e-01 1.000000000000000000e+00 -6.140714883804321289e-01 5.985698103904724121e-01 7.817301154136657715e-01 1.000000000000000000e+00 -6.103806495666503906e-01 5.950019359588623047e-01 7.800076603889465332e-01 1.000000000000000000e+00 -6.066897511482238770e-01 5.914340615272521973e-01 7.782852649688720703e-01 1.000000000000000000e+00 -6.029988527297973633e-01 5.878661870956420898e-01 7.765628695487976074e-01 1.000000000000000000e+00 -5.993079543113708496e-01 5.842983722686767578e-01 7.748404741287231445e-01 1.000000000000000000e+00 -5.956170558929443359e-01 5.807304978370666504e-01 7.731180191040039062e-01 1.000000000000000000e+00 -5.919261574745178223e-01 5.771626234054565430e-01 7.713956236839294434e-01 1.000000000000000000e+00 -5.882353186607360840e-01 5.735947489738464355e-01 7.696732282638549805e-01 1.000000000000000000e+00 -5.845444202423095703e-01 5.700269341468811035e-01 7.679507732391357422e-01 1.000000000000000000e+00 -5.808535218238830566e-01 5.664590597152709961e-01 7.662283778190612793e-01 1.000000000000000000e+00 -5.771626234054565430e-01 5.628911852836608887e-01 7.645059823989868164e-01 1.000000000000000000e+00 -5.734717249870300293e-01 5.593233108520507812e-01 7.627835273742675781e-01 1.000000000000000000e+00 -5.697808265686035156e-01 5.557554960250854492e-01 7.610611319541931152e-01 1.000000000000000000e+00 -5.660899877548217773e-01 5.521876215934753418e-01 7.593387365341186523e-01 1.000000000000000000e+00 -5.623990893363952637e-01 5.486197471618652344e-01 7.576162815093994141e-01 1.000000000000000000e+00 -5.587081909179687500e-01 5.450519323348999023e-01 7.558938860893249512e-01 1.000000000000000000e+00 -5.550172924995422363e-01 5.414840579032897949e-01 7.541714906692504883e-01 1.000000000000000000e+00 -5.513263940811157227e-01 5.379161834716796875e-01 7.524490356445312500e-01 1.000000000000000000e+00 -5.476354956626892090e-01 5.343483090400695801e-01 7.507266402244567871e-01 1.000000000000000000e+00 -5.439446568489074707e-01 5.307804942131042480e-01 7.490042448043823242e-01 1.000000000000000000e+00 -5.402537584304809570e-01 5.272126197814941406e-01 7.472817897796630859e-01 1.000000000000000000e+00 -5.365628600120544434e-01 5.236447453498840332e-01 7.455593943595886230e-01 1.000000000000000000e+00 -5.328719615936279297e-01 5.200768709182739258e-01 7.438369989395141602e-01 1.000000000000000000e+00 -5.291810631752014160e-01 5.165090560913085938e-01 7.421145439147949219e-01 1.000000000000000000e+00 -5.254902243614196777e-01 5.129411816596984863e-01 7.403921484947204590e-01 1.000000000000000000e+00 -5.217993259429931641e-01 5.093733072280883789e-01 7.386697530746459961e-01 1.000000000000000000e+00 -5.181084275245666504e-01 5.058054327964782715e-01 7.369473576545715332e-01 1.000000000000000000e+00 -5.144175291061401367e-01 5.022376179695129395e-01 7.352249026298522949e-01 1.000000000000000000e+00 -5.107266306877136230e-01 4.986697435379028320e-01 7.335025072097778320e-01 1.000000000000000000e+00 -5.070357322692871094e-01 4.951018691062927246e-01 7.317801117897033691e-01 1.000000000000000000e+00 -5.033448934555053711e-01 4.915340244770050049e-01 7.300576567649841309e-01 1.000000000000000000e+00 -5.002691149711608887e-01 4.868127703666687012e-01 7.276431918144226074e-01 1.000000000000000000e+00 -4.975624680519104004e-01 4.813994765281677246e-01 7.248135209083557129e-01 1.000000000000000000e+00 -4.948558211326599121e-01 4.759861528873443604e-01 7.219838500022888184e-01 1.000000000000000000e+00 -4.921491742134094238e-01 4.705728590488433838e-01 7.191541790962219238e-01 1.000000000000000000e+00 -4.894425272941589355e-01 4.651595652103424072e-01 7.163245081901550293e-01 1.000000000000000000e+00 -4.867358803749084473e-01 4.597462415695190430e-01 7.134948372840881348e-01 1.000000000000000000e+00 -4.840292334556579590e-01 4.543329477310180664e-01 7.106651067733764648e-01 1.000000000000000000e+00 -4.813225567340850830e-01 4.489196538925170898e-01 7.078354358673095703e-01 1.000000000000000000e+00 -4.786159098148345947e-01 4.435063302516937256e-01 7.050057649612426758e-01 1.000000000000000000e+00 -4.759092628955841064e-01 4.380930364131927490e-01 7.021760940551757812e-01 1.000000000000000000e+00 -4.732026159763336182e-01 4.326797425746917725e-01 6.993464231491088867e-01 1.000000000000000000e+00 -4.704959690570831299e-01 4.272664487361907959e-01 6.965167522430419922e-01 1.000000000000000000e+00 -4.677893221378326416e-01 4.218531250953674316e-01 6.936870217323303223e-01 1.000000000000000000e+00 -4.650826752185821533e-01 4.164398312568664551e-01 6.908573508262634277e-01 1.000000000000000000e+00 -4.623759984970092773e-01 4.110265374183654785e-01 6.880276799201965332e-01 1.000000000000000000e+00 -4.596693515777587891e-01 4.056132137775421143e-01 6.851980090141296387e-01 1.000000000000000000e+00 -4.569627046585083008e-01 4.001999199390411377e-01 6.823683381080627441e-01 1.000000000000000000e+00 -4.542560577392578125e-01 3.947866261005401611e-01 6.795386672019958496e-01 1.000000000000000000e+00 -4.515494108200073242e-01 3.893733322620391846e-01 6.767089366912841797e-01 1.000000000000000000e+00 -4.488427639007568359e-01 3.839600086212158203e-01 6.738792657852172852e-01 1.000000000000000000e+00 -4.461360871791839600e-01 3.785467147827148438e-01 6.710495948791503906e-01 1.000000000000000000e+00 -4.434294402599334717e-01 3.731334209442138672e-01 6.682199239730834961e-01 1.000000000000000000e+00 -4.407227933406829834e-01 3.677200973033905029e-01 6.653902530670166016e-01 1.000000000000000000e+00 -4.380161464214324951e-01 3.623068034648895264e-01 6.625605821609497070e-01 1.000000000000000000e+00 -4.353094995021820068e-01 3.568935096263885498e-01 6.597308516502380371e-01 1.000000000000000000e+00 -4.326028525829315186e-01 3.514801859855651855e-01 6.569011807441711426e-01 1.000000000000000000e+00 -4.298962056636810303e-01 3.460668921470642090e-01 6.540715098381042480e-01 1.000000000000000000e+00 -4.271895289421081543e-01 3.406535983085632324e-01 6.512418389320373535e-01 1.000000000000000000e+00 -4.244828820228576660e-01 3.352403044700622559e-01 6.484121680259704590e-01 1.000000000000000000e+00 -4.217762351036071777e-01 3.298269808292388916e-01 6.455824971199035645e-01 1.000000000000000000e+00 -4.190695881843566895e-01 3.244136869907379150e-01 6.427527666091918945e-01 1.000000000000000000e+00 -4.163629412651062012e-01 3.190003931522369385e-01 6.399230957031250000e-01 1.000000000000000000e+00 -4.136562943458557129e-01 3.137716352939605713e-01 6.373702287673950195e-01 1.000000000000000000e+00 -4.109496474266052246e-01 3.086043894290924072e-01 6.349096298217773438e-01 1.000000000000000000e+00 -4.082429707050323486e-01 3.034371435642242432e-01 6.324490308761596680e-01 1.000000000000000000e+00 -4.055363237857818604e-01 2.982698976993560791e-01 6.299884915351867676e-01 1.000000000000000000e+00 -4.028296768665313721e-01 2.931026518344879150e-01 6.275278925895690918e-01 1.000000000000000000e+00 -4.001230299472808838e-01 2.879354059696197510e-01 6.250672936439514160e-01 1.000000000000000000e+00 -3.974163830280303955e-01 2.827681601047515869e-01 6.226066946983337402e-01 1.000000000000000000e+00 -3.947097361087799072e-01 2.776009142398834229e-01 6.201460957527160645e-01 1.000000000000000000e+00 -3.920030891895294189e-01 2.724336683750152588e-01 6.176854968070983887e-01 1.000000000000000000e+00 -3.892964124679565430e-01 2.672664225101470947e-01 6.152248978614807129e-01 1.000000000000000000e+00 -3.865897655487060547e-01 2.620992064476013184e-01 6.127642989158630371e-01 1.000000000000000000e+00 -3.838831186294555664e-01 2.569319605827331543e-01 6.103036999702453613e-01 1.000000000000000000e+00 -3.811764717102050781e-01 2.517647147178649902e-01 6.078431606292724609e-01 1.000000000000000000e+00 -3.784698247909545898e-01 2.465974688529968262e-01 6.053825616836547852e-01 1.000000000000000000e+00 -3.757631778717041016e-01 2.414302229881286621e-01 6.029219627380371094e-01 1.000000000000000000e+00 -3.730565309524536133e-01 2.362629771232604980e-01 6.004613637924194336e-01 1.000000000000000000e+00 -3.703498542308807373e-01 2.310957312583923340e-01 5.980007648468017578e-01 1.000000000000000000e+00 -3.676432073116302490e-01 2.259284853935241699e-01 5.955401659011840820e-01 1.000000000000000000e+00 -3.649365603923797607e-01 2.207612395286560059e-01 5.930795669555664062e-01 1.000000000000000000e+00 -3.622299134731292725e-01 2.155940085649490356e-01 5.906189680099487305e-01 1.000000000000000000e+00 -3.595232665538787842e-01 2.104267627000808716e-01 5.881584286689758301e-01 1.000000000000000000e+00 -3.568166196346282959e-01 2.052595168352127075e-01 5.856978297233581543e-01 1.000000000000000000e+00 -3.541099429130554199e-01 2.000922709703445435e-01 5.832372307777404785e-01 1.000000000000000000e+00 -3.514032959938049316e-01 1.949250251054763794e-01 5.807766318321228027e-01 1.000000000000000000e+00 -3.486966490745544434e-01 1.897577792406082153e-01 5.783160328865051270e-01 1.000000000000000000e+00 -3.459900021553039551e-01 1.845905482769012451e-01 5.758554339408874512e-01 1.000000000000000000e+00 -3.432833552360534668e-01 1.794233024120330811e-01 5.733948349952697754e-01 1.000000000000000000e+00 -3.405767083168029785e-01 1.742560565471649170e-01 5.709342360496520996e-01 1.000000000000000000e+00 -3.378700613975524902e-01 1.690888106822967529e-01 5.684736371040344238e-01 1.000000000000000000e+00 -3.351633846759796143e-01 1.639215648174285889e-01 5.660130977630615234e-01 1.000000000000000000e+00 -3.324567377567291260e-01 1.587543189525604248e-01 5.635524988174438477e-01 1.000000000000000000e+00 -3.297500908374786377e-01 1.535870879888534546e-01 5.610918998718261719e-01 1.000000000000000000e+00 -3.271510899066925049e-01 1.487427949905395508e-01 5.588465929031372070e-01 1.000000000000000000e+00 -3.245674669742584229e-01 1.439446359872817993e-01 5.566320419311523438e-01 1.000000000000000000e+00 -3.219838440418243408e-01 1.391464769840240479e-01 5.544175505638122559e-01 1.000000000000000000e+00 -3.194002211093902588e-01 1.343483328819274902e-01 5.522029995918273926e-01 1.000000000000000000e+00 -3.168165981769561768e-01 1.295501738786697388e-01 5.499884486198425293e-01 1.000000000000000000e+00 -3.142329752445220947e-01 1.247520148754119873e-01 5.477739572525024414e-01 1.000000000000000000e+00 -3.116493523120880127e-01 1.199538633227348328e-01 5.455594062805175781e-01 1.000000000000000000e+00 -3.090657293796539307e-01 1.151557117700576782e-01 5.433448553085327148e-01 1.000000000000000000e+00 -3.064821362495422363e-01 1.103575527667999268e-01 5.411303639411926270e-01 1.000000000000000000e+00 -3.038985133171081543e-01 1.055594012141227722e-01 5.389158129692077637e-01 1.000000000000000000e+00 -3.013148903846740723e-01 1.007612422108650208e-01 5.367012619972229004e-01 1.000000000000000000e+00 -2.987312674522399902e-01 9.596309065818786621e-02 5.344867110252380371e-01 1.000000000000000000e+00 -2.961476445198059082e-01 9.116493910551071167e-02 5.322722196578979492e-01 1.000000000000000000e+00 -2.935640215873718262e-01 8.636678010225296021e-02 5.300576686859130859e-01 1.000000000000000000e+00 -2.909803986549377441e-01 8.156862854957580566e-02 5.278431177139282227e-01 1.000000000000000000e+00 -2.883967757225036621e-01 7.677046954631805420e-02 5.256286263465881348e-01 1.000000000000000000e+00 -2.858131527900695801e-01 7.197231799364089966e-02 5.234140753746032715e-01 1.000000000000000000e+00 -2.832295298576354980e-01 6.717416644096374512e-02 5.211995244026184082e-01 1.000000000000000000e+00 -2.806459069252014160e-01 6.237600743770599365e-02 5.189850330352783203e-01 1.000000000000000000e+00 -2.780622839927673340e-01 5.757785588502883911e-02 5.167704820632934570e-01 1.000000000000000000e+00 -2.754786610603332520e-01 5.277970060706138611e-02 5.145559310913085938e-01 1.000000000000000000e+00 -2.728950381278991699e-01 4.798154532909393311e-02 5.123413801193237305e-01 1.000000000000000000e+00 -2.703114151954650879e-01 4.318339005112648010e-02 5.101268887519836426e-01 1.000000000000000000e+00 -2.677277922630310059e-01 3.838523477315902710e-02 5.079123377799987793e-01 1.000000000000000000e+00 -2.651441693305969238e-01 3.358708322048187256e-02 5.056977868080139160e-01 1.000000000000000000e+00 -2.625605463981628418e-01 2.878892794251441956e-02 5.034832954406738281e-01 1.000000000000000000e+00 -2.599769234657287598e-01 2.399077266454696655e-02 5.012687444686889648e-01 1.000000000000000000e+00 -2.573933005332946777e-01 1.919261738657951355e-02 4.990542232990264893e-01 1.000000000000000000e+00 -2.548096776008605957e-01 1.439446397125720978e-02 4.968396723270416260e-01 1.000000000000000000e+00 -2.522260546684265137e-01 9.596308693289756775e-03 4.946251511573791504e-01 1.000000000000000000e+00 -2.496424466371536255e-01 4.798154346644878387e-03 4.924106001853942871e-01 1.000000000000000000e+00 -2.470588237047195435e-01 0.000000000000000000e+00 4.901960790157318115e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/RdBu b/fastplotlib/utils/colormaps/RdBu deleted file mode 100644 index 6482fa00f..000000000 --- a/fastplotlib/utils/colormaps/RdBu +++ /dev/null @@ -1,256 +0,0 @@ -4.039215743541717529e-01 0.000000000000000000e+00 1.215686276555061340e-01 1.000000000000000000e+00 -4.154555797576904297e-01 3.690888173878192902e-03 1.234140694141387939e-01 1.000000000000000000e+00 -4.269896149635314941e-01 7.381776347756385803e-03 1.252595186233520508e-01 1.000000000000000000e+00 -4.385236501693725586e-01 1.107266452163457870e-02 1.271049529314041138e-01 1.000000000000000000e+00 -4.500576555728912354e-01 1.476355269551277161e-02 1.289504021406173706e-01 1.000000000000000000e+00 -4.615916907787322998e-01 1.845443993806838989e-02 1.307958513498306274e-01 1.000000000000000000e+00 -4.731257259845733643e-01 2.214532904326915741e-02 1.326412856578826904e-01 1.000000000000000000e+00 -4.846597313880920410e-01 2.583621628582477570e-02 1.344867348670959473e-01 1.000000000000000000e+00 -4.961937665939331055e-01 2.952710539102554321e-02 1.363321840763092041e-01 1.000000000000000000e+00 -5.077278017997741699e-01 3.321799263358116150e-02 1.381776183843612671e-01 1.000000000000000000e+00 -5.192618370056152344e-01 3.690887987613677979e-02 1.400230675935745239e-01 1.000000000000000000e+00 -5.307958722114562988e-01 4.059977084398269653e-02 1.418685168027877808e-01 1.000000000000000000e+00 -5.423298478126525879e-01 4.429065808653831482e-02 1.437139511108398438e-01 1.000000000000000000e+00 -5.538638830184936523e-01 4.798154532909393311e-02 1.455594003200531006e-01 1.000000000000000000e+00 -5.653979182243347168e-01 5.167243257164955139e-02 1.474048495292663574e-01 1.000000000000000000e+00 -5.769319534301757812e-01 5.536332353949546814e-02 1.492502838373184204e-01 1.000000000000000000e+00 -5.884659886360168457e-01 5.905421078205108643e-02 1.510957330465316772e-01 1.000000000000000000e+00 -6.000000238418579102e-01 6.274510174989700317e-02 1.529411822557449341e-01 1.000000000000000000e+00 -6.115339994430541992e-01 6.643598526716232300e-02 1.547866165637969971e-01 1.000000000000000000e+00 -6.230680346488952637e-01 7.012687623500823975e-02 1.566320657730102539e-01 1.000000000000000000e+00 -6.346020698547363281e-01 7.381775975227355957e-02 1.584775149822235107e-01 1.000000000000000000e+00 -6.461361050605773926e-01 7.750865072011947632e-02 1.603229492902755737e-01 1.000000000000000000e+00 -6.576701402664184570e-01 8.119954168796539307e-02 1.621683984994888306e-01 1.000000000000000000e+00 -6.692041754722595215e-01 8.489042520523071289e-02 1.640138477087020874e-01 1.000000000000000000e+00 -6.807381510734558105e-01 8.858131617307662964e-02 1.658592820167541504e-01 1.000000000000000000e+00 -6.922721862792968750e-01 9.227219969034194946e-02 1.677047312259674072e-01 1.000000000000000000e+00 -7.008073925971984863e-01 9.965398162603378296e-02 1.712418347597122192e-01 1.000000000000000000e+00 -7.063437104225158691e-01 1.107266470789909363e-01 1.764705926179885864e-01 1.000000000000000000e+00 -7.118800282478332520e-01 1.217993050813674927e-01 1.816993504762649536e-01 1.000000000000000000e+00 -7.174164056777954102e-01 1.328719705343246460e-01 1.869281083345413208e-01 1.000000000000000000e+00 -7.229527235031127930e-01 1.439446359872817993e-01 1.921568661928176880e-01 1.000000000000000000e+00 -7.284890413284301758e-01 1.550173014402389526e-01 1.973856240510940552e-01 1.000000000000000000e+00 -7.340253591537475586e-01 1.660899668931961060e-01 2.026143819093704224e-01 1.000000000000000000e+00 -7.395617365837097168e-01 1.771626323461532593e-01 2.078431397676467896e-01 1.000000000000000000e+00 -7.450980544090270996e-01 1.882352977991104126e-01 2.130718976259231567e-01 1.000000000000000000e+00 -7.506343722343444824e-01 1.993079632520675659e-01 2.183006554841995239e-01 1.000000000000000000e+00 -7.561706900596618652e-01 2.103806287050247192e-01 2.235294133424758911e-01 1.000000000000000000e+00 -7.617070078849792480e-01 2.214532941579818726e-01 2.287581712007522583e-01 1.000000000000000000e+00 -7.672433853149414062e-01 2.325259447097778320e-01 2.339869290590286255e-01 1.000000000000000000e+00 -7.727797031402587891e-01 2.435986101627349854e-01 2.392156869173049927e-01 1.000000000000000000e+00 -7.783160209655761719e-01 2.546712756156921387e-01 2.444444447755813599e-01 1.000000000000000000e+00 -7.838523387908935547e-01 2.657439410686492920e-01 2.496732026338577271e-01 1.000000000000000000e+00 -7.893887162208557129e-01 2.768166065216064453e-01 2.549019753932952881e-01 1.000000000000000000e+00 -7.949250340461730957e-01 2.878892719745635986e-01 2.601307332515716553e-01 1.000000000000000000e+00 -8.004613518714904785e-01 2.989619374275207520e-01 2.653594911098480225e-01 1.000000000000000000e+00 -8.059976696968078613e-01 3.100346028804779053e-01 2.705882489681243896e-01 1.000000000000000000e+00 -8.115340471267700195e-01 3.211072683334350586e-01 2.758170068264007568e-01 1.000000000000000000e+00 -8.170703649520874023e-01 3.321799337863922119e-01 2.810457646846771240e-01 1.000000000000000000e+00 -8.226066827774047852e-01 3.432525992393493652e-01 2.862745225429534912e-01 1.000000000000000000e+00 -8.281430006027221680e-01 3.543252646923065186e-01 2.915032804012298584e-01 1.000000000000000000e+00 -8.336793780326843262e-01 3.653979301452636719e-01 2.967320382595062256e-01 1.000000000000000000e+00 -8.392156958580017090e-01 3.764705955982208252e-01 3.019607961177825928e-01 1.000000000000000000e+00 -8.438292741775512695e-01 3.870818912982940674e-01 3.101114928722381592e-01 1.000000000000000000e+00 -8.484429121017456055e-01 3.976931869983673096e-01 3.182622194290161133e-01 1.000000000000000000e+00 -8.530564904212951660e-01 4.083045125007629395e-01 3.264129161834716797e-01 1.000000000000000000e+00 -8.576701283454895020e-01 4.189158082008361816e-01 3.345636427402496338e-01 1.000000000000000000e+00 -8.622837662696838379e-01 4.295271039009094238e-01 3.427143394947052002e-01 1.000000000000000000e+00 -8.668973445892333984e-01 4.401383996009826660e-01 3.508650660514831543e-01 1.000000000000000000e+00 -8.715109825134277344e-01 4.507497251033782959e-01 3.590157628059387207e-01 1.000000000000000000e+00 -8.761245608329772949e-01 4.613610208034515381e-01 3.671664595603942871e-01 1.000000000000000000e+00 -8.807381987571716309e-01 4.719723165035247803e-01 3.753171861171722412e-01 1.000000000000000000e+00 -8.853517770767211914e-01 4.825836122035980225e-01 3.834678828716278076e-01 1.000000000000000000e+00 -8.899654150009155273e-01 4.931949377059936523e-01 3.916186094284057617e-01 1.000000000000000000e+00 -8.945789933204650879e-01 5.038062334060668945e-01 3.997693061828613281e-01 1.000000000000000000e+00 -8.991926312446594238e-01 5.144175291061401367e-01 4.079200327396392822e-01 1.000000000000000000e+00 -9.038062095642089844e-01 5.250288248062133789e-01 4.160707294940948486e-01 1.000000000000000000e+00 -9.084198474884033203e-01 5.356401205062866211e-01 4.242214560508728027e-01 1.000000000000000000e+00 -9.130334258079528809e-01 5.462514162063598633e-01 4.323721528053283691e-01 1.000000000000000000e+00 -9.176470637321472168e-01 5.568627715110778809e-01 4.405228793621063232e-01 1.000000000000000000e+00 -9.222606420516967773e-01 5.674740672111511230e-01 4.486735761165618896e-01 1.000000000000000000e+00 -9.268742799758911133e-01 5.780853629112243652e-01 4.568243026733398438e-01 1.000000000000000000e+00 -9.314879179000854492e-01 5.886966586112976074e-01 4.649749994277954102e-01 1.000000000000000000e+00 -9.361014962196350098e-01 5.993079543113708496e-01 4.731257259845733643e-01 1.000000000000000000e+00 -9.407151341438293457e-01 6.099192500114440918e-01 4.812764227390289307e-01 1.000000000000000000e+00 -9.453287124633789062e-01 6.205305457115173340e-01 4.894271492958068848e-01 1.000000000000000000e+00 -9.499423503875732422e-01 6.311418414115905762e-01 4.975778460502624512e-01 1.000000000000000000e+00 -9.545559287071228027e-01 6.417531967163085938e-01 5.057285428047180176e-01 1.000000000000000000e+00 -9.575547575950622559e-01 6.512110829353332520e-01 5.151095986366271973e-01 1.000000000000000000e+00 -9.589388966560363770e-01 6.595155596733093262e-01 5.257208943367004395e-01 1.000000000000000000e+00 -9.603229761123657227e-01 6.678200960159301758e-01 5.363321900367736816e-01 1.000000000000000000e+00 -9.617070555686950684e-01 6.761245727539062500e-01 5.469434857368469238e-01 1.000000000000000000e+00 -9.630911350250244141e-01 6.844290494918823242e-01 5.575547814369201660e-01 1.000000000000000000e+00 -9.644752144813537598e-01 6.927335858345031738e-01 5.681660771369934082e-01 1.000000000000000000e+00 -9.658592939376831055e-01 7.010380625724792480e-01 5.787773728370666504e-01 1.000000000000000000e+00 -9.672433733940124512e-01 7.093425393104553223e-01 5.893886685371398926e-01 1.000000000000000000e+00 -9.686274528503417969e-01 7.176470756530761719e-01 6.000000238418579102e-01 1.000000000000000000e+00 -9.700115323066711426e-01 7.259515523910522461e-01 6.106113195419311523e-01 1.000000000000000000e+00 -9.713956117630004883e-01 7.342560291290283203e-01 6.212226152420043945e-01 1.000000000000000000e+00 -9.727796912193298340e-01 7.425605654716491699e-01 6.318339109420776367e-01 1.000000000000000000e+00 -9.741637706756591797e-01 7.508650422096252441e-01 6.424452066421508789e-01 1.000000000000000000e+00 -9.755478501319885254e-01 7.591695785522460938e-01 6.530565023422241211e-01 1.000000000000000000e+00 -9.769319295883178711e-01 7.674740552902221680e-01 6.636677980422973633e-01 1.000000000000000000e+00 -9.783160090446472168e-01 7.757785320281982422e-01 6.742790937423706055e-01 1.000000000000000000e+00 -9.797000885009765625e-01 7.840830683708190918e-01 6.848904490470886230e-01 1.000000000000000000e+00 -9.810842275619506836e-01 7.923875451087951660e-01 6.955017447471618652e-01 1.000000000000000000e+00 -9.824683070182800293e-01 8.006920218467712402e-01 7.061130404472351074e-01 1.000000000000000000e+00 -9.838523864746093750e-01 8.089965581893920898e-01 7.167243361473083496e-01 1.000000000000000000e+00 -9.852364659309387207e-01 8.173010349273681641e-01 7.273356318473815918e-01 1.000000000000000000e+00 -9.866205453872680664e-01 8.256055116653442383e-01 7.379469275474548340e-01 1.000000000000000000e+00 -9.880046248435974121e-01 8.339100480079650879e-01 7.485582232475280762e-01 1.000000000000000000e+00 -9.893887042999267578e-01 8.422145247459411621e-01 7.591695785522460938e-01 1.000000000000000000e+00 -9.907727837562561035e-01 8.505190014839172363e-01 7.697808742523193359e-01 1.000000000000000000e+00 -9.921568632125854492e-01 8.588235378265380859e-01 7.803921699523925781e-01 1.000000000000000000e+00 -9.912341237068176270e-01 8.631295561790466309e-01 7.877739071846008301e-01 1.000000000000000000e+00 -9.903114438056945801e-01 8.674355745315551758e-01 7.951557040214538574e-01 1.000000000000000000e+00 -9.893887042999267578e-01 8.717416524887084961e-01 8.025375008583068848e-01 1.000000000000000000e+00 -9.884659647941589355e-01 8.760476708412170410e-01 8.099192380905151367e-01 1.000000000000000000e+00 -9.875432252883911133e-01 8.803536891937255859e-01 8.173010349273681641e-01 1.000000000000000000e+00 -9.866205453872680664e-01 8.846597671508789062e-01 8.246828317642211914e-01 1.000000000000000000e+00 -9.856978058815002441e-01 8.889657855033874512e-01 8.320645689964294434e-01 1.000000000000000000e+00 -9.847750663757324219e-01 8.932718038558959961e-01 8.394463658332824707e-01 1.000000000000000000e+00 -9.838523864746093750e-01 8.975778818130493164e-01 8.468281626701354980e-01 1.000000000000000000e+00 -9.829296469688415527e-01 9.018839001655578613e-01 8.542098999023437500e-01 1.000000000000000000e+00 -9.820069074630737305e-01 9.061899185180664062e-01 8.615916967391967773e-01 1.000000000000000000e+00 -9.810842275619506836e-01 9.104959368705749512e-01 8.689734935760498047e-01 1.000000000000000000e+00 -9.801614880561828613e-01 9.148020148277282715e-01 8.763552308082580566e-01 1.000000000000000000e+00 -9.792387485504150391e-01 9.191080331802368164e-01 8.837370276451110840e-01 1.000000000000000000e+00 -9.783160090446472168e-01 9.234140515327453613e-01 8.911188244819641113e-01 1.000000000000000000e+00 -9.773933291435241699e-01 9.277201294898986816e-01 8.985005617141723633e-01 1.000000000000000000e+00 -9.764705896377563477e-01 9.320261478424072266e-01 9.058823585510253906e-01 1.000000000000000000e+00 -9.755478501319885254e-01 9.363321661949157715e-01 9.132641553878784180e-01 1.000000000000000000e+00 -9.746251702308654785e-01 9.406382441520690918e-01 9.206458926200866699e-01 1.000000000000000000e+00 -9.737024307250976562e-01 9.449442625045776367e-01 9.280276894569396973e-01 1.000000000000000000e+00 -9.727796912193298340e-01 9.492502808570861816e-01 9.354094862937927246e-01 1.000000000000000000e+00 -9.718569517135620117e-01 9.535562992095947266e-01 9.427912235260009766e-01 1.000000000000000000e+00 -9.709342718124389648e-01 9.578623771667480469e-01 9.501730203628540039e-01 1.000000000000000000e+00 -9.700115323066711426e-01 9.621683955192565918e-01 9.575547575950622559e-01 1.000000000000000000e+00 -9.690887928009033203e-01 9.664744138717651367e-01 9.649365544319152832e-01 1.000000000000000000e+00 -9.657055139541625977e-01 9.672433733940124512e-01 9.680892229080200195e-01 1.000000000000000000e+00 -9.598615765571594238e-01 9.644752144813537598e-01 9.670127034187316895e-01 1.000000000000000000e+00 -9.540176987648010254e-01 9.617070555686950684e-01 9.659361839294433594e-01 1.000000000000000000e+00 -9.481737613677978516e-01 9.589388966560363770e-01 9.648596644401550293e-01 1.000000000000000000e+00 -9.423298835754394531e-01 9.561706781387329102e-01 9.637831449508666992e-01 1.000000000000000000e+00 -9.364859461784362793e-01 9.534025192260742188e-01 9.627066254615783691e-01 1.000000000000000000e+00 -9.306420683860778809e-01 9.506343603134155273e-01 9.616301655769348145e-01 1.000000000000000000e+00 -9.247981309890747070e-01 9.478662014007568359e-01 9.605536460876464844e-01 1.000000000000000000e+00 -9.189542531967163086e-01 9.450980424880981445e-01 9.594771265983581543e-01 1.000000000000000000e+00 -9.131103157997131348e-01 9.423298835754394531e-01 9.584006071090698242e-01 1.000000000000000000e+00 -9.072664380073547363e-01 9.395617246627807617e-01 9.573240876197814941e-01 1.000000000000000000e+00 -9.014225006103515625e-01 9.367935657501220703e-01 9.562475681304931641e-01 1.000000000000000000e+00 -8.955786228179931641e-01 9.340253472328186035e-01 9.551711082458496094e-01 1.000000000000000000e+00 -8.897347450256347656e-01 9.312571883201599121e-01 9.540945887565612793e-01 1.000000000000000000e+00 -8.838908076286315918e-01 9.284890294075012207e-01 9.530180692672729492e-01 1.000000000000000000e+00 -8.780469298362731934e-01 9.257208704948425293e-01 9.519415497779846191e-01 1.000000000000000000e+00 -8.722029924392700195e-01 9.229527115821838379e-01 9.508650302886962891e-01 1.000000000000000000e+00 -8.663591146469116211e-01 9.201845526695251465e-01 9.497885704040527344e-01 1.000000000000000000e+00 -8.605151772499084473e-01 9.174163937568664551e-01 9.487120509147644043e-01 1.000000000000000000e+00 -8.546712994575500488e-01 9.146482348442077637e-01 9.476355314254760742e-01 1.000000000000000000e+00 -8.488273620605468750e-01 9.118800759315490723e-01 9.465590119361877441e-01 1.000000000000000000e+00 -8.429834842681884766e-01 9.091118574142456055e-01 9.454824924468994141e-01 1.000000000000000000e+00 -8.371395468711853027e-01 9.063436985015869141e-01 9.444059729576110840e-01 1.000000000000000000e+00 -8.312956690788269043e-01 9.035755395889282227e-01 9.433295130729675293e-01 1.000000000000000000e+00 -8.254517316818237305e-01 9.008073806762695312e-01 9.422529935836791992e-01 1.000000000000000000e+00 -8.196078538894653320e-01 8.980392217636108398e-01 9.411764740943908691e-01 1.000000000000000000e+00 -8.099192380905151367e-01 8.931180238723754883e-01 9.384083151817321777e-01 1.000000000000000000e+00 -8.002306818962097168e-01 8.881968259811401367e-01 9.356401562690734863e-01 1.000000000000000000e+00 -7.905421257019042969e-01 8.832756876945495605e-01 9.328719973564147949e-01 1.000000000000000000e+00 -7.808535099029541016e-01 8.783544898033142090e-01 9.301037788391113281e-01 1.000000000000000000e+00 -7.711649537086486816e-01 8.734332919120788574e-01 9.273356199264526367e-01 1.000000000000000000e+00 -7.614763379096984863e-01 8.685120940208435059e-01 9.245674610137939453e-01 1.000000000000000000e+00 -7.517877817153930664e-01 8.635909557342529297e-01 9.217993021011352539e-01 1.000000000000000000e+00 -7.420991659164428711e-01 8.586697578430175781e-01 9.190311431884765625e-01 1.000000000000000000e+00 -7.324106097221374512e-01 8.537485599517822266e-01 9.162629842758178711e-01 1.000000000000000000e+00 -7.227220535278320312e-01 8.488273620605468750e-01 9.134948253631591797e-01 1.000000000000000000e+00 -7.130334377288818359e-01 8.439061641693115234e-01 9.107266664505004883e-01 1.000000000000000000e+00 -7.033448815345764160e-01 8.389850258827209473e-01 9.079584479331970215e-01 1.000000000000000000e+00 -6.936562657356262207e-01 8.340638279914855957e-01 9.051902890205383301e-01 1.000000000000000000e+00 -6.839677095413208008e-01 8.291426301002502441e-01 9.024221301078796387e-01 1.000000000000000000e+00 -6.742790937423706055e-01 8.242214322090148926e-01 8.996539711952209473e-01 1.000000000000000000e+00 -6.645905375480651855e-01 8.193002939224243164e-01 8.968858122825622559e-01 1.000000000000000000e+00 -6.549019813537597656e-01 8.143790960311889648e-01 8.941176533699035645e-01 1.000000000000000000e+00 -6.452133655548095703e-01 8.094578981399536133e-01 8.913494944572448730e-01 1.000000000000000000e+00 -6.355248093605041504e-01 8.045367002487182617e-01 8.885813355445861816e-01 1.000000000000000000e+00 -6.258361935615539551e-01 7.996155619621276855e-01 8.858131766319274902e-01 1.000000000000000000e+00 -6.161476373672485352e-01 7.946943640708923340e-01 8.830449581146240234e-01 1.000000000000000000e+00 -6.064590811729431152e-01 7.897731661796569824e-01 8.802767992019653320e-01 1.000000000000000000e+00 -5.967704653739929199e-01 7.848519682884216309e-01 8.775086402893066406e-01 1.000000000000000000e+00 -5.870819091796875000e-01 7.799307703971862793e-01 8.747404813766479492e-01 1.000000000000000000e+00 -5.773932933807373047e-01 7.750096321105957031e-01 8.719723224639892578e-01 1.000000000000000000e+00 -5.664744377136230469e-01 7.687043547630310059e-01 8.685120940208435059e-01 1.000000000000000000e+00 -5.543252825736999512e-01 7.610149979591369629e-01 8.643598556518554688e-01 1.000000000000000000e+00 -5.421760678291320801e-01 7.533256411552429199e-01 8.602076172828674316e-01 1.000000000000000000e+00 -5.300269126892089844e-01 7.456362843513488770e-01 8.560553789138793945e-01 1.000000000000000000e+00 -5.178777575492858887e-01 7.379469275474548340e-01 8.519031405448913574e-01 1.000000000000000000e+00 -5.057285428047180176e-01 7.302575707435607910e-01 8.477508425712585449e-01 1.000000000000000000e+00 -4.935793876647949219e-01 7.225682139396667480e-01 8.435986042022705078e-01 1.000000000000000000e+00 -4.814302325248718262e-01 7.148789167404174805e-01 8.394463658332824707e-01 1.000000000000000000e+00 -4.692810475826263428e-01 7.071895599365234375e-01 8.352941274642944336e-01 1.000000000000000000e+00 -4.571318626403808594e-01 6.995002031326293945e-01 8.311418890953063965e-01 1.000000000000000000e+00 -4.449827075004577637e-01 6.918108463287353516e-01 8.269895911216735840e-01 1.000000000000000000e+00 -4.328335225582122803e-01 6.841214895248413086e-01 8.228373527526855469e-01 1.000000000000000000e+00 -4.206843376159667969e-01 6.764321327209472656e-01 8.186851143836975098e-01 1.000000000000000000e+00 -4.085351824760437012e-01 6.687427759170532227e-01 8.145328760147094727e-01 1.000000000000000000e+00 -3.963859975337982178e-01 6.610534191131591797e-01 8.103806376457214355e-01 1.000000000000000000e+00 -3.842368423938751221e-01 6.533640623092651367e-01 8.062283992767333984e-01 1.000000000000000000e+00 -3.720876574516296387e-01 6.456747651100158691e-01 8.020761013031005859e-01 1.000000000000000000e+00 -3.599384725093841553e-01 6.379854083061218262e-01 7.979238629341125488e-01 1.000000000000000000e+00 -3.477893173694610596e-01 6.302960515022277832e-01 7.937716245651245117e-01 1.000000000000000000e+00 -3.356401324272155762e-01 6.226066946983337402e-01 7.896193861961364746e-01 1.000000000000000000e+00 -3.234909772872924805e-01 6.149173378944396973e-01 7.854671478271484375e-01 1.000000000000000000e+00 -3.113417923450469971e-01 6.072279810905456543e-01 7.813148498535156250e-01 1.000000000000000000e+00 -2.991926074028015137e-01 5.995386242866516113e-01 7.771626114845275879e-01 1.000000000000000000e+00 -2.870434522628784180e-01 5.918492674827575684e-01 7.730103731155395508e-01 1.000000000000000000e+00 -2.748942673206329346e-01 5.841599106788635254e-01 7.688581347465515137e-01 1.000000000000000000e+00 -2.627451121807098389e-01 5.764706134796142578e-01 7.647058963775634766e-01 1.000000000000000000e+00 -2.575163543224334717e-01 5.695501565933227539e-01 7.611687779426574707e-01 1.000000000000000000e+00 -2.522875964641571045e-01 5.626297593116760254e-01 7.576316595077514648e-01 1.000000000000000000e+00 -2.470588237047195435e-01 5.557093620300292969e-01 7.540946006774902344e-01 1.000000000000000000e+00 -2.418300658464431763e-01 5.487889051437377930e-01 7.505574822425842285e-01 1.000000000000000000e+00 -2.366013079881668091e-01 5.418685078620910645e-01 7.470203638076782227e-01 1.000000000000000000e+00 -2.313725501298904419e-01 5.349481105804443359e-01 7.434833049774169922e-01 1.000000000000000000e+00 -2.261437922716140747e-01 5.280276536941528320e-01 7.399461865425109863e-01 1.000000000000000000e+00 -2.209150344133377075e-01 5.211072564125061035e-01 7.364090681076049805e-01 1.000000000000000000e+00 -2.156862765550613403e-01 5.141868591308593750e-01 7.328719496726989746e-01 1.000000000000000000e+00 -2.104575186967849731e-01 5.072664618492126465e-01 7.293348908424377441e-01 1.000000000000000000e+00 -2.052287608385086060e-01 5.003460049629211426e-01 7.257977724075317383e-01 1.000000000000000000e+00 -2.000000029802322388e-01 4.934256076812744141e-01 7.222606539726257324e-01 1.000000000000000000e+00 -1.947712451219558716e-01 4.865051805973052979e-01 7.187235951423645020e-01 1.000000000000000000e+00 -1.895424872636795044e-01 4.795847833156585693e-01 7.151864767074584961e-01 1.000000000000000000e+00 -1.843137294054031372e-01 4.726643562316894531e-01 7.116493582725524902e-01 1.000000000000000000e+00 -1.790849715471267700e-01 4.657439589500427246e-01 7.081122398376464844e-01 1.000000000000000000e+00 -1.738562136888504028e-01 4.588235318660736084e-01 7.045751810073852539e-01 1.000000000000000000e+00 -1.686274558305740356e-01 4.519031047821044922e-01 7.010380625724792480e-01 1.000000000000000000e+00 -1.633986979722976685e-01 4.449827075004577637e-01 6.975009441375732422e-01 1.000000000000000000e+00 -1.581699401140213013e-01 4.380622804164886475e-01 6.939638853073120117e-01 1.000000000000000000e+00 -1.529411822557449341e-01 4.311418831348419189e-01 6.904267668724060059e-01 1.000000000000000000e+00 -1.477124243974685669e-01 4.242214560508728027e-01 6.868896484375000000e-01 1.000000000000000000e+00 -1.424836665391921997e-01 4.173010289669036865e-01 6.833525300025939941e-01 1.000000000000000000e+00 -1.372549086809158325e-01 4.103806316852569580e-01 6.798154711723327637e-01 1.000000000000000000e+00 -1.320261508226394653e-01 4.034602046012878418e-01 6.762783527374267578e-01 1.000000000000000000e+00 -1.272587478160858154e-01 3.958477377891540527e-01 6.687427759170532227e-01 1.000000000000000000e+00 -1.229527071118354797e-01 3.875432610511779785e-01 6.572087407112121582e-01 1.000000000000000000e+00 -1.186466738581657410e-01 3.792387545108795166e-01 6.456747651100158691e-01 1.000000000000000000e+00 -1.143406406044960022e-01 3.709342479705810547e-01 6.341407299041748047e-01 1.000000000000000000e+00 -1.100345999002456665e-01 3.626297712326049805e-01 6.226066946983337402e-01 1.000000000000000000e+00 -1.057285666465759277e-01 3.543252646923065186e-01 6.110726594924926758e-01 1.000000000000000000e+00 -1.014225333929061890e-01 3.460207581520080566e-01 5.995386242866516113e-01 1.000000000000000000e+00 -9.711649268865585327e-02 3.377162516117095947e-01 5.880045890808105469e-01 1.000000000000000000e+00 -9.281045943498611450e-02 3.294117748737335205e-01 5.764706134796142578e-01 1.000000000000000000e+00 -8.850441873073577881e-02 3.211072683334350586e-01 5.649365782737731934e-01 1.000000000000000000e+00 -8.419838547706604004e-02 3.128027617931365967e-01 5.534025430679321289e-01 1.000000000000000000e+00 -7.989235222339630127e-02 3.044982552528381348e-01 5.418685078620910645e-01 1.000000000000000000e+00 -7.558631151914596558e-02 2.961937785148620605e-01 5.303344726562500000e-01 1.000000000000000000e+00 -7.128027826547622681e-02 2.878892719745635986e-01 5.188004374504089355e-01 1.000000000000000000e+00 -6.697423756122589111e-02 2.795847654342651367e-01 5.072664618492126465e-01 1.000000000000000000e+00 -6.266820430755615234e-02 2.712802886962890625e-01 4.957323968410491943e-01 1.000000000000000000e+00 -5.836216732859611511e-02 2.629757821559906006e-01 4.841983914375305176e-01 1.000000000000000000e+00 -5.405613407492637634e-02 2.546712756156921387e-01 4.726643562316894531e-01 1.000000000000000000e+00 -4.975009709596633911e-02 2.463667839765548706e-01 4.611303210258483887e-01 1.000000000000000000e+00 -4.544406011700630188e-02 2.380622774362564087e-01 4.495963156223297119e-01 1.000000000000000000e+00 -4.113802313804626465e-02 2.297577857971191406e-01 4.380622804164886475e-01 1.000000000000000000e+00 -3.683198615908622742e-02 2.214532941579818726e-01 4.265282452106475830e-01 1.000000000000000000e+00 -3.252595290541648865e-02 2.131487876176834106e-01 4.149942398071289062e-01 1.000000000000000000e+00 -2.821991592645645142e-02 2.048442959785461426e-01 4.034602046012878418e-01 1.000000000000000000e+00 -2.391387894749641418e-02 1.965397894382476807e-01 3.919261693954467773e-01 1.000000000000000000e+00 -1.960784383118152618e-02 1.882352977991104126e-01 3.803921639919281006e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/RdGy b/fastplotlib/utils/colormaps/RdGy deleted file mode 100644 index a6f80f093..000000000 --- a/fastplotlib/utils/colormaps/RdGy +++ /dev/null @@ -1,256 +0,0 @@ -4.039215743541717529e-01 0.000000000000000000e+00 1.215686276555061340e-01 1.000000000000000000e+00 -4.154555797576904297e-01 3.690888173878192902e-03 1.234140694141387939e-01 1.000000000000000000e+00 -4.269896149635314941e-01 7.381776347756385803e-03 1.252595186233520508e-01 1.000000000000000000e+00 -4.385236501693725586e-01 1.107266452163457870e-02 1.271049529314041138e-01 1.000000000000000000e+00 -4.500576555728912354e-01 1.476355269551277161e-02 1.289504021406173706e-01 1.000000000000000000e+00 -4.615916907787322998e-01 1.845443993806838989e-02 1.307958513498306274e-01 1.000000000000000000e+00 -4.731257259845733643e-01 2.214532904326915741e-02 1.326412856578826904e-01 1.000000000000000000e+00 -4.846597313880920410e-01 2.583621628582477570e-02 1.344867348670959473e-01 1.000000000000000000e+00 -4.961937665939331055e-01 2.952710539102554321e-02 1.363321840763092041e-01 1.000000000000000000e+00 -5.077278017997741699e-01 3.321799263358116150e-02 1.381776183843612671e-01 1.000000000000000000e+00 -5.192618370056152344e-01 3.690887987613677979e-02 1.400230675935745239e-01 1.000000000000000000e+00 -5.307958722114562988e-01 4.059977084398269653e-02 1.418685168027877808e-01 1.000000000000000000e+00 -5.423298478126525879e-01 4.429065808653831482e-02 1.437139511108398438e-01 1.000000000000000000e+00 -5.538638830184936523e-01 4.798154532909393311e-02 1.455594003200531006e-01 1.000000000000000000e+00 -5.653979182243347168e-01 5.167243257164955139e-02 1.474048495292663574e-01 1.000000000000000000e+00 -5.769319534301757812e-01 5.536332353949546814e-02 1.492502838373184204e-01 1.000000000000000000e+00 -5.884659886360168457e-01 5.905421078205108643e-02 1.510957330465316772e-01 1.000000000000000000e+00 -6.000000238418579102e-01 6.274510174989700317e-02 1.529411822557449341e-01 1.000000000000000000e+00 -6.115339994430541992e-01 6.643598526716232300e-02 1.547866165637969971e-01 1.000000000000000000e+00 -6.230680346488952637e-01 7.012687623500823975e-02 1.566320657730102539e-01 1.000000000000000000e+00 -6.346020698547363281e-01 7.381775975227355957e-02 1.584775149822235107e-01 1.000000000000000000e+00 -6.461361050605773926e-01 7.750865072011947632e-02 1.603229492902755737e-01 1.000000000000000000e+00 -6.576701402664184570e-01 8.119954168796539307e-02 1.621683984994888306e-01 1.000000000000000000e+00 -6.692041754722595215e-01 8.489042520523071289e-02 1.640138477087020874e-01 1.000000000000000000e+00 -6.807381510734558105e-01 8.858131617307662964e-02 1.658592820167541504e-01 1.000000000000000000e+00 -6.922721862792968750e-01 9.227219969034194946e-02 1.677047312259674072e-01 1.000000000000000000e+00 -7.008073925971984863e-01 9.965398162603378296e-02 1.712418347597122192e-01 1.000000000000000000e+00 -7.063437104225158691e-01 1.107266470789909363e-01 1.764705926179885864e-01 1.000000000000000000e+00 -7.118800282478332520e-01 1.217993050813674927e-01 1.816993504762649536e-01 1.000000000000000000e+00 -7.174164056777954102e-01 1.328719705343246460e-01 1.869281083345413208e-01 1.000000000000000000e+00 -7.229527235031127930e-01 1.439446359872817993e-01 1.921568661928176880e-01 1.000000000000000000e+00 -7.284890413284301758e-01 1.550173014402389526e-01 1.973856240510940552e-01 1.000000000000000000e+00 -7.340253591537475586e-01 1.660899668931961060e-01 2.026143819093704224e-01 1.000000000000000000e+00 -7.395617365837097168e-01 1.771626323461532593e-01 2.078431397676467896e-01 1.000000000000000000e+00 -7.450980544090270996e-01 1.882352977991104126e-01 2.130718976259231567e-01 1.000000000000000000e+00 -7.506343722343444824e-01 1.993079632520675659e-01 2.183006554841995239e-01 1.000000000000000000e+00 -7.561706900596618652e-01 2.103806287050247192e-01 2.235294133424758911e-01 1.000000000000000000e+00 -7.617070078849792480e-01 2.214532941579818726e-01 2.287581712007522583e-01 1.000000000000000000e+00 -7.672433853149414062e-01 2.325259447097778320e-01 2.339869290590286255e-01 1.000000000000000000e+00 -7.727797031402587891e-01 2.435986101627349854e-01 2.392156869173049927e-01 1.000000000000000000e+00 -7.783160209655761719e-01 2.546712756156921387e-01 2.444444447755813599e-01 1.000000000000000000e+00 -7.838523387908935547e-01 2.657439410686492920e-01 2.496732026338577271e-01 1.000000000000000000e+00 -7.893887162208557129e-01 2.768166065216064453e-01 2.549019753932952881e-01 1.000000000000000000e+00 -7.949250340461730957e-01 2.878892719745635986e-01 2.601307332515716553e-01 1.000000000000000000e+00 -8.004613518714904785e-01 2.989619374275207520e-01 2.653594911098480225e-01 1.000000000000000000e+00 -8.059976696968078613e-01 3.100346028804779053e-01 2.705882489681243896e-01 1.000000000000000000e+00 -8.115340471267700195e-01 3.211072683334350586e-01 2.758170068264007568e-01 1.000000000000000000e+00 -8.170703649520874023e-01 3.321799337863922119e-01 2.810457646846771240e-01 1.000000000000000000e+00 -8.226066827774047852e-01 3.432525992393493652e-01 2.862745225429534912e-01 1.000000000000000000e+00 -8.281430006027221680e-01 3.543252646923065186e-01 2.915032804012298584e-01 1.000000000000000000e+00 -8.336793780326843262e-01 3.653979301452636719e-01 2.967320382595062256e-01 1.000000000000000000e+00 -8.392156958580017090e-01 3.764705955982208252e-01 3.019607961177825928e-01 1.000000000000000000e+00 -8.438292741775512695e-01 3.870818912982940674e-01 3.101114928722381592e-01 1.000000000000000000e+00 -8.484429121017456055e-01 3.976931869983673096e-01 3.182622194290161133e-01 1.000000000000000000e+00 -8.530564904212951660e-01 4.083045125007629395e-01 3.264129161834716797e-01 1.000000000000000000e+00 -8.576701283454895020e-01 4.189158082008361816e-01 3.345636427402496338e-01 1.000000000000000000e+00 -8.622837662696838379e-01 4.295271039009094238e-01 3.427143394947052002e-01 1.000000000000000000e+00 -8.668973445892333984e-01 4.401383996009826660e-01 3.508650660514831543e-01 1.000000000000000000e+00 -8.715109825134277344e-01 4.507497251033782959e-01 3.590157628059387207e-01 1.000000000000000000e+00 -8.761245608329772949e-01 4.613610208034515381e-01 3.671664595603942871e-01 1.000000000000000000e+00 -8.807381987571716309e-01 4.719723165035247803e-01 3.753171861171722412e-01 1.000000000000000000e+00 -8.853517770767211914e-01 4.825836122035980225e-01 3.834678828716278076e-01 1.000000000000000000e+00 -8.899654150009155273e-01 4.931949377059936523e-01 3.916186094284057617e-01 1.000000000000000000e+00 -8.945789933204650879e-01 5.038062334060668945e-01 3.997693061828613281e-01 1.000000000000000000e+00 -8.991926312446594238e-01 5.144175291061401367e-01 4.079200327396392822e-01 1.000000000000000000e+00 -9.038062095642089844e-01 5.250288248062133789e-01 4.160707294940948486e-01 1.000000000000000000e+00 -9.084198474884033203e-01 5.356401205062866211e-01 4.242214560508728027e-01 1.000000000000000000e+00 -9.130334258079528809e-01 5.462514162063598633e-01 4.323721528053283691e-01 1.000000000000000000e+00 -9.176470637321472168e-01 5.568627715110778809e-01 4.405228793621063232e-01 1.000000000000000000e+00 -9.222606420516967773e-01 5.674740672111511230e-01 4.486735761165618896e-01 1.000000000000000000e+00 -9.268742799758911133e-01 5.780853629112243652e-01 4.568243026733398438e-01 1.000000000000000000e+00 -9.314879179000854492e-01 5.886966586112976074e-01 4.649749994277954102e-01 1.000000000000000000e+00 -9.361014962196350098e-01 5.993079543113708496e-01 4.731257259845733643e-01 1.000000000000000000e+00 -9.407151341438293457e-01 6.099192500114440918e-01 4.812764227390289307e-01 1.000000000000000000e+00 -9.453287124633789062e-01 6.205305457115173340e-01 4.894271492958068848e-01 1.000000000000000000e+00 -9.499423503875732422e-01 6.311418414115905762e-01 4.975778460502624512e-01 1.000000000000000000e+00 -9.545559287071228027e-01 6.417531967163085938e-01 5.057285428047180176e-01 1.000000000000000000e+00 -9.575547575950622559e-01 6.512110829353332520e-01 5.151095986366271973e-01 1.000000000000000000e+00 -9.589388966560363770e-01 6.595155596733093262e-01 5.257208943367004395e-01 1.000000000000000000e+00 -9.603229761123657227e-01 6.678200960159301758e-01 5.363321900367736816e-01 1.000000000000000000e+00 -9.617070555686950684e-01 6.761245727539062500e-01 5.469434857368469238e-01 1.000000000000000000e+00 -9.630911350250244141e-01 6.844290494918823242e-01 5.575547814369201660e-01 1.000000000000000000e+00 -9.644752144813537598e-01 6.927335858345031738e-01 5.681660771369934082e-01 1.000000000000000000e+00 -9.658592939376831055e-01 7.010380625724792480e-01 5.787773728370666504e-01 1.000000000000000000e+00 -9.672433733940124512e-01 7.093425393104553223e-01 5.893886685371398926e-01 1.000000000000000000e+00 -9.686274528503417969e-01 7.176470756530761719e-01 6.000000238418579102e-01 1.000000000000000000e+00 -9.700115323066711426e-01 7.259515523910522461e-01 6.106113195419311523e-01 1.000000000000000000e+00 -9.713956117630004883e-01 7.342560291290283203e-01 6.212226152420043945e-01 1.000000000000000000e+00 -9.727796912193298340e-01 7.425605654716491699e-01 6.318339109420776367e-01 1.000000000000000000e+00 -9.741637706756591797e-01 7.508650422096252441e-01 6.424452066421508789e-01 1.000000000000000000e+00 -9.755478501319885254e-01 7.591695785522460938e-01 6.530565023422241211e-01 1.000000000000000000e+00 -9.769319295883178711e-01 7.674740552902221680e-01 6.636677980422973633e-01 1.000000000000000000e+00 -9.783160090446472168e-01 7.757785320281982422e-01 6.742790937423706055e-01 1.000000000000000000e+00 -9.797000885009765625e-01 7.840830683708190918e-01 6.848904490470886230e-01 1.000000000000000000e+00 -9.810842275619506836e-01 7.923875451087951660e-01 6.955017447471618652e-01 1.000000000000000000e+00 -9.824683070182800293e-01 8.006920218467712402e-01 7.061130404472351074e-01 1.000000000000000000e+00 -9.838523864746093750e-01 8.089965581893920898e-01 7.167243361473083496e-01 1.000000000000000000e+00 -9.852364659309387207e-01 8.173010349273681641e-01 7.273356318473815918e-01 1.000000000000000000e+00 -9.866205453872680664e-01 8.256055116653442383e-01 7.379469275474548340e-01 1.000000000000000000e+00 -9.880046248435974121e-01 8.339100480079650879e-01 7.485582232475280762e-01 1.000000000000000000e+00 -9.893887042999267578e-01 8.422145247459411621e-01 7.591695785522460938e-01 1.000000000000000000e+00 -9.907727837562561035e-01 8.505190014839172363e-01 7.697808742523193359e-01 1.000000000000000000e+00 -9.921568632125854492e-01 8.588235378265380859e-01 7.803921699523925781e-01 1.000000000000000000e+00 -9.924644231796264648e-01 8.643598556518554688e-01 7.890042066574096680e-01 1.000000000000000000e+00 -9.927719831466674805e-01 8.698961734771728516e-01 7.976163029670715332e-01 1.000000000000000000e+00 -9.930796027183532715e-01 8.754325509071350098e-01 8.062283992767333984e-01 1.000000000000000000e+00 -9.933871626853942871e-01 8.809688687324523926e-01 8.148404359817504883e-01 1.000000000000000000e+00 -9.936947226524353027e-01 8.865051865577697754e-01 8.234525322914123535e-01 1.000000000000000000e+00 -9.940022826194763184e-01 8.920415043830871582e-01 8.320645689964294434e-01 1.000000000000000000e+00 -9.943099021911621094e-01 8.975778818130493164e-01 8.406766653060913086e-01 1.000000000000000000e+00 -9.946174621582031250e-01 9.031141996383666992e-01 8.492887616157531738e-01 1.000000000000000000e+00 -9.949250221252441406e-01 9.086505174636840820e-01 8.579007983207702637e-01 1.000000000000000000e+00 -9.952325820922851562e-01 9.141868352890014648e-01 8.665128946304321289e-01 1.000000000000000000e+00 -9.955402016639709473e-01 9.197232127189636230e-01 8.751249313354492188e-01 1.000000000000000000e+00 -9.958477616310119629e-01 9.252595305442810059e-01 8.837370276451110840e-01 1.000000000000000000e+00 -9.961553215980529785e-01 9.307958483695983887e-01 8.923491239547729492e-01 1.000000000000000000e+00 -9.964628815650939941e-01 9.363321661949157715e-01 9.009611606597900391e-01 1.000000000000000000e+00 -9.967705011367797852e-01 9.418684840202331543e-01 9.095732569694519043e-01 1.000000000000000000e+00 -9.970780611038208008e-01 9.474048614501953125e-01 9.181852936744689941e-01 1.000000000000000000e+00 -9.973856210708618164e-01 9.529411792755126953e-01 9.267973899841308594e-01 1.000000000000000000e+00 -9.976931810379028320e-01 9.584774971008300781e-01 9.354094862937927246e-01 1.000000000000000000e+00 -9.980007410049438477e-01 9.640138149261474609e-01 9.440215229988098145e-01 1.000000000000000000e+00 -9.983083605766296387e-01 9.695501923561096191e-01 9.526336193084716797e-01 1.000000000000000000e+00 -9.986159205436706543e-01 9.750865101814270020e-01 9.612456560134887695e-01 1.000000000000000000e+00 -9.989234805107116699e-01 9.806228280067443848e-01 9.698577523231506348e-01 1.000000000000000000e+00 -9.992310404777526855e-01 9.861591458320617676e-01 9.784698486328125000e-01 1.000000000000000000e+00 -9.995386600494384766e-01 9.916955232620239258e-01 9.870818853378295898e-01 1.000000000000000000e+00 -9.998462200164794922e-01 9.972318410873413086e-01 9.956939816474914551e-01 1.000000000000000000e+00 -9.976162910461425781e-01 9.976162910461425781e-01 9.976162910461425781e-01 1.000000000000000000e+00 -9.928489327430725098e-01 9.928489327430725098e-01 9.928489327430725098e-01 1.000000000000000000e+00 -9.880815148353576660e-01 9.880815148353576660e-01 9.880815148353576660e-01 1.000000000000000000e+00 -9.833140969276428223e-01 9.833140969276428223e-01 9.833140969276428223e-01 1.000000000000000000e+00 -9.785467386245727539e-01 9.785467386245727539e-01 9.785467386245727539e-01 1.000000000000000000e+00 -9.737793207168579102e-01 9.737793207168579102e-01 9.737793207168579102e-01 1.000000000000000000e+00 -9.690119028091430664e-01 9.690119028091430664e-01 9.690119028091430664e-01 1.000000000000000000e+00 -9.642445445060729980e-01 9.642445445060729980e-01 9.642445445060729980e-01 1.000000000000000000e+00 -9.594771265983581543e-01 9.594771265983581543e-01 9.594771265983581543e-01 1.000000000000000000e+00 -9.547097086906433105e-01 9.547097086906433105e-01 9.547097086906433105e-01 1.000000000000000000e+00 -9.499423503875732422e-01 9.499423503875732422e-01 9.499423503875732422e-01 1.000000000000000000e+00 -9.451749324798583984e-01 9.451749324798583984e-01 9.451749324798583984e-01 1.000000000000000000e+00 -9.404075145721435547e-01 9.404075145721435547e-01 9.404075145721435547e-01 1.000000000000000000e+00 -9.356401562690734863e-01 9.356401562690734863e-01 9.356401562690734863e-01 1.000000000000000000e+00 -9.308727383613586426e-01 9.308727383613586426e-01 9.308727383613586426e-01 1.000000000000000000e+00 -9.261053204536437988e-01 9.261053204536437988e-01 9.261053204536437988e-01 1.000000000000000000e+00 -9.213379621505737305e-01 9.213379621505737305e-01 9.213379621505737305e-01 1.000000000000000000e+00 -9.165705442428588867e-01 9.165705442428588867e-01 9.165705442428588867e-01 1.000000000000000000e+00 -9.118031263351440430e-01 9.118031263351440430e-01 9.118031263351440430e-01 1.000000000000000000e+00 -9.070357680320739746e-01 9.070357680320739746e-01 9.070357680320739746e-01 1.000000000000000000e+00 -9.022683501243591309e-01 9.022683501243591309e-01 9.022683501243591309e-01 1.000000000000000000e+00 -8.975009322166442871e-01 8.975009322166442871e-01 8.975009322166442871e-01 1.000000000000000000e+00 -8.927335739135742188e-01 8.927335739135742188e-01 8.927335739135742188e-01 1.000000000000000000e+00 -8.879661560058593750e-01 8.879661560058593750e-01 8.879661560058593750e-01 1.000000000000000000e+00 -8.831987977027893066e-01 8.831987977027893066e-01 8.831987977027893066e-01 1.000000000000000000e+00 -8.784313797950744629e-01 8.784313797950744629e-01 8.784313797950744629e-01 1.000000000000000000e+00 -8.725874423980712891e-01 8.725874423980712891e-01 8.725874423980712891e-01 1.000000000000000000e+00 -8.667435646057128906e-01 8.667435646057128906e-01 8.667435646057128906e-01 1.000000000000000000e+00 -8.608996272087097168e-01 8.608996272087097168e-01 8.608996272087097168e-01 1.000000000000000000e+00 -8.550557494163513184e-01 8.550557494163513184e-01 8.550557494163513184e-01 1.000000000000000000e+00 -8.492118120193481445e-01 8.492118120193481445e-01 8.492118120193481445e-01 1.000000000000000000e+00 -8.433679342269897461e-01 8.433679342269897461e-01 8.433679342269897461e-01 1.000000000000000000e+00 -8.375240564346313477e-01 8.375240564346313477e-01 8.375240564346313477e-01 1.000000000000000000e+00 -8.316801190376281738e-01 8.316801190376281738e-01 8.316801190376281738e-01 1.000000000000000000e+00 -8.258362412452697754e-01 8.258362412452697754e-01 8.258362412452697754e-01 1.000000000000000000e+00 -8.199923038482666016e-01 8.199923038482666016e-01 8.199923038482666016e-01 1.000000000000000000e+00 -8.141484260559082031e-01 8.141484260559082031e-01 8.141484260559082031e-01 1.000000000000000000e+00 -8.083044886589050293e-01 8.083044886589050293e-01 8.083044886589050293e-01 1.000000000000000000e+00 -8.024606108665466309e-01 8.024606108665466309e-01 8.024606108665466309e-01 1.000000000000000000e+00 -7.966166734695434570e-01 7.966166734695434570e-01 7.966166734695434570e-01 1.000000000000000000e+00 -7.907727956771850586e-01 7.907727956771850586e-01 7.907727956771850586e-01 1.000000000000000000e+00 -7.849288582801818848e-01 7.849288582801818848e-01 7.849288582801818848e-01 1.000000000000000000e+00 -7.790849804878234863e-01 7.790849804878234863e-01 7.790849804878234863e-01 1.000000000000000000e+00 -7.732410430908203125e-01 7.732410430908203125e-01 7.732410430908203125e-01 1.000000000000000000e+00 -7.673971652984619141e-01 7.673971652984619141e-01 7.673971652984619141e-01 1.000000000000000000e+00 -7.615532279014587402e-01 7.615532279014587402e-01 7.615532279014587402e-01 1.000000000000000000e+00 -7.557093501091003418e-01 7.557093501091003418e-01 7.557093501091003418e-01 1.000000000000000000e+00 -7.498654127120971680e-01 7.498654127120971680e-01 7.498654127120971680e-01 1.000000000000000000e+00 -7.440215349197387695e-01 7.440215349197387695e-01 7.440215349197387695e-01 1.000000000000000000e+00 -7.381775975227355957e-01 7.381775975227355957e-01 7.381775975227355957e-01 1.000000000000000000e+00 -7.323337197303771973e-01 7.323337197303771973e-01 7.323337197303771973e-01 1.000000000000000000e+00 -7.254902124404907227e-01 7.254902124404907227e-01 7.254902124404907227e-01 1.000000000000000000e+00 -7.176470756530761719e-01 7.176470756530761719e-01 7.176470756530761719e-01 1.000000000000000000e+00 -7.098039388656616211e-01 7.098039388656616211e-01 7.098039388656616211e-01 1.000000000000000000e+00 -7.019608020782470703e-01 7.019608020782470703e-01 7.019608020782470703e-01 1.000000000000000000e+00 -6.941176652908325195e-01 6.941176652908325195e-01 6.941176652908325195e-01 1.000000000000000000e+00 -6.862745285034179688e-01 6.862745285034179688e-01 6.862745285034179688e-01 1.000000000000000000e+00 -6.784313917160034180e-01 6.784313917160034180e-01 6.784313917160034180e-01 1.000000000000000000e+00 -6.705882549285888672e-01 6.705882549285888672e-01 6.705882549285888672e-01 1.000000000000000000e+00 -6.627451181411743164e-01 6.627451181411743164e-01 6.627451181411743164e-01 1.000000000000000000e+00 -6.549019813537597656e-01 6.549019813537597656e-01 6.549019813537597656e-01 1.000000000000000000e+00 -6.470588445663452148e-01 6.470588445663452148e-01 6.470588445663452148e-01 1.000000000000000000e+00 -6.392157077789306641e-01 6.392157077789306641e-01 6.392157077789306641e-01 1.000000000000000000e+00 -6.313725709915161133e-01 6.313725709915161133e-01 6.313725709915161133e-01 1.000000000000000000e+00 -6.235294342041015625e-01 6.235294342041015625e-01 6.235294342041015625e-01 1.000000000000000000e+00 -6.156862974166870117e-01 6.156862974166870117e-01 6.156862974166870117e-01 1.000000000000000000e+00 -6.078431606292724609e-01 6.078431606292724609e-01 6.078431606292724609e-01 1.000000000000000000e+00 -6.000000238418579102e-01 6.000000238418579102e-01 6.000000238418579102e-01 1.000000000000000000e+00 -5.921568870544433594e-01 5.921568870544433594e-01 5.921568870544433594e-01 1.000000000000000000e+00 -5.843137502670288086e-01 5.843137502670288086e-01 5.843137502670288086e-01 1.000000000000000000e+00 -5.764706134796142578e-01 5.764706134796142578e-01 5.764706134796142578e-01 1.000000000000000000e+00 -5.686274766921997070e-01 5.686274766921997070e-01 5.686274766921997070e-01 1.000000000000000000e+00 -5.607843399047851562e-01 5.607843399047851562e-01 5.607843399047851562e-01 1.000000000000000000e+00 -5.529412031173706055e-01 5.529412031173706055e-01 5.529412031173706055e-01 1.000000000000000000e+00 -5.450980663299560547e-01 5.450980663299560547e-01 5.450980663299560547e-01 1.000000000000000000e+00 -5.372549295425415039e-01 5.372549295425415039e-01 5.372549295425415039e-01 1.000000000000000000e+00 -5.294117927551269531e-01 5.294117927551269531e-01 5.294117927551269531e-01 1.000000000000000000e+00 -5.204921364784240723e-01 5.204921364784240723e-01 5.204921364784240723e-01 1.000000000000000000e+00 -5.115724802017211914e-01 5.115724802017211914e-01 5.115724802017211914e-01 1.000000000000000000e+00 -5.026528239250183105e-01 5.026528239250183105e-01 5.026528239250183105e-01 1.000000000000000000e+00 -4.937331676483154297e-01 4.937331676483154297e-01 4.937331676483154297e-01 1.000000000000000000e+00 -4.848135411739349365e-01 4.848135411739349365e-01 4.848135411739349365e-01 1.000000000000000000e+00 -4.758938848972320557e-01 4.758938848972320557e-01 4.758938848972320557e-01 1.000000000000000000e+00 -4.669742286205291748e-01 4.669742286205291748e-01 4.669742286205291748e-01 1.000000000000000000e+00 -4.580546021461486816e-01 4.580546021461486816e-01 4.580546021461486816e-01 1.000000000000000000e+00 -4.491349458694458008e-01 4.491349458694458008e-01 4.491349458694458008e-01 1.000000000000000000e+00 -4.402152895927429199e-01 4.402152895927429199e-01 4.402152895927429199e-01 1.000000000000000000e+00 -4.312956631183624268e-01 4.312956631183624268e-01 4.312956631183624268e-01 1.000000000000000000e+00 -4.223760068416595459e-01 4.223760068416595459e-01 4.223760068416595459e-01 1.000000000000000000e+00 -4.134563505649566650e-01 4.134563505649566650e-01 4.134563505649566650e-01 1.000000000000000000e+00 -4.045367240905761719e-01 4.045367240905761719e-01 4.045367240905761719e-01 1.000000000000000000e+00 -3.956170678138732910e-01 3.956170678138732910e-01 3.956170678138732910e-01 1.000000000000000000e+00 -3.866974115371704102e-01 3.866974115371704102e-01 3.866974115371704102e-01 1.000000000000000000e+00 -3.777777850627899170e-01 3.777777850627899170e-01 3.777777850627899170e-01 1.000000000000000000e+00 -3.688581287860870361e-01 3.688581287860870361e-01 3.688581287860870361e-01 1.000000000000000000e+00 -3.599384725093841553e-01 3.599384725093841553e-01 3.599384725093841553e-01 1.000000000000000000e+00 -3.510188460350036621e-01 3.510188460350036621e-01 3.510188460350036621e-01 1.000000000000000000e+00 -3.420991897583007812e-01 3.420991897583007812e-01 3.420991897583007812e-01 1.000000000000000000e+00 -3.331795334815979004e-01 3.331795334815979004e-01 3.331795334815979004e-01 1.000000000000000000e+00 -3.242599070072174072e-01 3.242599070072174072e-01 3.242599070072174072e-01 1.000000000000000000e+00 -3.153402507305145264e-01 3.153402507305145264e-01 3.153402507305145264e-01 1.000000000000000000e+00 -3.064205944538116455e-01 3.064205944538116455e-01 3.064205944538116455e-01 1.000000000000000000e+00 -2.980392277240753174e-01 2.980392277240753174e-01 2.980392277240753174e-01 1.000000000000000000e+00 -2.901960909366607666e-01 2.901960909366607666e-01 2.901960909366607666e-01 1.000000000000000000e+00 -2.823529541492462158e-01 2.823529541492462158e-01 2.823529541492462158e-01 1.000000000000000000e+00 -2.745098173618316650e-01 2.745098173618316650e-01 2.745098173618316650e-01 1.000000000000000000e+00 -2.666666805744171143e-01 2.666666805744171143e-01 2.666666805744171143e-01 1.000000000000000000e+00 -2.588235437870025635e-01 2.588235437870025635e-01 2.588235437870025635e-01 1.000000000000000000e+00 -2.509804069995880127e-01 2.509804069995880127e-01 2.509804069995880127e-01 1.000000000000000000e+00 -2.431372553110122681e-01 2.431372553110122681e-01 2.431372553110122681e-01 1.000000000000000000e+00 -2.352941185235977173e-01 2.352941185235977173e-01 2.352941185235977173e-01 1.000000000000000000e+00 -2.274509817361831665e-01 2.274509817361831665e-01 2.274509817361831665e-01 1.000000000000000000e+00 -2.196078449487686157e-01 2.196078449487686157e-01 2.196078449487686157e-01 1.000000000000000000e+00 -2.117647081613540649e-01 2.117647081613540649e-01 2.117647081613540649e-01 1.000000000000000000e+00 -2.039215713739395142e-01 2.039215713739395142e-01 2.039215713739395142e-01 1.000000000000000000e+00 -1.960784345865249634e-01 1.960784345865249634e-01 1.960784345865249634e-01 1.000000000000000000e+00 -1.882352977991104126e-01 1.882352977991104126e-01 1.882352977991104126e-01 1.000000000000000000e+00 -1.803921610116958618e-01 1.803921610116958618e-01 1.803921610116958618e-01 1.000000000000000000e+00 -1.725490242242813110e-01 1.725490242242813110e-01 1.725490242242813110e-01 1.000000000000000000e+00 -1.647058874368667603e-01 1.647058874368667603e-01 1.647058874368667603e-01 1.000000000000000000e+00 -1.568627506494522095e-01 1.568627506494522095e-01 1.568627506494522095e-01 1.000000000000000000e+00 -1.490196138620376587e-01 1.490196138620376587e-01 1.490196138620376587e-01 1.000000000000000000e+00 -1.411764770746231079e-01 1.411764770746231079e-01 1.411764770746231079e-01 1.000000000000000000e+00 -1.333333402872085571e-01 1.333333402872085571e-01 1.333333402872085571e-01 1.000000000000000000e+00 -1.254902034997940063e-01 1.254902034997940063e-01 1.254902034997940063e-01 1.000000000000000000e+00 -1.176470592617988586e-01 1.176470592617988586e-01 1.176470592617988586e-01 1.000000000000000000e+00 -1.098039224743843079e-01 1.098039224743843079e-01 1.098039224743843079e-01 1.000000000000000000e+00 -1.019607856869697571e-01 1.019607856869697571e-01 1.019607856869697571e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/RdPu b/fastplotlib/utils/colormaps/RdPu deleted file mode 100644 index 9c6375b05..000000000 --- a/fastplotlib/utils/colormaps/RdPu +++ /dev/null @@ -1,256 +0,0 @@ -1.000000000000000000e+00 9.686274528503417969e-01 9.529411792755126953e-01 1.000000000000000000e+00 -9.997539520263671875e-01 9.657977819442749023e-01 9.502345323562622070e-01 1.000000000000000000e+00 -9.995079040527343750e-01 9.629681110382080078e-01 9.475278854370117188e-01 1.000000000000000000e+00 -9.992617964744567871e-01 9.601383805274963379e-01 9.448212385177612305e-01 1.000000000000000000e+00 -9.990157485008239746e-01 9.573087096214294434e-01 9.421145915985107422e-01 1.000000000000000000e+00 -9.987697005271911621e-01 9.544790387153625488e-01 9.394079446792602539e-01 1.000000000000000000e+00 -9.985236525535583496e-01 9.516493678092956543e-01 9.367012977600097656e-01 1.000000000000000000e+00 -9.982776045799255371e-01 9.488196969032287598e-01 9.339945912361145020e-01 1.000000000000000000e+00 -9.980314970016479492e-01 9.459900259971618652e-01 9.312879443168640137e-01 1.000000000000000000e+00 -9.977854490280151367e-01 9.431602954864501953e-01 9.285812973976135254e-01 1.000000000000000000e+00 -9.975394010543823242e-01 9.403306245803833008e-01 9.258746504783630371e-01 1.000000000000000000e+00 -9.972933530807495117e-01 9.375009536743164062e-01 9.231680035591125488e-01 1.000000000000000000e+00 -9.970473051071166992e-01 9.346712827682495117e-01 9.204613566398620605e-01 1.000000000000000000e+00 -9.968012571334838867e-01 9.318416118621826172e-01 9.177547097206115723e-01 1.000000000000000000e+00 -9.965551495552062988e-01 9.290119409561157227e-01 9.150480628013610840e-01 1.000000000000000000e+00 -9.963091015815734863e-01 9.261822104454040527e-01 9.123414158821105957e-01 1.000000000000000000e+00 -9.960630536079406738e-01 9.233525395393371582e-01 9.096347689628601074e-01 1.000000000000000000e+00 -9.958170056343078613e-01 9.205228686332702637e-01 9.069281220436096191e-01 1.000000000000000000e+00 -9.955709576606750488e-01 9.176931977272033691e-01 9.042214751243591309e-01 1.000000000000000000e+00 -9.953248500823974609e-01 9.148635268211364746e-01 9.015148282051086426e-01 1.000000000000000000e+00 -9.950788021087646484e-01 9.120338559150695801e-01 8.988081216812133789e-01 1.000000000000000000e+00 -9.948327541351318359e-01 9.092041254043579102e-01 8.961014747619628906e-01 1.000000000000000000e+00 -9.945867061614990234e-01 9.063744544982910156e-01 8.933948278427124023e-01 1.000000000000000000e+00 -9.943406581878662109e-01 9.035447835922241211e-01 8.906881809234619141e-01 1.000000000000000000e+00 -9.940945506095886230e-01 9.007151126861572266e-01 8.879815340042114258e-01 1.000000000000000000e+00 -9.938485026359558105e-01 8.978854417800903320e-01 8.852748870849609375e-01 1.000000000000000000e+00 -9.936024546623229980e-01 8.950557708740234375e-01 8.825682401657104492e-01 1.000000000000000000e+00 -9.933564066886901855e-01 8.922260403633117676e-01 8.798615932464599609e-01 1.000000000000000000e+00 -9.931103587150573730e-01 8.893963694572448730e-01 8.771549463272094727e-01 1.000000000000000000e+00 -9.928643107414245605e-01 8.865666985511779785e-01 8.744482994079589844e-01 1.000000000000000000e+00 -9.926182031631469727e-01 8.837370276451110840e-01 8.717416524887084961e-01 1.000000000000000000e+00 -9.923721551895141602e-01 8.809073567390441895e-01 8.690350055694580078e-01 1.000000000000000000e+00 -9.921414852142333984e-01 8.780161738395690918e-01 8.662207126617431641e-01 1.000000000000000000e+00 -9.920184612274169922e-01 8.746943473815917969e-01 8.626528382301330566e-01 1.000000000000000000e+00 -9.918954372406005859e-01 8.713725209236145020e-01 8.590849637985229492e-01 1.000000000000000000e+00 -9.917724132537841797e-01 8.680507540702819824e-01 8.555170893669128418e-01 1.000000000000000000e+00 -9.916493892669677734e-01 8.647289276123046875e-01 8.519492745399475098e-01 1.000000000000000000e+00 -9.915263652801513672e-01 8.614071607589721680e-01 8.483814001083374023e-01 1.000000000000000000e+00 -9.914032816886901855e-01 8.580853343009948730e-01 8.448135256767272949e-01 1.000000000000000000e+00 -9.912802577018737793e-01 8.547635674476623535e-01 8.412456512451171875e-01 1.000000000000000000e+00 -9.911572337150573730e-01 8.514417409896850586e-01 8.376778364181518555e-01 1.000000000000000000e+00 -9.910342097282409668e-01 8.481199741363525391e-01 8.341099619865417480e-01 1.000000000000000000e+00 -9.909111857414245605e-01 8.447981476783752441e-01 8.305420875549316406e-01 1.000000000000000000e+00 -9.907881617546081543e-01 8.414763808250427246e-01 8.269742131233215332e-01 1.000000000000000000e+00 -9.906651377677917480e-01 8.381545543670654297e-01 8.234063982963562012e-01 1.000000000000000000e+00 -9.905421137809753418e-01 8.348327279090881348e-01 8.198385238647460938e-01 1.000000000000000000e+00 -9.904190897941589355e-01 8.315109610557556152e-01 8.162706494331359863e-01 1.000000000000000000e+00 -9.902960658073425293e-01 8.281891345977783203e-01 8.127028346061706543e-01 1.000000000000000000e+00 -9.901729822158813477e-01 8.248673677444458008e-01 8.091349601745605469e-01 1.000000000000000000e+00 -9.900499582290649414e-01 8.215455412864685059e-01 8.055670857429504395e-01 1.000000000000000000e+00 -9.899269342422485352e-01 8.182237744331359863e-01 8.019992113113403320e-01 1.000000000000000000e+00 -9.898039102554321289e-01 8.149019479751586914e-01 7.984313964843750000e-01 1.000000000000000000e+00 -9.896808862686157227e-01 8.115801811218261719e-01 7.948635220527648926e-01 1.000000000000000000e+00 -9.895578622817993164e-01 8.082583546638488770e-01 7.912956476211547852e-01 1.000000000000000000e+00 -9.894348382949829102e-01 8.049365878105163574e-01 7.877277731895446777e-01 1.000000000000000000e+00 -9.893118143081665039e-01 8.016147613525390625e-01 7.841599583625793457e-01 1.000000000000000000e+00 -9.891887903213500977e-01 7.982929348945617676e-01 7.805920839309692383e-01 1.000000000000000000e+00 -9.890657663345336914e-01 7.949711680412292480e-01 7.770242094993591309e-01 1.000000000000000000e+00 -9.889427423477172852e-01 7.916493415832519531e-01 7.734563350677490234e-01 1.000000000000000000e+00 -9.888196587562561035e-01 7.883275747299194336e-01 7.698885202407836914e-01 1.000000000000000000e+00 -9.886966347694396973e-01 7.850057482719421387e-01 7.663206458091735840e-01 1.000000000000000000e+00 -9.885736107826232910e-01 7.816839814186096191e-01 7.627527713775634766e-01 1.000000000000000000e+00 -9.884505867958068848e-01 7.783621549606323242e-01 7.591849565505981445e-01 1.000000000000000000e+00 -9.883275628089904785e-01 7.750403881072998047e-01 7.556170821189880371e-01 1.000000000000000000e+00 -9.881737828254699707e-01 7.713802456855773926e-01 7.526028156280517578e-01 1.000000000000000000e+00 -9.879277348518371582e-01 7.667050957679748535e-01 7.512494921684265137e-01 1.000000000000000000e+00 -9.876816868782043457e-01 7.620300054550170898e-01 7.498961687088012695e-01 1.000000000000000000e+00 -9.874355792999267578e-01 7.573548555374145508e-01 7.485428452491760254e-01 1.000000000000000000e+00 -9.871895313262939453e-01 7.526797652244567871e-01 7.471895217895507812e-01 1.000000000000000000e+00 -9.869434833526611328e-01 7.480046153068542480e-01 7.458361983299255371e-01 1.000000000000000000e+00 -9.866974353790283203e-01 7.433294653892517090e-01 7.444828748703002930e-01 1.000000000000000000e+00 -9.864513874053955078e-01 7.386543750762939453e-01 7.431295514106750488e-01 1.000000000000000000e+00 -9.862052798271179199e-01 7.339792251586914062e-01 7.417762279510498047e-01 1.000000000000000000e+00 -9.859592318534851074e-01 7.293041348457336426e-01 7.404229044914245605e-01 1.000000000000000000e+00 -9.857131838798522949e-01 7.246289849281311035e-01 7.390695810317993164e-01 1.000000000000000000e+00 -9.854671359062194824e-01 7.199538350105285645e-01 7.377162575721740723e-01 1.000000000000000000e+00 -9.852210879325866699e-01 7.152787446975708008e-01 7.363629341125488281e-01 1.000000000000000000e+00 -9.849749803543090820e-01 7.106035947799682617e-01 7.350096106529235840e-01 1.000000000000000000e+00 -9.847289323806762695e-01 7.059285044670104980e-01 7.336562871932983398e-01 1.000000000000000000e+00 -9.844828844070434570e-01 7.012533545494079590e-01 7.323029637336730957e-01 1.000000000000000000e+00 -9.842368364334106445e-01 6.965782642364501953e-01 7.309496402740478516e-01 1.000000000000000000e+00 -9.839907884597778320e-01 6.919031143188476562e-01 7.295963168144226074e-01 1.000000000000000000e+00 -9.837447404861450195e-01 6.872279644012451172e-01 7.282429933547973633e-01 1.000000000000000000e+00 -9.834986329078674316e-01 6.825528740882873535e-01 7.268896698951721191e-01 1.000000000000000000e+00 -9.832525849342346191e-01 6.778777241706848145e-01 7.255363464355468750e-01 1.000000000000000000e+00 -9.830065369606018066e-01 6.732026338577270508e-01 7.241830229759216309e-01 1.000000000000000000e+00 -9.827604889869689941e-01 6.685274839401245117e-01 7.228296995162963867e-01 1.000000000000000000e+00 -9.825144410133361816e-01 6.638523936271667480e-01 7.214763760566711426e-01 1.000000000000000000e+00 -9.822683334350585938e-01 6.591772437095642090e-01 7.201230525970458984e-01 1.000000000000000000e+00 -9.820222854614257812e-01 6.545020937919616699e-01 7.187697291374206543e-01 1.000000000000000000e+00 -9.817762374877929688e-01 6.498270034790039062e-01 7.174164056777954102e-01 1.000000000000000000e+00 -9.815301895141601562e-01 6.451518535614013672e-01 7.160630822181701660e-01 1.000000000000000000e+00 -9.812841415405273438e-01 6.404767632484436035e-01 7.147096991539001465e-01 1.000000000000000000e+00 -9.810380339622497559e-01 6.358016133308410645e-01 7.133563756942749023e-01 1.000000000000000000e+00 -9.807919859886169434e-01 6.311264634132385254e-01 7.120030522346496582e-01 1.000000000000000000e+00 -9.805459380149841309e-01 6.264513731002807617e-01 7.106497287750244141e-01 1.000000000000000000e+00 -9.802537560462951660e-01 6.209919452667236328e-01 7.088811993598937988e-01 1.000000000000000000e+00 -9.798846840858459473e-01 6.142252683639526367e-01 7.064206004142761230e-01 1.000000000000000000e+00 -9.795155525207519531e-01 6.074586510658264160e-01 7.039600014686584473e-01 1.000000000000000000e+00 -9.791464805603027344e-01 6.006920337677001953e-01 7.014994025230407715e-01 1.000000000000000000e+00 -9.787774085998535156e-01 5.939254164695739746e-01 6.990388035774230957e-01 1.000000000000000000e+00 -9.784082770347595215e-01 5.871587991714477539e-01 6.965782642364501953e-01 1.000000000000000000e+00 -9.780392050743103027e-01 5.803921818733215332e-01 6.941176652908325195e-01 1.000000000000000000e+00 -9.776701331138610840e-01 5.736255049705505371e-01 6.916570663452148438e-01 1.000000000000000000e+00 -9.773010611534118652e-01 5.668588876724243164e-01 6.891964673995971680e-01 1.000000000000000000e+00 -9.769319295883178711e-01 5.600922703742980957e-01 6.867358684539794922e-01 1.000000000000000000e+00 -9.765628576278686523e-01 5.533256530761718750e-01 6.842752695083618164e-01 1.000000000000000000e+00 -9.761937856674194336e-01 5.465590357780456543e-01 6.818146705627441406e-01 1.000000000000000000e+00 -9.758246541023254395e-01 5.397923588752746582e-01 6.793540716171264648e-01 1.000000000000000000e+00 -9.754555821418762207e-01 5.330257415771484375e-01 6.768935322761535645e-01 1.000000000000000000e+00 -9.750865101814270020e-01 5.262591242790222168e-01 6.744329333305358887e-01 1.000000000000000000e+00 -9.747174382209777832e-01 5.194925069808959961e-01 6.719723343849182129e-01 1.000000000000000000e+00 -9.743483066558837891e-01 5.127258896827697754e-01 6.695117354393005371e-01 1.000000000000000000e+00 -9.739792346954345703e-01 5.059592723846435547e-01 6.670511364936828613e-01 1.000000000000000000e+00 -9.736101627349853516e-01 4.991926252841949463e-01 6.645905375480651855e-01 1.000000000000000000e+00 -9.732410907745361328e-01 4.924259781837463379e-01 6.621299386024475098e-01 1.000000000000000000e+00 -9.728719592094421387e-01 4.856593608856201172e-01 6.596693396568298340e-01 1.000000000000000000e+00 -9.725028872489929199e-01 4.788927435874938965e-01 6.572087407112121582e-01 1.000000000000000000e+00 -9.721338152885437012e-01 4.721260964870452881e-01 6.547482013702392578e-01 1.000000000000000000e+00 -9.717646837234497070e-01 4.653594791889190674e-01 6.522876024246215820e-01 1.000000000000000000e+00 -9.713956117630004883e-01 4.585928618907928467e-01 6.498270034790039062e-01 1.000000000000000000e+00 -9.710265398025512695e-01 4.518262147903442383e-01 6.473664045333862305e-01 1.000000000000000000e+00 -9.706574678421020508e-01 4.450595974922180176e-01 6.449058055877685547e-01 1.000000000000000000e+00 -9.702883362770080566e-01 4.382929503917694092e-01 6.424452066421508789e-01 1.000000000000000000e+00 -9.699192643165588379e-01 4.315263330936431885e-01 6.399846076965332031e-01 1.000000000000000000e+00 -9.695501923561096191e-01 4.247597157955169678e-01 6.375240087509155273e-01 1.000000000000000000e+00 -9.691810607910156250e-01 4.179930686950683594e-01 6.350634098052978516e-01 1.000000000000000000e+00 -9.688119888305664062e-01 4.112264513969421387e-01 6.326028704643249512e-01 1.000000000000000000e+00 -9.670280814170837402e-01 4.046443700790405273e-01 6.307573914527893066e-01 1.000000000000000000e+00 -9.638292789459228516e-01 3.982468247413635254e-01 6.295270919799804688e-01 1.000000000000000000e+00 -9.606305360794067383e-01 3.918492794036865234e-01 6.282967925071716309e-01 1.000000000000000000e+00 -9.574317336082458496e-01 3.854517638683319092e-01 6.270664930343627930e-01 1.000000000000000000e+00 -9.542329907417297363e-01 3.790542185306549072e-01 6.258361935615539551e-01 1.000000000000000000e+00 -9.510341882705688477e-01 3.726566731929779053e-01 6.246058940887451172e-01 1.000000000000000000e+00 -9.478354454040527344e-01 3.662591278553009033e-01 6.233756542205810547e-01 1.000000000000000000e+00 -9.446367025375366211e-01 3.598615825176239014e-01 6.221453547477722168e-01 1.000000000000000000e+00 -9.414379000663757324e-01 3.534640669822692871e-01 6.209150552749633789e-01 1.000000000000000000e+00 -9.382391571998596191e-01 3.470665216445922852e-01 6.196847558021545410e-01 1.000000000000000000e+00 -9.350403547286987305e-01 3.406689763069152832e-01 6.184544563293457031e-01 1.000000000000000000e+00 -9.318416118621826172e-01 3.342714309692382812e-01 6.172241568565368652e-01 1.000000000000000000e+00 -9.286428093910217285e-01 3.278738856315612793e-01 6.159938573837280273e-01 1.000000000000000000e+00 -9.254440665245056152e-01 3.214763700962066650e-01 6.147635579109191895e-01 1.000000000000000000e+00 -9.222452640533447266e-01 3.150788247585296631e-01 6.135332584381103516e-01 1.000000000000000000e+00 -9.190465211868286133e-01 3.086812794208526611e-01 6.123029589653015137e-01 1.000000000000000000e+00 -9.158477783203125000e-01 3.022837340831756592e-01 6.110726594924926758e-01 1.000000000000000000e+00 -9.126489758491516113e-01 2.958861887454986572e-01 6.098423600196838379e-01 1.000000000000000000e+00 -9.094502329826354980e-01 2.894886434078216553e-01 6.086120605468750000e-01 1.000000000000000000e+00 -9.062514305114746094e-01 2.830911278724670410e-01 6.073817610740661621e-01 1.000000000000000000e+00 -9.030526876449584961e-01 2.766935825347900391e-01 6.061514616012573242e-01 1.000000000000000000e+00 -8.998538851737976074e-01 2.702960371971130371e-01 6.049211621284484863e-01 1.000000000000000000e+00 -8.966551423072814941e-01 2.638984918594360352e-01 6.036908626556396484e-01 1.000000000000000000e+00 -8.934563398361206055e-01 2.575009465217590332e-01 6.024605631828308105e-01 1.000000000000000000e+00 -8.902575969696044922e-01 2.511034309864044189e-01 6.012303233146667480e-01 1.000000000000000000e+00 -8.870587944984436035e-01 2.447058856487274170e-01 6.000000238418579102e-01 1.000000000000000000e+00 -8.838600516319274902e-01 2.383083403110504150e-01 5.987697243690490723e-01 1.000000000000000000e+00 -8.806613087654113770e-01 2.319108098745346069e-01 5.975394248962402344e-01 1.000000000000000000e+00 -8.774625062942504883e-01 2.255132645368576050e-01 5.963091254234313965e-01 1.000000000000000000e+00 -8.742637634277343750e-01 2.191157191991806030e-01 5.950788259506225586e-01 1.000000000000000000e+00 -8.710649609565734863e-01 2.127181887626647949e-01 5.938485264778137207e-01 1.000000000000000000e+00 -8.678662180900573730e-01 2.063206434249877930e-01 5.926182270050048828e-01 1.000000000000000000e+00 -8.630526661872863770e-01 2.000000029802322388e-01 5.902345180511474609e-01 1.000000000000000000e+00 -8.572703003883361816e-01 1.937254965305328369e-01 5.871587991714477539e-01 1.000000000000000000e+00 -8.514878749847412109e-01 1.874509751796722412e-01 5.840830206871032715e-01 1.000000000000000000e+00 -8.457055091857910156e-01 1.811764687299728394e-01 5.810073018074035645e-01 1.000000000000000000e+00 -8.399230837821960449e-01 1.749019622802734375e-01 5.779315829277038574e-01 1.000000000000000000e+00 -8.341407179832458496e-01 1.686274558305740356e-01 5.748558044433593750e-01 1.000000000000000000e+00 -8.283583521842956543e-01 1.623529344797134399e-01 5.717800855636596680e-01 1.000000000000000000e+00 -8.225759267807006836e-01 1.560784280300140381e-01 5.687043666839599609e-01 1.000000000000000000e+00 -8.167935609817504883e-01 1.498039215803146362e-01 5.656285881996154785e-01 1.000000000000000000e+00 -8.110111355781555176e-01 1.435294151306152344e-01 5.625528693199157715e-01 1.000000000000000000e+00 -8.052287697792053223e-01 1.372549086809158325e-01 5.594771504402160645e-01 1.000000000000000000e+00 -7.994463443756103516e-01 1.309803873300552368e-01 5.564013719558715820e-01 1.000000000000000000e+00 -7.936639785766601562e-01 1.247058808803558350e-01 5.533256530761718750e-01 1.000000000000000000e+00 -7.878816127777099609e-01 1.184313744306564331e-01 5.502498745918273926e-01 1.000000000000000000e+00 -7.820991873741149902e-01 1.121568605303764343e-01 5.471741557121276855e-01 1.000000000000000000e+00 -7.763168215751647949e-01 1.058823540806770325e-01 5.440984368324279785e-01 1.000000000000000000e+00 -7.705343961715698242e-01 9.960784018039703369e-02 5.410226583480834961e-01 1.000000000000000000e+00 -7.647520303726196289e-01 9.333333373069763184e-02 5.379469394683837891e-01 1.000000000000000000e+00 -7.589696049690246582e-01 8.705881983041763306e-02 5.348712205886840820e-01 1.000000000000000000e+00 -7.531872391700744629e-01 8.078431338071823120e-02 5.317954421043395996e-01 1.000000000000000000e+00 -7.474048733711242676e-01 7.450980693101882935e-02 5.287197232246398926e-01 1.000000000000000000e+00 -7.416224479675292969e-01 6.823529303073883057e-02 5.256440043449401855e-01 1.000000000000000000e+00 -7.358400821685791016e-01 6.196078285574913025e-02 5.225682258605957031e-01 1.000000000000000000e+00 -7.300576567649841309e-01 5.568627268075942993e-02 5.194925069808959961e-01 1.000000000000000000e+00 -7.242752909660339355e-01 4.941176623106002808e-02 5.164167881011962891e-01 1.000000000000000000e+00 -7.184928655624389648e-01 4.313725605607032776e-02 5.133410096168518066e-01 1.000000000000000000e+00 -7.127104997634887695e-01 3.686274588108062744e-02 5.102652907371520996e-01 1.000000000000000000e+00 -7.069281339645385742e-01 3.058823570609092712e-02 5.071895718574523926e-01 1.000000000000000000e+00 -7.011457085609436035e-01 2.431372553110122681e-02 5.041137933731079102e-01 1.000000000000000000e+00 -6.953633427619934082e-01 1.803921535611152649e-02 5.010380744934082031e-01 1.000000000000000000e+00 -6.895809173583984375e-01 1.176470611244440079e-02 4.979623258113861084e-01 1.000000000000000000e+00 -6.837985515594482422e-01 5.490195937454700470e-03 4.948865771293640137e-01 1.000000000000000000e+00 -6.775547862052917480e-01 3.921568859368562698e-03 4.934717416763305664e-01 1.000000000000000000e+00 -6.711572408676147461e-01 3.921568859368562698e-03 4.926105439662933350e-01 1.000000000000000000e+00 -6.647596955299377441e-01 3.921568859368562698e-03 4.917493164539337158e-01 1.000000000000000000e+00 -6.583621501922607422e-01 3.921568859368562698e-03 4.908881187438964844e-01 1.000000000000000000e+00 -6.519646048545837402e-01 3.921568859368562698e-03 4.900269210338592529e-01 1.000000000000000000e+00 -6.455671191215515137e-01 3.921568859368562698e-03 4.891656935214996338e-01 1.000000000000000000e+00 -6.391695737838745117e-01 3.921568859368562698e-03 4.883044958114624023e-01 1.000000000000000000e+00 -6.327720284461975098e-01 3.921568859368562698e-03 4.874432981014251709e-01 1.000000000000000000e+00 -6.263744831085205078e-01 3.921568859368562698e-03 4.865820705890655518e-01 1.000000000000000000e+00 -6.199769377708435059e-01 3.921568859368562698e-03 4.857208728790283203e-01 1.000000000000000000e+00 -6.135793924331665039e-01 3.921568859368562698e-03 4.848596751689910889e-01 1.000000000000000000e+00 -6.071818470954895020e-01 3.921568859368562698e-03 4.839984476566314697e-01 1.000000000000000000e+00 -6.007843017578125000e-01 3.921568859368562698e-03 4.831372499465942383e-01 1.000000000000000000e+00 -5.943867564201354980e-01 3.921568859368562698e-03 4.822760522365570068e-01 1.000000000000000000e+00 -5.879892110824584961e-01 3.921568859368562698e-03 4.814148545265197754e-01 1.000000000000000000e+00 -5.815916657447814941e-01 3.921568859368562698e-03 4.805536270141601562e-01 1.000000000000000000e+00 -5.751941800117492676e-01 3.921568859368562698e-03 4.796924293041229248e-01 1.000000000000000000e+00 -5.687966346740722656e-01 3.921568859368562698e-03 4.788312315940856934e-01 1.000000000000000000e+00 -5.623990893363952637e-01 3.921568859368562698e-03 4.779700040817260742e-01 1.000000000000000000e+00 -5.560015439987182617e-01 3.921568859368562698e-03 4.771088063716888428e-01 1.000000000000000000e+00 -5.496039986610412598e-01 3.921568859368562698e-03 4.762476086616516113e-01 1.000000000000000000e+00 -5.432064533233642578e-01 3.921568859368562698e-03 4.753863811492919922e-01 1.000000000000000000e+00 -5.368089079856872559e-01 3.921568859368562698e-03 4.745251834392547607e-01 1.000000000000000000e+00 -5.304113626480102539e-01 3.921568859368562698e-03 4.736639857292175293e-01 1.000000000000000000e+00 -5.240138173103332520e-01 3.921568859368562698e-03 4.728027582168579102e-01 1.000000000000000000e+00 -5.176162719726562500e-01 3.921568859368562698e-03 4.719415605068206787e-01 1.000000000000000000e+00 -5.112187862396240234e-01 3.921568859368562698e-03 4.710803627967834473e-01 1.000000000000000000e+00 -5.048212409019470215e-01 3.921568859368562698e-03 4.702191352844238281e-01 1.000000000000000000e+00 -4.984236955642700195e-01 3.921568859368562698e-03 4.693579375743865967e-01 1.000000000000000000e+00 -4.920261502265930176e-01 3.921568859368562698e-03 4.684967398643493652e-01 1.000000000000000000e+00 -4.856286048889160156e-01 3.921568859368562698e-03 4.676355123519897461e-01 1.000000000000000000e+00 -4.792310595512390137e-01 3.921568859368562698e-03 4.667743146419525146e-01 1.000000000000000000e+00 -4.731564819812774658e-01 3.813917748630046844e-03 4.652672111988067627e-01 1.000000000000000000e+00 -4.671280384063720703e-01 3.690888173878192902e-03 4.636678099632263184e-01 1.000000000000000000e+00 -4.610995650291442871e-01 3.567858599126338959e-03 4.620684385299682617e-01 1.000000000000000000e+00 -4.550711214542388916e-01 3.444829024374485016e-03 4.604690372943878174e-01 1.000000000000000000e+00 -4.490426778793334961e-01 3.321799216791987419e-03 4.588696658611297607e-01 1.000000000000000000e+00 -4.430142343044281006e-01 3.198769642040133476e-03 4.572702944278717041e-01 1.000000000000000000e+00 -4.369857609272003174e-01 3.075740067288279533e-03 4.556708931922912598e-01 1.000000000000000000e+00 -4.309573173522949219e-01 2.952710492536425591e-03 4.540715217590332031e-01 1.000000000000000000e+00 -4.249288737773895264e-01 2.829680917784571648e-03 4.524721205234527588e-01 1.000000000000000000e+00 -4.189004302024841309e-01 2.706651343032717705e-03 4.508727490901947021e-01 1.000000000000000000e+00 -4.128719866275787354e-01 2.583621768280863762e-03 4.492733478546142578e-01 1.000000000000000000e+00 -4.068435132503509521e-01 2.460592193529009819e-03 4.476739764213562012e-01 1.000000000000000000e+00 -4.008150696754455566e-01 2.337562385946512222e-03 4.460745751857757568e-01 1.000000000000000000e+00 -3.947866261005401611e-01 2.214532811194658279e-03 4.444752037525177002e-01 1.000000000000000000e+00 -3.887581825256347656e-01 2.091503236442804337e-03 4.428758025169372559e-01 1.000000000000000000e+00 -3.827297091484069824e-01 1.968473661690950394e-03 4.412764310836791992e-01 1.000000000000000000e+00 -3.767012655735015869e-01 1.845444086939096451e-03 4.396770596504211426e-01 1.000000000000000000e+00 -3.706728219985961914e-01 1.722414512187242508e-03 4.380776584148406982e-01 1.000000000000000000e+00 -3.646443784236907959e-01 1.599384821020066738e-03 4.364782869815826416e-01 1.000000000000000000e+00 -3.586159050464630127e-01 1.476355246268212795e-03 4.348788857460021973e-01 1.000000000000000000e+00 -3.525874614715576172e-01 1.353325671516358852e-03 4.332795143127441406e-01 1.000000000000000000e+00 -3.465590178966522217e-01 1.230296096764504910e-03 4.316801130771636963e-01 1.000000000000000000e+00 -3.405305743217468262e-01 1.107266405597329140e-03 4.300807416439056396e-01 1.000000000000000000e+00 -3.345021009445190430e-01 9.842368308454751968e-04 4.284813404083251953e-01 1.000000000000000000e+00 -3.284736573696136475e-01 8.612072560936212540e-04 4.268819689750671387e-01 1.000000000000000000e+00 -3.224452137947082520e-01 7.381776231341063976e-04 4.252825975418090820e-01 1.000000000000000000e+00 -3.164167702198028564e-01 6.151480483822524548e-04 4.236831963062286377e-01 1.000000000000000000e+00 -3.103883266448974609e-01 4.921184154227375984e-04 4.220838248729705811e-01 1.000000000000000000e+00 -3.043598532676696777e-01 3.690888115670531988e-04 4.204844236373901367e-01 1.000000000000000000e+00 -2.983314096927642822e-01 2.460592077113687992e-04 4.188850522041320801e-01 1.000000000000000000e+00 -2.923029661178588867e-01 1.230296038556843996e-04 4.172856509685516357e-01 1.000000000000000000e+00 -2.862745225429534912e-01 0.000000000000000000e+00 4.156862795352935791e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/RdYlBu b/fastplotlib/utils/colormaps/RdYlBu deleted file mode 100644 index 9323fe0b1..000000000 --- a/fastplotlib/utils/colormaps/RdYlBu +++ /dev/null @@ -1,256 +0,0 @@ -6.470588445663452148e-01 0.000000000000000000e+00 1.490196138620376587e-01 1.000000000000000000e+00 -6.547482013702392578e-01 7.381776347756385803e-03 1.491733938455581665e-01 1.000000000000000000e+00 -6.624374985694885254e-01 1.476355269551277161e-02 1.493271887302398682e-01 1.000000000000000000e+00 -6.701268553733825684e-01 2.214532904326915741e-02 1.494809687137603760e-01 1.000000000000000000e+00 -6.778162121772766113e-01 2.952710539102554321e-02 1.496347486972808838e-01 1.000000000000000000e+00 -6.855055689811706543e-01 3.690887987613677979e-02 1.497885435819625854e-01 1.000000000000000000e+00 -6.931949257850646973e-01 4.429065808653831482e-02 1.499423235654830933e-01 1.000000000000000000e+00 -7.008842825889587402e-01 5.167243257164955139e-02 1.500961184501647949e-01 1.000000000000000000e+00 -7.085736393928527832e-01 5.905421078205108643e-02 1.502498984336853027e-01 1.000000000000000000e+00 -7.162629961967468262e-01 6.643598526716232300e-02 1.504036933183670044e-01 1.000000000000000000e+00 -7.239523530006408691e-01 7.381775975227355957e-02 1.505574733018875122e-01 1.000000000000000000e+00 -7.316416501998901367e-01 8.119954168796539307e-02 1.507112681865692139e-01 1.000000000000000000e+00 -7.393310070037841797e-01 8.858131617307662964e-02 1.508650481700897217e-01 1.000000000000000000e+00 -7.470203638076782227e-01 9.596309065818786621e-02 1.510188430547714233e-01 1.000000000000000000e+00 -7.547097206115722656e-01 1.033448651432991028e-01 1.511726230382919312e-01 1.000000000000000000e+00 -7.623990774154663086e-01 1.107266470789909363e-01 1.513264179229736328e-01 1.000000000000000000e+00 -7.700884342193603516e-01 1.181084215641021729e-01 1.514801979064941406e-01 1.000000000000000000e+00 -7.777777910232543945e-01 1.254902034997940063e-01 1.516339927911758423e-01 1.000000000000000000e+00 -7.854671478271484375e-01 1.328719705343246460e-01 1.517877727746963501e-01 1.000000000000000000e+00 -7.931565046310424805e-01 1.402537524700164795e-01 1.519415676593780518e-01 1.000000000000000000e+00 -8.008458018302917480e-01 1.476355195045471191e-01 1.520953476428985596e-01 1.000000000000000000e+00 -8.085351586341857910e-01 1.550173014402389526e-01 1.522491276264190674e-01 1.000000000000000000e+00 -8.162245154380798340e-01 1.623990833759307861e-01 1.524029225111007690e-01 1.000000000000000000e+00 -8.239138722419738770e-01 1.697808504104614258e-01 1.525567024946212769e-01 1.000000000000000000e+00 -8.316032290458679199e-01 1.771626323461532593e-01 1.527104973793029785e-01 1.000000000000000000e+00 -8.392925858497619629e-01 1.845443993806838989e-01 1.528642773628234863e-01 1.000000000000000000e+00 -8.453671932220458984e-01 1.929257959127426147e-01 1.550941914319992065e-01 1.000000000000000000e+00 -8.498269915580749512e-01 2.023068070411682129e-01 1.594002246856689453e-01 1.000000000000000000e+00 -8.542867898941040039e-01 2.116878181695938110e-01 1.637062728404998779e-01 1.000000000000000000e+00 -8.587466478347778320e-01 2.210688143968582153e-01 1.680123060941696167e-01 1.000000000000000000e+00 -8.632064461708068848e-01 2.304498255252838135e-01 1.723183393478393555e-01 1.000000000000000000e+00 -8.676663041114807129e-01 2.398308366537094116e-01 1.766243726015090942e-01 1.000000000000000000e+00 -8.721261024475097656e-01 2.492118477821350098e-01 1.809304058551788330e-01 1.000000000000000000e+00 -8.765859007835388184e-01 2.585928440093994141e-01 1.852364540100097656e-01 1.000000000000000000e+00 -8.810457587242126465e-01 2.679738700389862061e-01 1.895424872636795044e-01 1.000000000000000000e+00 -8.855055570602416992e-01 2.773548662662506104e-01 1.938485205173492432e-01 1.000000000000000000e+00 -8.899654150009155273e-01 2.867358624935150146e-01 1.981545537710189819e-01 1.000000000000000000e+00 -8.944252133369445801e-01 2.961168885231018066e-01 2.024605870246887207e-01 1.000000000000000000e+00 -8.988850712776184082e-01 3.054978847503662109e-01 2.067666351795196533e-01 1.000000000000000000e+00 -9.033448696136474609e-01 3.148788809776306152e-01 2.110726684331893921e-01 1.000000000000000000e+00 -9.078046679496765137e-01 3.242599070072174072e-01 2.153787016868591309e-01 1.000000000000000000e+00 -9.122645258903503418e-01 3.336409032344818115e-01 2.196847349405288696e-01 1.000000000000000000e+00 -9.167243242263793945e-01 3.430219292640686035e-01 2.239907681941986084e-01 1.000000000000000000e+00 -9.211841821670532227e-01 3.524029254913330078e-01 2.282968163490295410e-01 1.000000000000000000e+00 -9.256439805030822754e-01 3.617839217185974121e-01 2.326028496026992798e-01 1.000000000000000000e+00 -9.301037788391113281e-01 3.711649477481842041e-01 2.369088828563690186e-01 1.000000000000000000e+00 -9.345636367797851562e-01 3.805459439754486084e-01 2.412149161100387573e-01 1.000000000000000000e+00 -9.390234351158142090e-01 3.899269402027130127e-01 2.455209493637084961e-01 1.000000000000000000e+00 -9.434832930564880371e-01 3.993079662322998047e-01 2.498269826173782349e-01 1.000000000000000000e+00 -9.479430913925170898e-01 4.086889624595642090e-01 2.541330158710479736e-01 1.000000000000000000e+00 -9.524029493331909180e-01 4.180699586868286133e-01 2.584390640258789062e-01 1.000000000000000000e+00 -9.568627476692199707e-01 4.274509847164154053e-01 2.627451121807098389e-01 1.000000000000000000e+00 -9.582468271255493164e-01 4.374471306800842285e-01 2.673587203025817871e-01 1.000000000000000000e+00 -9.596309065818786621e-01 4.474432766437530518e-01 2.719723284244537354e-01 1.000000000000000000e+00 -9.610149860382080078e-01 4.574394524097442627e-01 2.765859365463256836e-01 1.000000000000000000e+00 -9.623990654945373535e-01 4.674355983734130859e-01 2.811995446681976318e-01 1.000000000000000000e+00 -9.637831449508666992e-01 4.774317443370819092e-01 2.858131527900695801e-01 1.000000000000000000e+00 -9.651672244071960449e-01 4.874279201030731201e-01 2.904267609119415283e-01 1.000000000000000000e+00 -9.665513038635253906e-01 4.974240660667419434e-01 2.950403690338134766e-01 1.000000000000000000e+00 -9.679353833198547363e-01 5.074202418327331543e-01 2.996539771556854248e-01 1.000000000000000000e+00 -9.693194627761840820e-01 5.174163579940795898e-01 3.042675852775573730e-01 1.000000000000000000e+00 -9.707036018371582031e-01 5.274125337600708008e-01 3.088811933994293213e-01 1.000000000000000000e+00 -9.720876812934875488e-01 5.374087095260620117e-01 3.134948015213012695e-01 1.000000000000000000e+00 -9.734717607498168945e-01 5.474048256874084473e-01 3.181084096431732178e-01 1.000000000000000000e+00 -9.748558402061462402e-01 5.574010014533996582e-01 3.227220177650451660e-01 1.000000000000000000e+00 -9.762399196624755859e-01 5.673971772193908691e-01 3.273356258869171143e-01 1.000000000000000000e+00 -9.776239991188049316e-01 5.773932933807373047e-01 3.319492638111114502e-01 1.000000000000000000e+00 -9.790080785751342773e-01 5.873894691467285156e-01 3.365628719329833984e-01 1.000000000000000000e+00 -9.803921580314636230e-01 5.973856449127197266e-01 3.411764800548553467e-01 1.000000000000000000e+00 -9.817762374877929688e-01 6.073817610740661621e-01 3.457900881767272949e-01 1.000000000000000000e+00 -9.831603169441223145e-01 6.173779368400573730e-01 3.504036962985992432e-01 1.000000000000000000e+00 -9.845443964004516602e-01 6.273741126060485840e-01 3.550173044204711914e-01 1.000000000000000000e+00 -9.859284758567810059e-01 6.373702287673950195e-01 3.596309125423431396e-01 1.000000000000000000e+00 -9.873125553131103516e-01 6.473664045333862305e-01 3.642445206642150879e-01 1.000000000000000000e+00 -9.886966347694396973e-01 6.573625802993774414e-01 3.688581287860870361e-01 1.000000000000000000e+00 -9.900807142257690430e-01 6.673586964607238770e-01 3.734717369079589844e-01 1.000000000000000000e+00 -9.914647936820983887e-01 6.773548722267150879e-01 3.780853450298309326e-01 1.000000000000000000e+00 -9.922337532043457031e-01 6.861976385116577148e-01 3.840061426162719727e-01 1.000000000000000000e+00 -9.923875331878662109e-01 6.938869953155517578e-01 3.912341296672821045e-01 1.000000000000000000e+00 -9.925413131713867188e-01 7.015762925148010254e-01 3.984621167182922363e-01 1.000000000000000000e+00 -9.926950931549072266e-01 7.092656493186950684e-01 4.056901335716247559e-01 1.000000000000000000e+00 -9.928489327430725098e-01 7.169550061225891113e-01 4.129181206226348877e-01 1.000000000000000000e+00 -9.930027127265930176e-01 7.246443629264831543e-01 4.201461076736450195e-01 1.000000000000000000e+00 -9.931564927101135254e-01 7.323337197303771973e-01 4.273740947246551514e-01 1.000000000000000000e+00 -9.933102726936340332e-01 7.400230765342712402e-01 4.346020817756652832e-01 1.000000000000000000e+00 -9.934640526771545410e-01 7.477124333381652832e-01 4.418300688266754150e-01 1.000000000000000000e+00 -9.936178326606750488e-01 7.554017901420593262e-01 4.490580558776855469e-01 1.000000000000000000e+00 -9.937716126441955566e-01 7.630911469459533691e-01 4.562860429286956787e-01 1.000000000000000000e+00 -9.939253926277160645e-01 7.707804441452026367e-01 4.635140299797058105e-01 1.000000000000000000e+00 -9.940791726112365723e-01 7.784698009490966797e-01 4.707420170307159424e-01 1.000000000000000000e+00 -9.942330121994018555e-01 7.861591577529907227e-01 4.779700040817260742e-01 1.000000000000000000e+00 -9.943867921829223633e-01 7.938485145568847656e-01 4.851979911327362061e-01 1.000000000000000000e+00 -9.945405721664428711e-01 8.015378713607788086e-01 4.924259781837463379e-01 1.000000000000000000e+00 -9.946943521499633789e-01 8.092272281646728516e-01 4.996539652347564697e-01 1.000000000000000000e+00 -9.948481321334838867e-01 8.169165849685668945e-01 5.068819522857666016e-01 1.000000000000000000e+00 -9.950019121170043945e-01 8.246059417724609375e-01 5.141099691390991211e-01 1.000000000000000000e+00 -9.951556921005249023e-01 8.322952985763549805e-01 5.213379263877868652e-01 1.000000000000000000e+00 -9.953094720840454102e-01 8.399845957756042480e-01 5.285659432411193848e-01 1.000000000000000000e+00 -9.954633116722106934e-01 8.476739525794982910e-01 5.357939004898071289e-01 1.000000000000000000e+00 -9.956170916557312012e-01 8.553633093833923340e-01 5.430219173431396484e-01 1.000000000000000000e+00 -9.957708716392517090e-01 8.630526661872863770e-01 5.502498745918273926e-01 1.000000000000000000e+00 -9.959246516227722168e-01 8.707420229911804199e-01 5.574778914451599121e-01 1.000000000000000000e+00 -9.960784316062927246e-01 8.784313797950744629e-01 5.647059082984924316e-01 1.000000000000000000e+00 -9.962322115898132324e-01 8.831987977027893066e-01 5.719338655471801758e-01 1.000000000000000000e+00 -9.963859915733337402e-01 8.879661560058593750e-01 5.791618824005126953e-01 1.000000000000000000e+00 -9.965397715568542480e-01 8.927335739135742188e-01 5.863898396492004395e-01 1.000000000000000000e+00 -9.966935515403747559e-01 8.975009322166442871e-01 5.936178565025329590e-01 1.000000000000000000e+00 -9.968473911285400391e-01 9.022683501243591309e-01 6.008458137512207031e-01 1.000000000000000000e+00 -9.970011711120605469e-01 9.070357680320739746e-01 6.080738306045532227e-01 1.000000000000000000e+00 -9.971549510955810547e-01 9.118031263351440430e-01 6.153017878532409668e-01 1.000000000000000000e+00 -9.973087310791015625e-01 9.165705442428588867e-01 6.225298047065734863e-01 1.000000000000000000e+00 -9.974625110626220703e-01 9.213379621505737305e-01 6.297577619552612305e-01 1.000000000000000000e+00 -9.976162910461425781e-01 9.261053204536437988e-01 6.369857788085937500e-01 1.000000000000000000e+00 -9.977700710296630859e-01 9.308727383613586426e-01 6.442137360572814941e-01 1.000000000000000000e+00 -9.979238510131835938e-01 9.356401562690734863e-01 6.514417529106140137e-01 1.000000000000000000e+00 -9.980776906013488770e-01 9.404075145721435547e-01 6.586697697639465332e-01 1.000000000000000000e+00 -9.982314705848693848e-01 9.451749324798583984e-01 6.658977270126342773e-01 1.000000000000000000e+00 -9.983852505683898926e-01 9.499423503875732422e-01 6.731257438659667969e-01 1.000000000000000000e+00 -9.985390305519104004e-01 9.547097086906433105e-01 6.803537011146545410e-01 1.000000000000000000e+00 -9.986928105354309082e-01 9.594771265983581543e-01 6.875817179679870605e-01 1.000000000000000000e+00 -9.988465905189514160e-01 9.642445445060729980e-01 6.948096752166748047e-01 1.000000000000000000e+00 -9.990003705024719238e-01 9.690119028091430664e-01 7.020376920700073242e-01 1.000000000000000000e+00 -9.991541504859924316e-01 9.737793207168579102e-01 7.092656493186950684e-01 1.000000000000000000e+00 -9.993079304695129395e-01 9.785467386245727539e-01 7.164936661720275879e-01 1.000000000000000000e+00 -9.994617700576782227e-01 9.833140969276428223e-01 7.237216234207153320e-01 1.000000000000000000e+00 -9.996155500411987305e-01 9.880815148353576660e-01 7.309496402740478516e-01 1.000000000000000000e+00 -9.997693300247192383e-01 9.928489327430725098e-01 7.381775975227355957e-01 1.000000000000000000e+00 -9.999231100082397461e-01 9.976162910461425781e-01 7.454056143760681152e-01 1.000000000000000000e+00 -9.976162910461425781e-01 9.990772604942321777e-01 7.534025311470031738e-01 1.000000000000000000e+00 -9.928489327430725098e-01 9.972318410873413086e-01 7.621684074401855469e-01 1.000000000000000000e+00 -9.880815148353576660e-01 9.953863620758056641e-01 7.709342837333679199e-01 1.000000000000000000e+00 -9.833140969276428223e-01 9.935409426689147949e-01 7.797001004219055176e-01 1.000000000000000000e+00 -9.785467386245727539e-01 9.916955232620239258e-01 7.884659767150878906e-01 1.000000000000000000e+00 -9.737793207168579102e-01 9.898500442504882812e-01 7.972318530082702637e-01 1.000000000000000000e+00 -9.690119028091430664e-01 9.880046248435974121e-01 8.059976696968078613e-01 1.000000000000000000e+00 -9.642445445060729980e-01 9.861591458320617676e-01 8.147635459899902344e-01 1.000000000000000000e+00 -9.594771265983581543e-01 9.843137264251708984e-01 8.235294222831726074e-01 1.000000000000000000e+00 -9.547097086906433105e-01 9.824683070182800293e-01 8.322952985763549805e-01 1.000000000000000000e+00 -9.499423503875732422e-01 9.806228280067443848e-01 8.410611152648925781e-01 1.000000000000000000e+00 -9.451749324798583984e-01 9.787774085998535156e-01 8.498269915580749512e-01 1.000000000000000000e+00 -9.404075145721435547e-01 9.769319295883178711e-01 8.585928678512573242e-01 1.000000000000000000e+00 -9.356401562690734863e-01 9.750865101814270020e-01 8.673586845397949219e-01 1.000000000000000000e+00 -9.308727383613586426e-01 9.732410907745361328e-01 8.761245608329772949e-01 1.000000000000000000e+00 -9.261053204536437988e-01 9.713956117630004883e-01 8.848904371261596680e-01 1.000000000000000000e+00 -9.213379621505737305e-01 9.695501923561096191e-01 8.936563134193420410e-01 1.000000000000000000e+00 -9.165705442428588867e-01 9.677047133445739746e-01 9.024221301078796387e-01 1.000000000000000000e+00 -9.118031263351440430e-01 9.658592939376831055e-01 9.111880064010620117e-01 1.000000000000000000e+00 -9.070357680320739746e-01 9.640138149261474609e-01 9.199538826942443848e-01 1.000000000000000000e+00 -9.022683501243591309e-01 9.621683955192565918e-01 9.287196993827819824e-01 1.000000000000000000e+00 -8.975009322166442871e-01 9.603229761123657227e-01 9.374855756759643555e-01 1.000000000000000000e+00 -8.927335739135742188e-01 9.584774971008300781e-01 9.462514519691467285e-01 1.000000000000000000e+00 -8.879661560058593750e-01 9.566320776939392090e-01 9.550173282623291016e-01 1.000000000000000000e+00 -8.831987977027893066e-01 9.547865986824035645e-01 9.637831449508666992e-01 1.000000000000000000e+00 -8.784313797950744629e-01 9.529411792755126953e-01 9.725490212440490723e-01 1.000000000000000000e+00 -8.702806830406188965e-01 9.489427208900451660e-01 9.702422022819519043e-01 1.000000000000000000e+00 -8.621299266815185547e-01 9.449442625045776367e-01 9.679353833198547363e-01 1.000000000000000000e+00 -8.539792299270629883e-01 9.409458041191101074e-01 9.656286239624023438e-01 1.000000000000000000e+00 -8.458285331726074219e-01 9.369473457336425781e-01 9.633218050003051758e-01 1.000000000000000000e+00 -8.376778364181518555e-01 9.329488873481750488e-01 9.610149860382080078e-01 1.000000000000000000e+00 -8.295270800590515137e-01 9.289504289627075195e-01 9.587081670761108398e-01 1.000000000000000000e+00 -8.213763833045959473e-01 9.249519705772399902e-01 9.564014077186584473e-01 1.000000000000000000e+00 -8.132256865501403809e-01 9.209534525871276855e-01 9.540945887565612793e-01 1.000000000000000000e+00 -8.050749897956848145e-01 9.169549942016601562e-01 9.517877697944641113e-01 1.000000000000000000e+00 -7.969242334365844727e-01 9.129565358161926270e-01 9.494809508323669434e-01 1.000000000000000000e+00 -7.887735366821289062e-01 9.089580774307250977e-01 9.471741914749145508e-01 1.000000000000000000e+00 -7.806228399276733398e-01 9.049596190452575684e-01 9.448673725128173828e-01 1.000000000000000000e+00 -7.724721431732177734e-01 9.009611606597900391e-01 9.425605535507202148e-01 1.000000000000000000e+00 -7.643213868141174316e-01 8.969627022743225098e-01 9.402537345886230469e-01 1.000000000000000000e+00 -7.561706900596618652e-01 8.929642438888549805e-01 9.379469156265258789e-01 1.000000000000000000e+00 -7.480199933052062988e-01 8.889657855033874512e-01 9.356401562690734863e-01 1.000000000000000000e+00 -7.398692965507507324e-01 8.849673271179199219e-01 9.333333373069763184e-01 1.000000000000000000e+00 -7.317185401916503906e-01 8.809688687324523926e-01 9.310265183448791504e-01 1.000000000000000000e+00 -7.235678434371948242e-01 8.769704103469848633e-01 9.287196993827819824e-01 1.000000000000000000e+00 -7.154171466827392578e-01 8.729719519615173340e-01 9.264129400253295898e-01 1.000000000000000000e+00 -7.072664499282836914e-01 8.689734935760498047e-01 9.241061210632324219e-01 1.000000000000000000e+00 -6.991157531738281250e-01 8.649750351905822754e-01 9.217993021011352539e-01 1.000000000000000000e+00 -6.909649968147277832e-01 8.609765768051147461e-01 9.194924831390380859e-01 1.000000000000000000e+00 -6.828143000602722168e-01 8.569780588150024414e-01 9.171857237815856934e-01 1.000000000000000000e+00 -6.746636033058166504e-01 8.529796004295349121e-01 9.148789048194885254e-01 1.000000000000000000e+00 -6.663590669631958008e-01 8.475970625877380371e-01 9.118800759315490723e-01 1.000000000000000000e+00 -6.579008102416992188e-01 8.408304452896118164e-01 9.081891775131225586e-01 1.000000000000000000e+00 -6.494424939155578613e-01 8.340638279914855957e-01 9.044982790946960449e-01 1.000000000000000000e+00 -6.409842371940612793e-01 8.272972106933593750e-01 9.008073806762695312e-01 1.000000000000000000e+00 -6.325259804725646973e-01 8.205305933952331543e-01 8.971164822578430176e-01 1.000000000000000000e+00 -6.240676641464233398e-01 8.137639164924621582e-01 8.934255838394165039e-01 1.000000000000000000e+00 -6.156094074249267578e-01 8.069972991943359375e-01 8.897347450256347656e-01 1.000000000000000000e+00 -6.071510910987854004e-01 8.002306818962097168e-01 8.860438466072082520e-01 1.000000000000000000e+00 -5.986928343772888184e-01 7.934640645980834961e-01 8.823529481887817383e-01 1.000000000000000000e+00 -5.902345180511474609e-01 7.866974472999572754e-01 8.786620497703552246e-01 1.000000000000000000e+00 -5.817762613296508789e-01 7.799307703971862793e-01 8.749711513519287109e-01 1.000000000000000000e+00 -5.733179450035095215e-01 7.731641530990600586e-01 8.712802529335021973e-01 1.000000000000000000e+00 -5.648596882820129395e-01 7.663975358009338379e-01 8.675894141197204590e-01 1.000000000000000000e+00 -5.564013719558715820e-01 7.596309185028076172e-01 8.638985157012939453e-01 1.000000000000000000e+00 -5.479431152343750000e-01 7.528643012046813965e-01 8.602076172828674316e-01 1.000000000000000000e+00 -5.394847989082336426e-01 7.460976839065551758e-01 8.565167188644409180e-01 1.000000000000000000e+00 -5.310265421867370605e-01 7.393310070037841797e-01 8.528258204460144043e-01 1.000000000000000000e+00 -5.225682258605957031e-01 7.325643897056579590e-01 8.491349220275878906e-01 1.000000000000000000e+00 -5.141099691390991211e-01 7.257977724075317383e-01 8.454440832138061523e-01 1.000000000000000000e+00 -5.056516528129577637e-01 7.190311551094055176e-01 8.417531847953796387e-01 1.000000000000000000e+00 -4.971933960914611816e-01 7.122645378112792969e-01 8.380622863769531250e-01 1.000000000000000000e+00 -4.887351095676422119e-01 7.054978609085083008e-01 8.343713879585266113e-01 1.000000000000000000e+00 -4.802768230438232422e-01 6.987312436103820801e-01 8.306804895401000977e-01 1.000000000000000000e+00 -4.718185365200042725e-01 6.919646263122558594e-01 8.269895911216735840e-01 1.000000000000000000e+00 -4.633602499961853027e-01 6.851980090141296387e-01 8.232987523078918457e-01 1.000000000000000000e+00 -4.549019634723663330e-01 6.784313917160034180e-01 8.196078538894653320e-01 1.000000000000000000e+00 -4.476739764213562012e-01 6.698192954063415527e-01 8.151479959487915039e-01 1.000000000000000000e+00 -4.404459893703460693e-01 6.612071990966796875e-01 8.106881976127624512e-01 1.000000000000000000e+00 -4.332180023193359375e-01 6.525951623916625977e-01 8.062283992767333984e-01 1.000000000000000000e+00 -4.259900152683258057e-01 6.439830660820007324e-01 8.017685413360595703e-01 1.000000000000000000e+00 -4.187620282173156738e-01 6.353710293769836426e-01 7.973087430000305176e-01 1.000000000000000000e+00 -4.115340113639831543e-01 6.267589330673217773e-01 7.928488850593566895e-01 1.000000000000000000e+00 -4.043060243129730225e-01 6.181468963623046875e-01 7.883890867233276367e-01 1.000000000000000000e+00 -3.970780372619628906e-01 6.095348000526428223e-01 7.839292287826538086e-01 1.000000000000000000e+00 -3.898500502109527588e-01 6.009227037429809570e-01 7.794694304466247559e-01 1.000000000000000000e+00 -3.826220631599426270e-01 5.923106670379638672e-01 7.750096321105957031e-01 1.000000000000000000e+00 -3.753940761089324951e-01 5.836985707283020020e-01 7.705497741699218750e-01 1.000000000000000000e+00 -3.681660890579223633e-01 5.750865340232849121e-01 7.660899758338928223e-01 1.000000000000000000e+00 -3.609381020069122314e-01 5.664744377136230469e-01 7.616301178932189941e-01 1.000000000000000000e+00 -3.537101149559020996e-01 5.578623414039611816e-01 7.571703195571899414e-01 1.000000000000000000e+00 -3.464821279048919678e-01 5.492503046989440918e-01 7.527105212211608887e-01 1.000000000000000000e+00 -3.392541408538818359e-01 5.406382083892822266e-01 7.482506632804870605e-01 1.000000000000000000e+00 -3.320261538028717041e-01 5.320261716842651367e-01 7.437908649444580078e-01 1.000000000000000000e+00 -3.247981667518615723e-01 5.234140753746032715e-01 7.393310070037841797e-01 1.000000000000000000e+00 -3.175701797008514404e-01 5.148019790649414062e-01 7.348712086677551270e-01 1.000000000000000000e+00 -3.103421628475189209e-01 5.061899423599243164e-01 7.304113507270812988e-01 1.000000000000000000e+00 -3.031141757965087891e-01 4.975778460502624512e-01 7.259515523910522461e-01 1.000000000000000000e+00 -2.958861887454986572e-01 4.889657795429229736e-01 7.214917540550231934e-01 1.000000000000000000e+00 -2.886582016944885254e-01 4.803537130355834961e-01 7.170318961143493652e-01 1.000000000000000000e+00 -2.814302146434783936e-01 4.717416465282440186e-01 7.125720977783203125e-01 1.000000000000000000e+00 -2.742022275924682617e-01 4.631295800209045410e-01 7.081122398376464844e-01 1.000000000000000000e+00 -2.690503597259521484e-01 4.539792239665985107e-01 7.034986615180969238e-01 1.000000000000000000e+00 -2.659746110439300537e-01 4.442906677722930908e-01 6.987312436103820801e-01 1.000000000000000000e+00 -2.628988921642303467e-01 4.346020817756652832e-01 6.939638853073120117e-01 1.000000000000000000e+00 -2.598231434822082520e-01 4.249134957790374756e-01 6.891964673995971680e-01 1.000000000000000000e+00 -2.567473948001861572e-01 4.152249097824096680e-01 6.844290494918823242e-01 1.000000000000000000e+00 -2.536716759204864502e-01 4.055363237857818604e-01 6.796616911888122559e-01 1.000000000000000000e+00 -2.505959272384643555e-01 3.958477377891540527e-01 6.748942732810974121e-01 1.000000000000000000e+00 -2.475201785564422607e-01 3.861591815948486328e-01 6.701268553733825684e-01 1.000000000000000000e+00 -2.444444447755813599e-01 3.764705955982208252e-01 6.653594970703125000e-01 1.000000000000000000e+00 -2.413687109947204590e-01 3.667820096015930176e-01 6.605920791625976562e-01 1.000000000000000000e+00 -2.382929623126983643e-01 3.570934236049652100e-01 6.558246612548828125e-01 1.000000000000000000e+00 -2.352172285318374634e-01 3.474048376083374023e-01 6.510573029518127441e-01 1.000000000000000000e+00 -2.321414798498153687e-01 3.377162516117095947e-01 6.462898850440979004e-01 1.000000000000000000e+00 -2.290657460689544678e-01 3.280276954174041748e-01 6.415224671363830566e-01 1.000000000000000000e+00 -2.259899973869323730e-01 3.183391094207763672e-01 6.367551088333129883e-01 1.000000000000000000e+00 -2.229142636060714722e-01 3.086505234241485596e-01 6.319876909255981445e-01 1.000000000000000000e+00 -2.198385298252105713e-01 2.989619374275207520e-01 6.272202730178833008e-01 1.000000000000000000e+00 -2.167627811431884766e-01 2.892733514308929443e-01 6.224529147148132324e-01 1.000000000000000000e+00 -2.136870473623275757e-01 2.795847654342651367e-01 6.176854968070983887e-01 1.000000000000000000e+00 -2.106112986803054810e-01 2.698961794376373291e-01 6.129180788993835449e-01 1.000000000000000000e+00 -2.075355648994445801e-01 2.602076232433319092e-01 6.081507205963134766e-01 1.000000000000000000e+00 -2.044598162174224854e-01 2.505190372467041016e-01 6.033833026885986328e-01 1.000000000000000000e+00 -2.013840824365615845e-01 2.408304512500762939e-01 5.986159443855285645e-01 1.000000000000000000e+00 -1.983083486557006836e-01 2.311418652534484863e-01 5.938485264778137207e-01 1.000000000000000000e+00 -1.952325999736785889e-01 2.214532941579818726e-01 5.890811085700988770e-01 1.000000000000000000e+00 -1.921568661928176880e-01 2.117647081613540649e-01 5.843137502670288086e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/RdYlGn b/fastplotlib/utils/colormaps/RdYlGn deleted file mode 100644 index bfe5c3ef1..000000000 --- a/fastplotlib/utils/colormaps/RdYlGn +++ /dev/null @@ -1,256 +0,0 @@ -6.470588445663452148e-01 0.000000000000000000e+00 1.490196138620376587e-01 1.000000000000000000e+00 -6.547482013702392578e-01 7.381776347756385803e-03 1.491733938455581665e-01 1.000000000000000000e+00 -6.624374985694885254e-01 1.476355269551277161e-02 1.493271887302398682e-01 1.000000000000000000e+00 -6.701268553733825684e-01 2.214532904326915741e-02 1.494809687137603760e-01 1.000000000000000000e+00 -6.778162121772766113e-01 2.952710539102554321e-02 1.496347486972808838e-01 1.000000000000000000e+00 -6.855055689811706543e-01 3.690887987613677979e-02 1.497885435819625854e-01 1.000000000000000000e+00 -6.931949257850646973e-01 4.429065808653831482e-02 1.499423235654830933e-01 1.000000000000000000e+00 -7.008842825889587402e-01 5.167243257164955139e-02 1.500961184501647949e-01 1.000000000000000000e+00 -7.085736393928527832e-01 5.905421078205108643e-02 1.502498984336853027e-01 1.000000000000000000e+00 -7.162629961967468262e-01 6.643598526716232300e-02 1.504036933183670044e-01 1.000000000000000000e+00 -7.239523530006408691e-01 7.381775975227355957e-02 1.505574733018875122e-01 1.000000000000000000e+00 -7.316416501998901367e-01 8.119954168796539307e-02 1.507112681865692139e-01 1.000000000000000000e+00 -7.393310070037841797e-01 8.858131617307662964e-02 1.508650481700897217e-01 1.000000000000000000e+00 -7.470203638076782227e-01 9.596309065818786621e-02 1.510188430547714233e-01 1.000000000000000000e+00 -7.547097206115722656e-01 1.033448651432991028e-01 1.511726230382919312e-01 1.000000000000000000e+00 -7.623990774154663086e-01 1.107266470789909363e-01 1.513264179229736328e-01 1.000000000000000000e+00 -7.700884342193603516e-01 1.181084215641021729e-01 1.514801979064941406e-01 1.000000000000000000e+00 -7.777777910232543945e-01 1.254902034997940063e-01 1.516339927911758423e-01 1.000000000000000000e+00 -7.854671478271484375e-01 1.328719705343246460e-01 1.517877727746963501e-01 1.000000000000000000e+00 -7.931565046310424805e-01 1.402537524700164795e-01 1.519415676593780518e-01 1.000000000000000000e+00 -8.008458018302917480e-01 1.476355195045471191e-01 1.520953476428985596e-01 1.000000000000000000e+00 -8.085351586341857910e-01 1.550173014402389526e-01 1.522491276264190674e-01 1.000000000000000000e+00 -8.162245154380798340e-01 1.623990833759307861e-01 1.524029225111007690e-01 1.000000000000000000e+00 -8.239138722419738770e-01 1.697808504104614258e-01 1.525567024946212769e-01 1.000000000000000000e+00 -8.316032290458679199e-01 1.771626323461532593e-01 1.527104973793029785e-01 1.000000000000000000e+00 -8.392925858497619629e-01 1.845443993806838989e-01 1.528642773628234863e-01 1.000000000000000000e+00 -8.453671932220458984e-01 1.929257959127426147e-01 1.550941914319992065e-01 1.000000000000000000e+00 -8.498269915580749512e-01 2.023068070411682129e-01 1.594002246856689453e-01 1.000000000000000000e+00 -8.542867898941040039e-01 2.116878181695938110e-01 1.637062728404998779e-01 1.000000000000000000e+00 -8.587466478347778320e-01 2.210688143968582153e-01 1.680123060941696167e-01 1.000000000000000000e+00 -8.632064461708068848e-01 2.304498255252838135e-01 1.723183393478393555e-01 1.000000000000000000e+00 -8.676663041114807129e-01 2.398308366537094116e-01 1.766243726015090942e-01 1.000000000000000000e+00 -8.721261024475097656e-01 2.492118477821350098e-01 1.809304058551788330e-01 1.000000000000000000e+00 -8.765859007835388184e-01 2.585928440093994141e-01 1.852364540100097656e-01 1.000000000000000000e+00 -8.810457587242126465e-01 2.679738700389862061e-01 1.895424872636795044e-01 1.000000000000000000e+00 -8.855055570602416992e-01 2.773548662662506104e-01 1.938485205173492432e-01 1.000000000000000000e+00 -8.899654150009155273e-01 2.867358624935150146e-01 1.981545537710189819e-01 1.000000000000000000e+00 -8.944252133369445801e-01 2.961168885231018066e-01 2.024605870246887207e-01 1.000000000000000000e+00 -8.988850712776184082e-01 3.054978847503662109e-01 2.067666351795196533e-01 1.000000000000000000e+00 -9.033448696136474609e-01 3.148788809776306152e-01 2.110726684331893921e-01 1.000000000000000000e+00 -9.078046679496765137e-01 3.242599070072174072e-01 2.153787016868591309e-01 1.000000000000000000e+00 -9.122645258903503418e-01 3.336409032344818115e-01 2.196847349405288696e-01 1.000000000000000000e+00 -9.167243242263793945e-01 3.430219292640686035e-01 2.239907681941986084e-01 1.000000000000000000e+00 -9.211841821670532227e-01 3.524029254913330078e-01 2.282968163490295410e-01 1.000000000000000000e+00 -9.256439805030822754e-01 3.617839217185974121e-01 2.326028496026992798e-01 1.000000000000000000e+00 -9.301037788391113281e-01 3.711649477481842041e-01 2.369088828563690186e-01 1.000000000000000000e+00 -9.345636367797851562e-01 3.805459439754486084e-01 2.412149161100387573e-01 1.000000000000000000e+00 -9.390234351158142090e-01 3.899269402027130127e-01 2.455209493637084961e-01 1.000000000000000000e+00 -9.434832930564880371e-01 3.993079662322998047e-01 2.498269826173782349e-01 1.000000000000000000e+00 -9.479430913925170898e-01 4.086889624595642090e-01 2.541330158710479736e-01 1.000000000000000000e+00 -9.524029493331909180e-01 4.180699586868286133e-01 2.584390640258789062e-01 1.000000000000000000e+00 -9.568627476692199707e-01 4.274509847164154053e-01 2.627451121807098389e-01 1.000000000000000000e+00 -9.582468271255493164e-01 4.374471306800842285e-01 2.673587203025817871e-01 1.000000000000000000e+00 -9.596309065818786621e-01 4.474432766437530518e-01 2.719723284244537354e-01 1.000000000000000000e+00 -9.610149860382080078e-01 4.574394524097442627e-01 2.765859365463256836e-01 1.000000000000000000e+00 -9.623990654945373535e-01 4.674355983734130859e-01 2.811995446681976318e-01 1.000000000000000000e+00 -9.637831449508666992e-01 4.774317443370819092e-01 2.858131527900695801e-01 1.000000000000000000e+00 -9.651672244071960449e-01 4.874279201030731201e-01 2.904267609119415283e-01 1.000000000000000000e+00 -9.665513038635253906e-01 4.974240660667419434e-01 2.950403690338134766e-01 1.000000000000000000e+00 -9.679353833198547363e-01 5.074202418327331543e-01 2.996539771556854248e-01 1.000000000000000000e+00 -9.693194627761840820e-01 5.174163579940795898e-01 3.042675852775573730e-01 1.000000000000000000e+00 -9.707036018371582031e-01 5.274125337600708008e-01 3.088811933994293213e-01 1.000000000000000000e+00 -9.720876812934875488e-01 5.374087095260620117e-01 3.134948015213012695e-01 1.000000000000000000e+00 -9.734717607498168945e-01 5.474048256874084473e-01 3.181084096431732178e-01 1.000000000000000000e+00 -9.748558402061462402e-01 5.574010014533996582e-01 3.227220177650451660e-01 1.000000000000000000e+00 -9.762399196624755859e-01 5.673971772193908691e-01 3.273356258869171143e-01 1.000000000000000000e+00 -9.776239991188049316e-01 5.773932933807373047e-01 3.319492638111114502e-01 1.000000000000000000e+00 -9.790080785751342773e-01 5.873894691467285156e-01 3.365628719329833984e-01 1.000000000000000000e+00 -9.803921580314636230e-01 5.973856449127197266e-01 3.411764800548553467e-01 1.000000000000000000e+00 -9.817762374877929688e-01 6.073817610740661621e-01 3.457900881767272949e-01 1.000000000000000000e+00 -9.831603169441223145e-01 6.173779368400573730e-01 3.504036962985992432e-01 1.000000000000000000e+00 -9.845443964004516602e-01 6.273741126060485840e-01 3.550173044204711914e-01 1.000000000000000000e+00 -9.859284758567810059e-01 6.373702287673950195e-01 3.596309125423431396e-01 1.000000000000000000e+00 -9.873125553131103516e-01 6.473664045333862305e-01 3.642445206642150879e-01 1.000000000000000000e+00 -9.886966347694396973e-01 6.573625802993774414e-01 3.688581287860870361e-01 1.000000000000000000e+00 -9.900807142257690430e-01 6.673586964607238770e-01 3.734717369079589844e-01 1.000000000000000000e+00 -9.914647936820983887e-01 6.773548722267150879e-01 3.780853450298309326e-01 1.000000000000000000e+00 -9.922337532043457031e-01 6.861976385116577148e-01 3.836216926574707031e-01 1.000000000000000000e+00 -9.923875331878662109e-01 6.938869953155517578e-01 3.900807499885559082e-01 1.000000000000000000e+00 -9.925413131713867188e-01 7.015762925148010254e-01 3.965397775173187256e-01 1.000000000000000000e+00 -9.926950931549072266e-01 7.092656493186950684e-01 4.029988348484039307e-01 1.000000000000000000e+00 -9.928489327430725098e-01 7.169550061225891113e-01 4.094578921794891357e-01 1.000000000000000000e+00 -9.930027127265930176e-01 7.246443629264831543e-01 4.159169495105743408e-01 1.000000000000000000e+00 -9.931564927101135254e-01 7.323337197303771973e-01 4.223760068416595459e-01 1.000000000000000000e+00 -9.933102726936340332e-01 7.400230765342712402e-01 4.288350641727447510e-01 1.000000000000000000e+00 -9.934640526771545410e-01 7.477124333381652832e-01 4.352941215038299561e-01 1.000000000000000000e+00 -9.936178326606750488e-01 7.554017901420593262e-01 4.417531788349151611e-01 1.000000000000000000e+00 -9.937716126441955566e-01 7.630911469459533691e-01 4.482122361660003662e-01 1.000000000000000000e+00 -9.939253926277160645e-01 7.707804441452026367e-01 4.546712934970855713e-01 1.000000000000000000e+00 -9.940791726112365723e-01 7.784698009490966797e-01 4.611303210258483887e-01 1.000000000000000000e+00 -9.942330121994018555e-01 7.861591577529907227e-01 4.675893783569335938e-01 1.000000000000000000e+00 -9.943867921829223633e-01 7.938485145568847656e-01 4.740484356880187988e-01 1.000000000000000000e+00 -9.945405721664428711e-01 8.015378713607788086e-01 4.805074930191040039e-01 1.000000000000000000e+00 -9.946943521499633789e-01 8.092272281646728516e-01 4.869665503501892090e-01 1.000000000000000000e+00 -9.948481321334838867e-01 8.169165849685668945e-01 4.934256076812744141e-01 1.000000000000000000e+00 -9.950019121170043945e-01 8.246059417724609375e-01 4.998846650123596191e-01 1.000000000000000000e+00 -9.951556921005249023e-01 8.322952985763549805e-01 5.063437223434448242e-01 1.000000000000000000e+00 -9.953094720840454102e-01 8.399845957756042480e-01 5.128027796745300293e-01 1.000000000000000000e+00 -9.954633116722106934e-01 8.476739525794982910e-01 5.192618370056152344e-01 1.000000000000000000e+00 -9.956170916557312012e-01 8.553633093833923340e-01 5.257208943367004395e-01 1.000000000000000000e+00 -9.957708716392517090e-01 8.630526661872863770e-01 5.321799516677856445e-01 1.000000000000000000e+00 -9.959246516227722168e-01 8.707420229911804199e-01 5.386390089988708496e-01 1.000000000000000000e+00 -9.960784316062927246e-01 8.784313797950744629e-01 5.450980663299560547e-01 1.000000000000000000e+00 -9.962322115898132324e-01 8.831987977027893066e-01 5.530949831008911133e-01 1.000000000000000000e+00 -9.963859915733337402e-01 8.879661560058593750e-01 5.610918998718261719e-01 1.000000000000000000e+00 -9.965397715568542480e-01 8.927335739135742188e-01 5.690888166427612305e-01 1.000000000000000000e+00 -9.966935515403747559e-01 8.975009322166442871e-01 5.770857334136962891e-01 1.000000000000000000e+00 -9.968473911285400391e-01 9.022683501243591309e-01 5.850826501846313477e-01 1.000000000000000000e+00 -9.970011711120605469e-01 9.070357680320739746e-01 5.930795669555664062e-01 1.000000000000000000e+00 -9.971549510955810547e-01 9.118031263351440430e-01 6.010764837265014648e-01 1.000000000000000000e+00 -9.973087310791015625e-01 9.165705442428588867e-01 6.090734601020812988e-01 1.000000000000000000e+00 -9.974625110626220703e-01 9.213379621505737305e-01 6.170703768730163574e-01 1.000000000000000000e+00 -9.976162910461425781e-01 9.261053204536437988e-01 6.250672936439514160e-01 1.000000000000000000e+00 -9.977700710296630859e-01 9.308727383613586426e-01 6.330642104148864746e-01 1.000000000000000000e+00 -9.979238510131835938e-01 9.356401562690734863e-01 6.410611271858215332e-01 1.000000000000000000e+00 -9.980776906013488770e-01 9.404075145721435547e-01 6.490580439567565918e-01 1.000000000000000000e+00 -9.982314705848693848e-01 9.451749324798583984e-01 6.570549607276916504e-01 1.000000000000000000e+00 -9.983852505683898926e-01 9.499423503875732422e-01 6.650518774986267090e-01 1.000000000000000000e+00 -9.985390305519104004e-01 9.547097086906433105e-01 6.730488538742065430e-01 1.000000000000000000e+00 -9.986928105354309082e-01 9.594771265983581543e-01 6.810457706451416016e-01 1.000000000000000000e+00 -9.988465905189514160e-01 9.642445445060729980e-01 6.890426874160766602e-01 1.000000000000000000e+00 -9.990003705024719238e-01 9.690119028091430664e-01 6.970396041870117188e-01 1.000000000000000000e+00 -9.991541504859924316e-01 9.737793207168579102e-01 7.050365209579467773e-01 1.000000000000000000e+00 -9.993079304695129395e-01 9.785467386245727539e-01 7.130334377288818359e-01 1.000000000000000000e+00 -9.994617700576782227e-01 9.833140969276428223e-01 7.210303544998168945e-01 1.000000000000000000e+00 -9.996155500411987305e-01 9.880815148353576660e-01 7.290272712707519531e-01 1.000000000000000000e+00 -9.997693300247192383e-01 9.928489327430725098e-01 7.370242476463317871e-01 1.000000000000000000e+00 -9.999231100082397461e-01 9.976162910461425781e-01 7.450211644172668457e-01 1.000000000000000000e+00 -9.970780611038208008e-01 9.987697005271911621e-01 7.450211644172668457e-01 1.000000000000000000e+00 -9.912341237068176270e-01 9.963091015815734863e-01 7.370242476463317871e-01 1.000000000000000000e+00 -9.853902459144592285e-01 9.938485026359558105e-01 7.290272712707519531e-01 1.000000000000000000e+00 -9.795463085174560547e-01 9.913879036903381348e-01 7.210303544998168945e-01 1.000000000000000000e+00 -9.737024307250976562e-01 9.889273643493652344e-01 7.130334377288818359e-01 1.000000000000000000e+00 -9.678584933280944824e-01 9.864667654037475586e-01 7.050365209579467773e-01 1.000000000000000000e+00 -9.620146155357360840e-01 9.840061664581298828e-01 6.970396041870117188e-01 1.000000000000000000e+00 -9.561706781387329102e-01 9.815455675125122070e-01 6.890426874160766602e-01 1.000000000000000000e+00 -9.503268003463745117e-01 9.790849685668945312e-01 6.810457706451416016e-01 1.000000000000000000e+00 -9.444828629493713379e-01 9.766243696212768555e-01 6.730488538742065430e-01 1.000000000000000000e+00 -9.386389851570129395e-01 9.741637706756591797e-01 6.650518774986267090e-01 1.000000000000000000e+00 -9.327951073646545410e-01 9.717031717300415039e-01 6.570549607276916504e-01 1.000000000000000000e+00 -9.269511699676513672e-01 9.692425727844238281e-01 6.490580439567565918e-01 1.000000000000000000e+00 -9.211072921752929688e-01 9.667820334434509277e-01 6.410611271858215332e-01 1.000000000000000000e+00 -9.152633547782897949e-01 9.643214344978332520e-01 6.330642104148864746e-01 1.000000000000000000e+00 -9.094194769859313965e-01 9.618608355522155762e-01 6.250672936439514160e-01 1.000000000000000000e+00 -9.035755395889282227e-01 9.594002366065979004e-01 6.170703768730163574e-01 1.000000000000000000e+00 -8.977316617965698242e-01 9.569396376609802246e-01 6.090734601020812988e-01 1.000000000000000000e+00 -8.918877243995666504e-01 9.544790387153625488e-01 6.010764837265014648e-01 1.000000000000000000e+00 -8.860438466072082520e-01 9.520184397697448730e-01 5.930795669555664062e-01 1.000000000000000000e+00 -8.801999092102050781e-01 9.495578408241271973e-01 5.850826501846313477e-01 1.000000000000000000e+00 -8.743560314178466797e-01 9.470972418785095215e-01 5.770857334136962891e-01 1.000000000000000000e+00 -8.685120940208435059e-01 9.446367025375366211e-01 5.690888166427612305e-01 1.000000000000000000e+00 -8.626682162284851074e-01 9.421761035919189453e-01 5.610918998718261719e-01 1.000000000000000000e+00 -8.568242788314819336e-01 9.397155046463012695e-01 5.530949831008911133e-01 1.000000000000000000e+00 -8.509804010391235352e-01 9.372549057006835938e-01 5.450980663299560547e-01 1.000000000000000000e+00 -8.431372642517089844e-01 9.338715672492980957e-01 5.400230884552001953e-01 1.000000000000000000e+00 -8.352941274642944336e-01 9.304882884025573730e-01 5.349481105804443359e-01 1.000000000000000000e+00 -8.274509906768798828e-01 9.271049499511718750e-01 5.298731327056884766e-01 1.000000000000000000e+00 -8.196078538894653320e-01 9.237216711044311523e-01 5.247981548309326172e-01 1.000000000000000000e+00 -8.117647171020507812e-01 9.203383326530456543e-01 5.197231769561767578e-01 1.000000000000000000e+00 -8.039215803146362305e-01 9.169549942016601562e-01 5.146481990814208984e-01 1.000000000000000000e+00 -7.960784435272216797e-01 9.135717153549194336e-01 5.095732212066650391e-01 1.000000000000000000e+00 -7.882353067398071289e-01 9.101883769035339355e-01 5.044982433319091797e-01 1.000000000000000000e+00 -7.803921699523925781e-01 9.068050980567932129e-01 4.994232952594757080e-01 1.000000000000000000e+00 -7.725490331649780273e-01 9.034217596054077148e-01 4.943483173847198486e-01 1.000000000000000000e+00 -7.647058963775634766e-01 9.000384211540222168e-01 4.892733693122863770e-01 1.000000000000000000e+00 -7.568627595901489258e-01 8.966551423072814941e-01 4.841983914375305176e-01 1.000000000000000000e+00 -7.490196228027343750e-01 8.932718038558959961e-01 4.791234135627746582e-01 1.000000000000000000e+00 -7.411764860153198242e-01 8.898885250091552734e-01 4.740484356880187988e-01 1.000000000000000000e+00 -7.333333492279052734e-01 8.865051865577697754e-01 4.689734578132629395e-01 1.000000000000000000e+00 -7.254902124404907227e-01 8.831218481063842773e-01 4.638985097408294678e-01 1.000000000000000000e+00 -7.176470756530761719e-01 8.797385692596435547e-01 4.588235318660736084e-01 1.000000000000000000e+00 -7.098039388656616211e-01 8.763552308082580566e-01 4.537485539913177490e-01 1.000000000000000000e+00 -7.019608020782470703e-01 8.729719519615173340e-01 4.486735761165618896e-01 1.000000000000000000e+00 -6.941176652908325195e-01 8.695886135101318359e-01 4.435986280441284180e-01 1.000000000000000000e+00 -6.862745285034179688e-01 8.662053346633911133e-01 4.385236501693725586e-01 1.000000000000000000e+00 -6.784313917160034180e-01 8.628219962120056152e-01 4.334486722946166992e-01 1.000000000000000000e+00 -6.705882549285888672e-01 8.594386577606201172e-01 4.283736944198608398e-01 1.000000000000000000e+00 -6.627451181411743164e-01 8.560553789138793945e-01 4.232987165451049805e-01 1.000000000000000000e+00 -6.549019813537597656e-01 8.526720404624938965e-01 4.182237684726715088e-01 1.000000000000000000e+00 -6.460592150688171387e-01 8.488273620605468750e-01 4.151480197906494141e-01 1.000000000000000000e+00 -6.362168192863464355e-01 8.445213437080383301e-01 4.140715003013610840e-01 1.000000000000000000e+00 -6.263744831085205078e-01 8.402153253555297852e-01 4.129950106143951416e-01 1.000000000000000000e+00 -6.165320873260498047e-01 8.359092473983764648e-01 4.119184911251068115e-01 1.000000000000000000e+00 -6.066897511482238770e-01 8.316032290458679199e-01 4.108419716358184814e-01 1.000000000000000000e+00 -5.968473553657531738e-01 8.272972106933593750e-01 4.097654819488525391e-01 1.000000000000000000e+00 -5.870050191879272461e-01 8.229911327362060547e-01 4.086889624595642090e-01 1.000000000000000000e+00 -5.771626234054565430e-01 8.186851143836975098e-01 4.076124429702758789e-01 1.000000000000000000e+00 -5.673202872276306152e-01 8.143790960311889648e-01 4.065359532833099365e-01 1.000000000000000000e+00 -5.574778914451599121e-01 8.100730776786804199e-01 4.054594337940216064e-01 1.000000000000000000e+00 -5.476354956626892090e-01 8.057669997215270996e-01 4.043829441070556641e-01 1.000000000000000000e+00 -5.377931594848632812e-01 8.014609813690185547e-01 4.033064246177673340e-01 1.000000000000000000e+00 -5.279507637023925781e-01 7.971549630165100098e-01 4.022299051284790039e-01 1.000000000000000000e+00 -5.181084275245666504e-01 7.928488850593566895e-01 4.011534154415130615e-01 1.000000000000000000e+00 -5.082660317420959473e-01 7.885428667068481445e-01 4.000768959522247314e-01 1.000000000000000000e+00 -4.984236955642700195e-01 7.842368483543395996e-01 3.990003764629364014e-01 1.000000000000000000e+00 -4.885813295841217041e-01 7.799307703971862793e-01 3.979238867759704590e-01 1.000000000000000000e+00 -4.787389338016510010e-01 7.756247520446777344e-01 3.968473672866821289e-01 1.000000000000000000e+00 -4.688965678215026855e-01 7.713187336921691895e-01 3.957708477973937988e-01 1.000000000000000000e+00 -4.590542018413543701e-01 7.670127153396606445e-01 3.946943581104278564e-01 1.000000000000000000e+00 -4.492118358612060547e-01 7.627066373825073242e-01 3.936178386211395264e-01 1.000000000000000000e+00 -4.393694698810577393e-01 7.584006190299987793e-01 3.925413191318511963e-01 1.000000000000000000e+00 -4.295271039009094238e-01 7.540946006774902344e-01 3.914648294448852539e-01 1.000000000000000000e+00 -4.196847379207611084e-01 7.497885227203369141e-01 3.903883099555969238e-01 1.000000000000000000e+00 -4.098423719406127930e-01 7.454825043678283691e-01 3.893117904663085938e-01 1.000000000000000000e+00 -4.000000059604644775e-01 7.411764860153198242e-01 3.882353007793426514e-01 1.000000000000000000e+00 -3.883121907711029053e-01 7.354863286018371582e-01 3.853133320808410645e-01 1.000000000000000000e+00 -3.766243755817413330e-01 7.297962307929992676e-01 3.823913931846618652e-01 1.000000000000000000e+00 -3.649365603923797607e-01 7.241061329841613770e-01 3.794694244861602783e-01 1.000000000000000000e+00 -3.532487452030181885e-01 7.184159755706787109e-01 3.765474855899810791e-01 1.000000000000000000e+00 -3.415609300136566162e-01 7.127258777618408203e-01 3.736255168914794922e-01 1.000000000000000000e+00 -3.298731148242950439e-01 7.070357799530029297e-01 3.707035779953002930e-01 1.000000000000000000e+00 -3.181852996349334717e-01 7.013456225395202637e-01 3.677816092967987061e-01 1.000000000000000000e+00 -3.064975142478942871e-01 6.956555247306823730e-01 3.648596704006195068e-01 1.000000000000000000e+00 -2.948096990585327148e-01 6.899654269218444824e-01 3.619377017021179199e-01 1.000000000000000000e+00 -2.831218838691711426e-01 6.842752695083618164e-01 3.590157628059387207e-01 1.000000000000000000e+00 -2.714340686798095703e-01 6.785851716995239258e-01 3.560938239097595215e-01 1.000000000000000000e+00 -2.597462534904479980e-01 6.728950142860412598e-01 3.531718552112579346e-01 1.000000000000000000e+00 -2.480584383010864258e-01 6.672049164772033691e-01 3.502499163150787354e-01 1.000000000000000000e+00 -2.363706231117248535e-01 6.615148186683654785e-01 3.473279476165771484e-01 1.000000000000000000e+00 -2.246828079223632812e-01 6.558246612548828125e-01 3.444060087203979492e-01 1.000000000000000000e+00 -2.129950076341629028e-01 6.501345634460449219e-01 3.414840400218963623e-01 1.000000000000000000e+00 -2.013071924448013306e-01 6.444444656372070312e-01 3.385621011257171631e-01 1.000000000000000000e+00 -1.896193772554397583e-01 6.387543082237243652e-01 3.356401324272155762e-01 1.000000000000000000e+00 -1.779315620660781860e-01 6.330642104148864746e-01 3.327181935310363770e-01 1.000000000000000000e+00 -1.662437468767166138e-01 6.273741126060485840e-01 3.297962248325347900e-01 1.000000000000000000e+00 -1.545559465885162354e-01 6.216839551925659180e-01 3.268742859363555908e-01 1.000000000000000000e+00 -1.428681313991546631e-01 6.159938573837280273e-01 3.239523172378540039e-01 1.000000000000000000e+00 -1.311803162097930908e-01 6.103036999702453613e-01 3.210303783416748047e-01 1.000000000000000000e+00 -1.194925010204315186e-01 6.046136021614074707e-01 3.181084096431732178e-01 1.000000000000000000e+00 -1.078046932816505432e-01 5.989235043525695801e-01 3.151864707469940186e-01 1.000000000000000000e+00 -9.996155649423599243e-02 5.923875570297241211e-01 3.118031620979309082e-01 1.000000000000000000e+00 -9.596309065818786621e-02 5.850057601928710938e-01 3.079584836959838867e-01 1.000000000000000000e+00 -9.196463227272033691e-02 5.776239633560180664e-01 3.041138052940368652e-01 1.000000000000000000e+00 -8.796616643667221069e-02 5.702422261238098145e-01 3.002691268920898438e-01 1.000000000000000000e+00 -8.396770805120468140e-02 5.628604292869567871e-01 2.964244484901428223e-01 1.000000000000000000e+00 -7.996924221515655518e-02 5.554786324501037598e-01 2.925797700881958008e-01 1.000000000000000000e+00 -7.597078382968902588e-02 5.480968952178955078e-01 2.887350916862487793e-01 1.000000000000000000e+00 -7.197231799364089966e-02 5.407150983810424805e-01 2.848904132843017578e-01 1.000000000000000000e+00 -6.797385960817337036e-02 5.333333611488342285e-01 2.810457646846771240e-01 1.000000000000000000e+00 -6.397539377212524414e-02 5.259515643119812012e-01 2.772010862827301025e-01 1.000000000000000000e+00 -5.997693166136741638e-02 5.185697674751281738e-01 2.733564078807830811e-01 1.000000000000000000e+00 -5.597846955060958862e-02 5.111880302429199219e-01 2.695117294788360596e-01 1.000000000000000000e+00 -5.198000743985176086e-02 5.038062334060668945e-01 2.656670510768890381e-01 1.000000000000000000e+00 -4.798154532909393311e-02 4.964244663715362549e-01 2.618223726749420166e-01 1.000000000000000000e+00 -4.398308321833610535e-02 4.890426695346832275e-01 2.579776942729949951e-01 1.000000000000000000e+00 -3.998462110757827759e-02 4.816609025001525879e-01 2.541330158710479736e-01 1.000000000000000000e+00 -3.598615899682044983e-02 4.742791354656219482e-01 2.502883374691009521e-01 1.000000000000000000e+00 -3.198769688606262207e-02 4.668973386287689209e-01 2.464436739683151245e-01 1.000000000000000000e+00 -2.798923477530479431e-02 4.595155715942382812e-01 2.425989955663681030e-01 1.000000000000000000e+00 -2.399077266454696655e-02 4.521338045597076416e-01 2.387543320655822754e-01 1.000000000000000000e+00 -1.999231055378913879e-02 4.447520077228546143e-01 2.349096536636352539e-01 1.000000000000000000e+00 -1.599384844303131104e-02 4.373702406883239746e-01 2.310649752616882324e-01 1.000000000000000000e+00 -1.199538633227348328e-02 4.299884736537933350e-01 2.272202968597412109e-01 1.000000000000000000e+00 -7.996924221515655518e-03 4.226066768169403076e-01 2.233756184577941895e-01 1.000000000000000000e+00 -3.998462110757827759e-03 4.152249097824096680e-01 2.195309549570083618e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.078431427478790283e-01 2.156862765550613403e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/Reds b/fastplotlib/utils/colormaps/Reds deleted file mode 100644 index daaee20b6..000000000 --- a/fastplotlib/utils/colormaps/Reds +++ /dev/null @@ -1,256 +0,0 @@ -1.000000000000000000e+00 9.607843160629272461e-01 9.411764740943908691e-01 1.000000000000000000e+00 -9.998769760131835938e-01 9.582006931304931641e-01 9.374855756759643555e-01 1.000000000000000000e+00 -9.997539520263671875e-01 9.556170701980590820e-01 9.337946772575378418e-01 1.000000000000000000e+00 -9.996309280395507812e-01 9.530334472656250000e-01 9.301037788391113281e-01 1.000000000000000000e+00 -9.995079040527343750e-01 9.504498243331909180e-01 9.264129400253295898e-01 1.000000000000000000e+00 -9.993848800659179688e-01 9.478662014007568359e-01 9.227220416069030762e-01 1.000000000000000000e+00 -9.992617964744567871e-01 9.452825784683227539e-01 9.190311431884765625e-01 1.000000000000000000e+00 -9.991387724876403809e-01 9.426989555358886719e-01 9.153402447700500488e-01 1.000000000000000000e+00 -9.990157485008239746e-01 9.401153326034545898e-01 9.116493463516235352e-01 1.000000000000000000e+00 -9.988927245140075684e-01 9.375317096710205078e-01 9.079584479331970215e-01 1.000000000000000000e+00 -9.987697005271911621e-01 9.349480867385864258e-01 9.042676091194152832e-01 1.000000000000000000e+00 -9.986466765403747559e-01 9.323644638061523438e-01 9.005767107009887695e-01 1.000000000000000000e+00 -9.985236525535583496e-01 9.297808408737182617e-01 8.968858122825622559e-01 1.000000000000000000e+00 -9.984006285667419434e-01 9.271972179412841797e-01 8.931949138641357422e-01 1.000000000000000000e+00 -9.982776045799255371e-01 9.246135950088500977e-01 8.895040154457092285e-01 1.000000000000000000e+00 -9.981545805931091309e-01 9.220299720764160156e-01 8.858131766319274902e-01 1.000000000000000000e+00 -9.980314970016479492e-01 9.194463491439819336e-01 8.821222782135009766e-01 1.000000000000000000e+00 -9.979084730148315430e-01 9.168627262115478516e-01 8.784313797950744629e-01 1.000000000000000000e+00 -9.977854490280151367e-01 9.142791032791137695e-01 8.747404813766479492e-01 1.000000000000000000e+00 -9.976624250411987305e-01 9.116954803466796875e-01 8.710495829582214355e-01 1.000000000000000000e+00 -9.975394010543823242e-01 9.091118574142456055e-01 8.673586845397949219e-01 1.000000000000000000e+00 -9.974163770675659180e-01 9.065282344818115234e-01 8.636678457260131836e-01 1.000000000000000000e+00 -9.972933530807495117e-01 9.039446115493774414e-01 8.599769473075866699e-01 1.000000000000000000e+00 -9.971703290939331055e-01 9.013609886169433594e-01 8.562860488891601562e-01 1.000000000000000000e+00 -9.970473051071166992e-01 8.987773656845092773e-01 8.525951504707336426e-01 1.000000000000000000e+00 -9.969242811203002930e-01 8.961937427520751953e-01 8.489042520523071289e-01 1.000000000000000000e+00 -9.968012571334838867e-01 8.936101794242858887e-01 8.452133536338806152e-01 1.000000000000000000e+00 -9.966781735420227051e-01 8.910265564918518066e-01 8.415225148200988770e-01 1.000000000000000000e+00 -9.965551495552062988e-01 8.884429335594177246e-01 8.378316164016723633e-01 1.000000000000000000e+00 -9.964321255683898926e-01 8.858593106269836426e-01 8.341407179832458496e-01 1.000000000000000000e+00 -9.963091015815734863e-01 8.832756876945495605e-01 8.304498195648193359e-01 1.000000000000000000e+00 -9.961860775947570801e-01 8.806920647621154785e-01 8.267589211463928223e-01 1.000000000000000000e+00 -9.960476756095886230e-01 8.778623342514038086e-01 8.227758407592773438e-01 1.000000000000000000e+00 -9.958016276359558105e-01 8.733102679252624512e-01 8.167474269866943359e-01 1.000000000000000000e+00 -9.955555796623229980e-01 8.687581419944763184e-01 8.107189536094665527e-01 1.000000000000000000e+00 -9.953094720840454102e-01 8.642060756683349609e-01 8.046904802322387695e-01 1.000000000000000000e+00 -9.950634241104125977e-01 8.596539497375488281e-01 7.986620664596557617e-01 1.000000000000000000e+00 -9.948173761367797852e-01 8.551018834114074707e-01 7.926335930824279785e-01 1.000000000000000000e+00 -9.945713281631469727e-01 8.505498170852661133e-01 7.866051793098449707e-01 1.000000000000000000e+00 -9.943252801895141602e-01 8.459976911544799805e-01 7.805767059326171875e-01 1.000000000000000000e+00 -9.940791726112365723e-01 8.414456248283386230e-01 7.745482325553894043e-01 1.000000000000000000e+00 -9.938331246376037598e-01 8.368934988975524902e-01 7.685198187828063965e-01 1.000000000000000000e+00 -9.935870766639709473e-01 8.323414325714111328e-01 7.624913454055786133e-01 1.000000000000000000e+00 -9.933410286903381348e-01 8.277893066406250000e-01 7.564628720283508301e-01 1.000000000000000000e+00 -9.930949807167053223e-01 8.232372403144836426e-01 7.504344582557678223e-01 1.000000000000000000e+00 -9.928489327430725098e-01 8.186851143836975098e-01 7.444059848785400391e-01 1.000000000000000000e+00 -9.926028251647949219e-01 8.141330480575561523e-01 7.383775711059570312e-01 1.000000000000000000e+00 -9.923567771911621094e-01 8.095809221267700195e-01 7.323490977287292480e-01 1.000000000000000000e+00 -9.921107292175292969e-01 8.050288558006286621e-01 7.263206243515014648e-01 1.000000000000000000e+00 -9.918646812438964844e-01 8.004767298698425293e-01 7.202922105789184570e-01 1.000000000000000000e+00 -9.916186332702636719e-01 7.959246635437011719e-01 7.142637372016906738e-01 1.000000000000000000e+00 -9.913725256919860840e-01 7.913725376129150391e-01 7.082353234291076660e-01 1.000000000000000000e+00 -9.911264777183532715e-01 7.868204712867736816e-01 7.022068500518798828e-01 1.000000000000000000e+00 -9.908804297447204590e-01 7.822683453559875488e-01 6.961783766746520996e-01 1.000000000000000000e+00 -9.906343817710876465e-01 7.777162790298461914e-01 6.901499629020690918e-01 1.000000000000000000e+00 -9.903883337974548340e-01 7.731641530990600586e-01 6.841214895248413086e-01 1.000000000000000000e+00 -9.901422262191772461e-01 7.686120867729187012e-01 6.780930161476135254e-01 1.000000000000000000e+00 -9.898961782455444336e-01 7.640599608421325684e-01 6.720646023750305176e-01 1.000000000000000000e+00 -9.896501302719116211e-01 7.595078945159912109e-01 6.660361289978027344e-01 1.000000000000000000e+00 -9.894040822982788086e-01 7.549557685852050781e-01 6.600077152252197266e-01 1.000000000000000000e+00 -9.891580343246459961e-01 7.504037022590637207e-01 6.539792418479919434e-01 1.000000000000000000e+00 -9.889119863510131836e-01 7.458515763282775879e-01 6.479507684707641602e-01 1.000000000000000000e+00 -9.886658787727355957e-01 7.412995100021362305e-01 6.419223546981811523e-01 1.000000000000000000e+00 -9.884198307991027832e-01 7.367473840713500977e-01 6.358938813209533691e-01 1.000000000000000000e+00 -9.882352948188781738e-01 7.320722937583923340e-01 6.299269795417785645e-01 1.000000000000000000e+00 -9.882352948188781738e-01 7.270280718803405762e-01 6.241445541381835938e-01 1.000000000000000000e+00 -9.882352948188781738e-01 7.219838500022888184e-01 6.183621883392333984e-01 1.000000000000000000e+00 -9.882352948188781738e-01 7.169396281242370605e-01 6.125797629356384277e-01 1.000000000000000000e+00 -9.882352948188781738e-01 7.118954062461853027e-01 6.067973971366882324e-01 1.000000000000000000e+00 -9.882352948188781738e-01 7.068511843681335449e-01 6.010149717330932617e-01 1.000000000000000000e+00 -9.882352948188781738e-01 7.018070220947265625e-01 5.952326059341430664e-01 1.000000000000000000e+00 -9.882352948188781738e-01 6.967628002166748047e-01 5.894502401351928711e-01 1.000000000000000000e+00 -9.882352948188781738e-01 6.917185783386230469e-01 5.836678147315979004e-01 1.000000000000000000e+00 -9.882352948188781738e-01 6.866743564605712891e-01 5.778854489326477051e-01 1.000000000000000000e+00 -9.882352948188781738e-01 6.816301345825195312e-01 5.721030235290527344e-01 1.000000000000000000e+00 -9.882352948188781738e-01 6.765859127044677734e-01 5.663206577301025391e-01 1.000000000000000000e+00 -9.882352948188781738e-01 6.715416908264160156e-01 5.605382323265075684e-01 1.000000000000000000e+00 -9.882352948188781738e-01 6.664975285530090332e-01 5.547558665275573730e-01 1.000000000000000000e+00 -9.882352948188781738e-01 6.614533066749572754e-01 5.489735007286071777e-01 1.000000000000000000e+00 -9.882352948188781738e-01 6.564090847969055176e-01 5.431910753250122070e-01 1.000000000000000000e+00 -9.882352948188781738e-01 6.513648629188537598e-01 5.374087095260620117e-01 1.000000000000000000e+00 -9.882352948188781738e-01 6.463206410408020020e-01 5.316262841224670410e-01 1.000000000000000000e+00 -9.882352948188781738e-01 6.412764191627502441e-01 5.258439183235168457e-01 1.000000000000000000e+00 -9.882352948188781738e-01 6.362321972846984863e-01 5.200614929199218750e-01 1.000000000000000000e+00 -9.882352948188781738e-01 6.311879754066467285e-01 5.142791271209716797e-01 1.000000000000000000e+00 -9.882352948188781738e-01 6.261438131332397461e-01 5.084967613220214844e-01 1.000000000000000000e+00 -9.882352948188781738e-01 6.210995912551879883e-01 5.027143359184265137e-01 1.000000000000000000e+00 -9.882352948188781738e-01 6.160553693771362305e-01 4.969319403171539307e-01 1.000000000000000000e+00 -9.882352948188781738e-01 6.110111474990844727e-01 4.911495447158813477e-01 1.000000000000000000e+00 -9.882352948188781738e-01 6.059669256210327148e-01 4.853671789169311523e-01 1.000000000000000000e+00 -9.882352948188781738e-01 6.009227037429809570e-01 4.795847833156585693e-01 1.000000000000000000e+00 -9.882352948188781738e-01 5.958784818649291992e-01 4.738023877143859863e-01 1.000000000000000000e+00 -9.882352948188781738e-01 5.908343195915222168e-01 4.680199921131134033e-01 1.000000000000000000e+00 -9.882352948188781738e-01 5.857900977134704590e-01 4.622375965118408203e-01 1.000000000000000000e+00 -9.882352948188781738e-01 5.807458758354187012e-01 4.564552009105682373e-01 1.000000000000000000e+00 -9.882352948188781738e-01 5.757016539573669434e-01 4.506728053092956543e-01 1.000000000000000000e+00 -9.881891608238220215e-01 5.707035660743713379e-01 4.452133774757385254e-01 1.000000000000000000e+00 -9.880661368370056152e-01 5.657823681831359863e-01 4.402922093868255615e-01 1.000000000000000000e+00 -9.879431128501892090e-01 5.608612298965454102e-01 4.353710114955902100e-01 1.000000000000000000e+00 -9.878200888633728027e-01 5.559400320053100586e-01 4.304498136043548584e-01 1.000000000000000000e+00 -9.876970648765563965e-01 5.510188341140747070e-01 4.255286455154418945e-01 1.000000000000000000e+00 -9.875739812850952148e-01 5.460976362228393555e-01 4.206074476242065430e-01 1.000000000000000000e+00 -9.874509572982788086e-01 5.411764979362487793e-01 4.156862795352935791e-01 1.000000000000000000e+00 -9.873279333114624023e-01 5.362553000450134277e-01 4.107650816440582275e-01 1.000000000000000000e+00 -9.872049093246459961e-01 5.313341021537780762e-01 4.058439135551452637e-01 1.000000000000000000e+00 -9.870818853378295898e-01 5.264129042625427246e-01 4.009227156639099121e-01 1.000000000000000000e+00 -9.869588613510131836e-01 5.214917063713073730e-01 3.960015475749969482e-01 1.000000000000000000e+00 -9.868358373641967773e-01 5.165705680847167969e-01 3.910803496837615967e-01 1.000000000000000000e+00 -9.867128133773803711e-01 5.116493701934814453e-01 3.861591815948486328e-01 1.000000000000000000e+00 -9.865897893905639648e-01 5.067281723022460938e-01 3.812379837036132812e-01 1.000000000000000000e+00 -9.864667654037475586e-01 5.018069744110107422e-01 3.763168156147003174e-01 1.000000000000000000e+00 -9.863437414169311523e-01 4.968858063220977783e-01 3.713956177234649658e-01 1.000000000000000000e+00 -9.862206578254699707e-01 4.919646382331848145e-01 3.664744198322296143e-01 1.000000000000000000e+00 -9.860976338386535645e-01 4.870434403419494629e-01 3.615532517433166504e-01 1.000000000000000000e+00 -9.859746098518371582e-01 4.821222722530364990e-01 3.566320538520812988e-01 1.000000000000000000e+00 -9.858515858650207520e-01 4.772010743618011475e-01 3.517108857631683350e-01 1.000000000000000000e+00 -9.857285618782043457e-01 4.722799062728881836e-01 3.467896878719329834e-01 1.000000000000000000e+00 -9.856055378913879395e-01 4.673587083816528320e-01 3.418685197830200195e-01 1.000000000000000000e+00 -9.854825139045715332e-01 4.624375104904174805e-01 3.369473218917846680e-01 1.000000000000000000e+00 -9.853594899177551270e-01 4.575163424015045166e-01 3.320261538028717041e-01 1.000000000000000000e+00 -9.852364659309387207e-01 4.525951445102691650e-01 3.271049559116363525e-01 1.000000000000000000e+00 -9.851134419441223145e-01 4.476739764213562012e-01 3.221837878227233887e-01 1.000000000000000000e+00 -9.849904179573059082e-01 4.427527785301208496e-01 3.172625899314880371e-01 1.000000000000000000e+00 -9.848673343658447266e-01 4.378316104412078857e-01 3.123414218425750732e-01 1.000000000000000000e+00 -9.847443103790283203e-01 4.329104125499725342e-01 3.074202239513397217e-01 1.000000000000000000e+00 -9.846212863922119141e-01 4.279892444610595703e-01 3.024990260601043701e-01 1.000000000000000000e+00 -9.844982624053955078e-01 4.230680465698242188e-01 2.975778579711914062e-01 1.000000000000000000e+00 -9.843752384185791016e-01 4.181468784809112549e-01 2.926566600799560547e-01 1.000000000000000000e+00 -9.835755228996276855e-01 4.127950668334960938e-01 2.883506417274475098e-01 1.000000000000000000e+00 -9.820991754531860352e-01 4.070127010345458984e-01 2.846597433090209961e-01 1.000000000000000000e+00 -9.806228280067443848e-01 4.012303054332733154e-01 2.809688448905944824e-01 1.000000000000000000e+00 -9.791464805603027344e-01 3.954479098320007324e-01 2.772779762744903564e-01 1.000000000000000000e+00 -9.776701331138610840e-01 3.896655142307281494e-01 2.735870778560638428e-01 1.000000000000000000e+00 -9.761937856674194336e-01 3.838831186294555664e-01 2.698961794376373291e-01 1.000000000000000000e+00 -9.747174382209777832e-01 3.781007230281829834e-01 2.662053108215332031e-01 1.000000000000000000e+00 -9.732410907745361328e-01 3.723183274269104004e-01 2.625144124031066895e-01 1.000000000000000000e+00 -9.717646837234497070e-01 3.665359616279602051e-01 2.588235437870025635e-01 1.000000000000000000e+00 -9.702883362770080566e-01 3.607535660266876221e-01 2.551326453685760498e-01 1.000000000000000000e+00 -9.688119888305664062e-01 3.549711704254150391e-01 2.514417469501495361e-01 1.000000000000000000e+00 -9.673356413841247559e-01 3.491887748241424561e-01 2.477508634328842163e-01 1.000000000000000000e+00 -9.658592939376831055e-01 3.434063792228698730e-01 2.440599799156188965e-01 1.000000000000000000e+00 -9.643829464912414551e-01 3.376239836215972900e-01 2.403690814971923828e-01 1.000000000000000000e+00 -9.629065990447998047e-01 3.318415880203247070e-01 2.366781979799270630e-01 1.000000000000000000e+00 -9.614301919937133789e-01 3.260592222213745117e-01 2.329873144626617432e-01 1.000000000000000000e+00 -9.599538445472717285e-01 3.202768266201019287e-01 2.292964309453964233e-01 1.000000000000000000e+00 -9.584774971008300781e-01 3.144944310188293457e-01 2.256055325269699097e-01 1.000000000000000000e+00 -9.570011496543884277e-01 3.087120354175567627e-01 2.219146490097045898e-01 1.000000000000000000e+00 -9.555248022079467773e-01 3.029296398162841797e-01 2.182237654924392700e-01 1.000000000000000000e+00 -9.540484547615051270e-01 2.971472442150115967e-01 2.145328670740127563e-01 1.000000000000000000e+00 -9.525721073150634766e-01 2.913648486137390137e-01 2.108419835567474365e-01 1.000000000000000000e+00 -9.510957598686218262e-01 2.855824828147888184e-01 2.071511000394821167e-01 1.000000000000000000e+00 -9.496193528175354004e-01 2.798000872135162354e-01 2.034602016210556030e-01 1.000000000000000000e+00 -9.481430053710937500e-01 2.740176916122436523e-01 1.997693181037902832e-01 1.000000000000000000e+00 -9.466666579246520996e-01 2.682352960109710693e-01 1.960784345865249634e-01 1.000000000000000000e+00 -9.451903104782104492e-01 2.624529004096984863e-01 1.923875361680984497e-01 1.000000000000000000e+00 -9.437139630317687988e-01 2.566705048084259033e-01 1.886966526508331299e-01 1.000000000000000000e+00 -9.422376155853271484e-01 2.508881092071533203e-01 1.850057691335678101e-01 1.000000000000000000e+00 -9.407612681388854980e-01 2.451057285070419312e-01 1.813148856163024902e-01 1.000000000000000000e+00 -9.392848610877990723e-01 2.393233329057693481e-01 1.776239871978759766e-01 1.000000000000000000e+00 -9.378085136413574219e-01 2.335409522056579590e-01 1.739331036806106567e-01 1.000000000000000000e+00 -9.344867467880249023e-01 2.286812812089920044e-01 1.713956147432327271e-01 1.000000000000000000e+00 -9.300576448440551758e-01 2.243752330541610718e-01 1.695501804351806641e-01 1.000000000000000000e+00 -9.256286025047302246e-01 2.200691998004913330e-01 1.677047312259674072e-01 1.000000000000000000e+00 -9.211995601654052734e-01 2.157631665468215942e-01 1.658592820167541504e-01 1.000000000000000000e+00 -9.167704582214355469e-01 2.114571332931518555e-01 1.640138477087020874e-01 1.000000000000000000e+00 -9.123414158821105957e-01 2.071511000394821167e-01 1.621683984994888306e-01 1.000000000000000000e+00 -9.079123139381408691e-01 2.028450667858123779e-01 1.603229492902755737e-01 1.000000000000000000e+00 -9.034832715988159180e-01 1.985390186309814453e-01 1.584775149822235107e-01 1.000000000000000000e+00 -8.990542292594909668e-01 1.942329853773117065e-01 1.566320657730102539e-01 1.000000000000000000e+00 -8.946251273155212402e-01 1.899269521236419678e-01 1.547866165637969971e-01 1.000000000000000000e+00 -8.901960849761962891e-01 1.856209188699722290e-01 1.529411822557449341e-01 1.000000000000000000e+00 -8.857669830322265625e-01 1.813148856163024902e-01 1.510957330465316772e-01 1.000000000000000000e+00 -8.813379406929016113e-01 1.770088374614715576e-01 1.492502838373184204e-01 1.000000000000000000e+00 -8.769088983535766602e-01 1.727028042078018188e-01 1.474048495292663574e-01 1.000000000000000000e+00 -8.724797964096069336e-01 1.683967709541320801e-01 1.455594003200531006e-01 1.000000000000000000e+00 -8.680507540702819824e-01 1.640907377004623413e-01 1.437139511108398438e-01 1.000000000000000000e+00 -8.636217117309570312e-01 1.597847044467926025e-01 1.418685168027877808e-01 1.000000000000000000e+00 -8.591926097869873047e-01 1.554786562919616699e-01 1.400230675935745239e-01 1.000000000000000000e+00 -8.547635674476623535e-01 1.511726230382919312e-01 1.381776183843612671e-01 1.000000000000000000e+00 -8.503344655036926270e-01 1.468665897846221924e-01 1.363321840763092041e-01 1.000000000000000000e+00 -8.459054231643676758e-01 1.425605565309524536e-01 1.344867348670959473e-01 1.000000000000000000e+00 -8.414763808250427246e-01 1.382545232772827148e-01 1.326412856578826904e-01 1.000000000000000000e+00 -8.370472788810729980e-01 1.339484751224517822e-01 1.307958513498306274e-01 1.000000000000000000e+00 -8.326182365417480469e-01 1.296424418687820435e-01 1.289504021406173706e-01 1.000000000000000000e+00 -8.281891345977783203e-01 1.253364086151123047e-01 1.271049529314041138e-01 1.000000000000000000e+00 -8.237600922584533691e-01 1.210303753614425659e-01 1.252595186233520508e-01 1.000000000000000000e+00 -8.193310499191284180e-01 1.167243346571922302e-01 1.234140694141387939e-01 1.000000000000000000e+00 -8.149019479751586914e-01 1.124183014035224915e-01 1.215686276555061340e-01 1.000000000000000000e+00 -8.104729056358337402e-01 1.081122681498527527e-01 1.197231858968734741e-01 1.000000000000000000e+00 -8.060438036918640137e-01 1.038062274456024170e-01 1.178777366876602173e-01 1.000000000000000000e+00 -8.016147613525390625e-01 9.950019419193267822e-02 1.160322949290275574e-01 1.000000000000000000e+00 -7.971857190132141113e-01 9.519415348768234253e-02 1.141868531703948975e-01 1.000000000000000000e+00 -7.925720810890197754e-01 9.328719973564147949e-02 1.129873096942901611e-01 1.000000000000000000e+00 -7.878969907760620117e-01 9.217993170022964478e-02 1.120030730962753296e-01 1.000000000000000000e+00 -7.832218408584594727e-01 9.107266366481781006e-02 1.110188364982604980e-01 1.000000000000000000e+00 -7.785466909408569336e-01 8.996539562940597534e-02 1.100345999002456665e-01 1.000000000000000000e+00 -7.738716006278991699e-01 8.885813504457473755e-02 1.090503633022308350e-01 1.000000000000000000e+00 -7.691964507102966309e-01 8.775086700916290283e-02 1.080661267042160034e-01 1.000000000000000000e+00 -7.645213603973388672e-01 8.664359897375106812e-02 1.070818901062011719e-01 1.000000000000000000e+00 -7.598462104797363281e-01 8.553633093833923340e-02 1.060976535081863403e-01 1.000000000000000000e+00 -7.551710605621337891e-01 8.442906290292739868e-02 1.051134169101715088e-01 1.000000000000000000e+00 -7.504959702491760254e-01 8.332180231809616089e-02 1.041291803121566772e-01 1.000000000000000000e+00 -7.458208203315734863e-01 8.221453428268432617e-02 1.031449437141418457e-01 1.000000000000000000e+00 -7.411457300186157227e-01 8.110726624727249146e-02 1.021607071161270142e-01 1.000000000000000000e+00 -7.364705801010131836e-01 7.999999821186065674e-02 1.011764705181121826e-01 1.000000000000000000e+00 -7.317954897880554199e-01 7.889273017644882202e-02 1.001922339200973511e-01 1.000000000000000000e+00 -7.271203398704528809e-01 7.778546959161758423e-02 9.920799732208251953e-02 1.000000000000000000e+00 -7.224451899528503418e-01 7.667820155620574951e-02 9.822376072406768799e-02 1.000000000000000000e+00 -7.177700996398925781e-01 7.557093352079391479e-02 9.723952412605285645e-02 1.000000000000000000e+00 -7.130949497222900391e-01 7.446366548538208008e-02 9.625528752803802490e-02 1.000000000000000000e+00 -7.084198594093322754e-01 7.335640490055084229e-02 9.527105093002319336e-02 1.000000000000000000e+00 -7.037447094917297363e-01 7.224913686513900757e-02 9.428681433200836182e-02 1.000000000000000000e+00 -6.990695595741271973e-01 7.114186882972717285e-02 9.330257773399353027e-02 1.000000000000000000e+00 -6.943944692611694336e-01 7.003460079431533813e-02 9.231834113597869873e-02 1.000000000000000000e+00 -6.897193193435668945e-01 6.892733275890350342e-02 9.133410453796386719e-02 1.000000000000000000e+00 -6.850442290306091309e-01 6.782007217407226562e-02 9.034986793994903564e-02 1.000000000000000000e+00 -6.803690791130065918e-01 6.671280413866043091e-02 8.936563134193420410e-02 1.000000000000000000e+00 -6.756939888000488281e-01 6.560553610324859619e-02 8.838139474391937256e-02 1.000000000000000000e+00 -6.710188388824462891e-01 6.449826806783676147e-02 8.739715814590454102e-02 1.000000000000000000e+00 -6.663436889648437500e-01 6.339100003242492676e-02 8.641292154788970947e-02 1.000000000000000000e+00 -6.616685986518859863e-01 6.228373572230339050e-02 8.542868494987487793e-02 1.000000000000000000e+00 -6.569934487342834473e-01 6.117647141218185425e-02 8.444444090127944946e-02 1.000000000000000000e+00 -6.523183584213256836e-01 6.006920337677001953e-02 8.346020430326461792e-02 1.000000000000000000e+00 -6.476432085037231445e-01 5.896193906664848328e-02 8.247596770524978638e-02 1.000000000000000000e+00 -6.403844952583312988e-01 5.720876529812812805e-02 8.149173110723495483e-02 1.000000000000000000e+00 -6.327566504478454590e-01 5.536332353949546814e-02 8.050749450922012329e-02 1.000000000000000000e+00 -6.251288056373596191e-01 5.351787805557250977e-02 7.952325791120529175e-02 1.000000000000000000e+00 -6.175009608268737793e-01 5.167243257164955139e-02 7.853902131319046021e-02 1.000000000000000000e+00 -6.098731160163879395e-01 4.982699081301689148e-02 7.755478471517562866e-02 1.000000000000000000e+00 -6.022452712059020996e-01 4.798154532909393311e-02 7.657054811716079712e-02 1.000000000000000000e+00 -5.946174263954162598e-01 4.613609984517097473e-02 7.558631151914596558e-02 1.000000000000000000e+00 -5.869896411895751953e-01 4.429065808653831482e-02 7.460207492113113403e-02 1.000000000000000000e+00 -5.793617963790893555e-01 4.244521260261535645e-02 7.361783832311630249e-02 1.000000000000000000e+00 -5.717339515686035156e-01 4.059977084398269653e-02 7.263360172510147095e-02 1.000000000000000000e+00 -5.641061067581176758e-01 3.875432536005973816e-02 7.164936512708663940e-02 1.000000000000000000e+00 -5.564782619476318359e-01 3.690887987613677979e-02 7.066512852907180786e-02 1.000000000000000000e+00 -5.488504171371459961e-01 3.506343811750411987e-02 6.968089193105697632e-02 1.000000000000000000e+00 -5.412226319313049316e-01 3.321799263358116150e-02 6.869665533304214478e-02 1.000000000000000000e+00 -5.335947871208190918e-01 3.137255087494850159e-02 6.771241873502731323e-02 1.000000000000000000e+00 -5.259669423103332520e-01 2.952710539102554321e-02 6.672818213701248169e-02 1.000000000000000000e+00 -5.183390974998474121e-01 2.768166176974773407e-02 6.574394553899765015e-02 1.000000000000000000e+00 -5.107112526893615723e-01 2.583621628582477570e-02 6.475970894098281860e-02 1.000000000000000000e+00 -5.030834078788757324e-01 2.399077266454696655e-02 6.377547234296798706e-02 1.000000000000000000e+00 -4.954555928707122803e-01 2.214532904326915741e-02 6.279123574495315552e-02 1.000000000000000000e+00 -4.878277480602264404e-01 2.029988542199134827e-02 6.180699914693832397e-02 1.000000000000000000e+00 -4.801999330520629883e-01 1.845443993806838989e-02 6.082275882363319397e-02 1.000000000000000000e+00 -4.725720882415771484e-01 1.660899631679058075e-02 5.983852222561836243e-02 1.000000000000000000e+00 -4.649442434310913086e-01 1.476355269551277161e-02 5.885428562760353088e-02 1.000000000000000000e+00 -4.573164284229278564e-01 1.291810814291238785e-02 5.787004902958869934e-02 1.000000000000000000e+00 -4.496885836124420166e-01 1.107266452163457870e-02 5.688581243157386780e-02 1.000000000000000000e+00 -4.420607388019561768e-01 9.227219969034194946e-03 5.590157583355903625e-02 1.000000000000000000e+00 -4.344329237937927246e-01 7.381776347756385803e-03 5.491733923554420471e-02 1.000000000000000000e+00 -4.268050789833068848e-01 5.536332260817289352e-03 5.393310263752937317e-02 1.000000000000000000e+00 -4.191772341728210449e-01 3.690888173878192902e-03 5.294886603951454163e-02 1.000000000000000000e+00 -4.115493893623352051e-01 1.845444086939096451e-03 5.196462944149971008e-02 1.000000000000000000e+00 -4.039215743541717529e-01 0.000000000000000000e+00 5.098039284348487854e-02 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/Set1 b/fastplotlib/utils/colormaps/Set1 deleted file mode 100644 index 1a7435364..000000000 --- a/fastplotlib/utils/colormaps/Set1 +++ /dev/null @@ -1,9 +0,0 @@ -8.941176533699035645e-01 1.019607856869697571e-01 1.098039224743843079e-01 1.000000000000000000e+00 -2.156862765550613403e-01 4.941176474094390869e-01 7.215686440467834473e-01 1.000000000000000000e+00 -3.019607961177825928e-01 6.862745285034179688e-01 2.901960909366607666e-01 1.000000000000000000e+00 -5.960784554481506348e-01 3.058823645114898682e-01 6.392157077789306641e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.980392158031463623e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 2.000000029802322388e-01 1.000000000000000000e+00 -6.509804129600524902e-01 3.372549116611480713e-01 1.568627506494522095e-01 1.000000000000000000e+00 -9.686274528503417969e-01 5.058823823928833008e-01 7.490196228027343750e-01 1.000000000000000000e+00 -6.000000238418579102e-01 6.000000238418579102e-01 6.000000238418579102e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/Set2 b/fastplotlib/utils/colormaps/Set2 deleted file mode 100644 index 5ae1478ab..000000000 --- a/fastplotlib/utils/colormaps/Set2 +++ /dev/null @@ -1,8 +0,0 @@ -4.000000059604644775e-01 7.607843279838562012e-01 6.470588445663452148e-01 1.000000000000000000e+00 -9.882352948188781738e-01 5.529412031173706055e-01 3.843137323856353760e-01 1.000000000000000000e+00 -5.529412031173706055e-01 6.274510025978088379e-01 7.960784435272216797e-01 1.000000000000000000e+00 -9.058823585510253906e-01 5.411764979362487793e-01 7.647058963775634766e-01 1.000000000000000000e+00 -6.509804129600524902e-01 8.470588326454162598e-01 3.294117748737335205e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.509804010391235352e-01 1.843137294054031372e-01 1.000000000000000000e+00 -8.980392217636108398e-01 7.686274647712707520e-01 5.803921818733215332e-01 1.000000000000000000e+00 -7.019608020782470703e-01 7.019608020782470703e-01 7.019608020782470703e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/Set3 b/fastplotlib/utils/colormaps/Set3 deleted file mode 100644 index 01ea38486..000000000 --- a/fastplotlib/utils/colormaps/Set3 +++ /dev/null @@ -1,12 +0,0 @@ -5.529412031173706055e-01 8.274509906768798828e-01 7.803921699523925781e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 7.019608020782470703e-01 1.000000000000000000e+00 -7.450980544090270996e-01 7.294117808341979980e-01 8.549019694328308105e-01 1.000000000000000000e+00 -9.843137264251708984e-01 5.019608139991760254e-01 4.470588266849517822e-01 1.000000000000000000e+00 -5.019608139991760254e-01 6.941176652908325195e-01 8.274509906768798828e-01 1.000000000000000000e+00 -9.921568632125854492e-01 7.058823704719543457e-01 3.843137323856353760e-01 1.000000000000000000e+00 -7.019608020782470703e-01 8.705882430076599121e-01 4.117647111415863037e-01 1.000000000000000000e+00 -9.882352948188781738e-01 8.039215803146362305e-01 8.980392217636108398e-01 1.000000000000000000e+00 -8.509804010391235352e-01 8.509804010391235352e-01 8.509804010391235352e-01 1.000000000000000000e+00 -7.372549176216125488e-01 5.019608139991760254e-01 7.411764860153198242e-01 1.000000000000000000e+00 -8.000000119209289551e-01 9.215686321258544922e-01 7.725490331649780273e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.294117689132690430e-01 4.352941215038299561e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/Spectral b/fastplotlib/utils/colormaps/Spectral deleted file mode 100644 index 2ce4f53d0..000000000 --- a/fastplotlib/utils/colormaps/Spectral +++ /dev/null @@ -1,256 +0,0 @@ -6.196078658103942871e-01 3.921568859368562698e-03 2.588235437870025635e-01 1.000000000000000000e+00 -6.280661225318908691e-01 1.330257579684257507e-02 2.608227729797363281e-01 1.000000000000000000e+00 -6.365244388580322266e-01 2.268358319997787476e-02 2.628220021724700928e-01 1.000000000000000000e+00 -6.449826955795288086e-01 3.206459060311317444e-02 2.648212313652038574e-01 1.000000000000000000e+00 -6.534410119056701660e-01 4.144559800624847412e-02 2.668204605579376221e-01 1.000000000000000000e+00 -6.618992686271667480e-01 5.082660540938377380e-02 2.688196897506713867e-01 1.000000000000000000e+00 -6.703575253486633301e-01 6.020761281251907349e-02 2.708189189434051514e-01 1.000000000000000000e+00 -6.788158416748046875e-01 6.958861649036407471e-02 2.728181481361389160e-01 1.000000000000000000e+00 -6.872740983963012695e-01 7.896962761878967285e-02 2.748173773288726807e-01 1.000000000000000000e+00 -6.957324147224426270e-01 8.835063129663467407e-02 2.768166065216064453e-01 1.000000000000000000e+00 -7.041906714439392090e-01 9.773164242506027222e-02 2.788158357143402100e-01 1.000000000000000000e+00 -7.126489877700805664e-01 1.071126461029052734e-01 2.808150649070739746e-01 1.000000000000000000e+00 -7.211072444915771484e-01 1.164936572313308716e-01 2.828142940998077393e-01 1.000000000000000000e+00 -7.295655608177185059e-01 1.258746683597564697e-01 2.848135232925415039e-01 1.000000000000000000e+00 -7.380238175392150879e-01 1.352556645870208740e-01 2.868127524852752686e-01 1.000000000000000000e+00 -7.464821338653564453e-01 1.446366757154464722e-01 2.888119816780090332e-01 1.000000000000000000e+00 -7.549403905868530273e-01 1.540176868438720703e-01 2.908112406730651855e-01 1.000000000000000000e+00 -7.633987069129943848e-01 1.633986979722976685e-01 2.928104698657989502e-01 1.000000000000000000e+00 -7.718569636344909668e-01 1.727796941995620728e-01 2.948096990585327148e-01 1.000000000000000000e+00 -7.803152799606323242e-01 1.821607053279876709e-01 2.968089282512664795e-01 1.000000000000000000e+00 -7.887735366821289062e-01 1.915417164564132690e-01 2.988081574440002441e-01 1.000000000000000000e+00 -7.972318530082702637e-01 2.009227275848388672e-01 3.008073866367340088e-01 1.000000000000000000e+00 -8.056901097297668457e-01 2.103037238121032715e-01 3.028066158294677734e-01 1.000000000000000000e+00 -8.141484260559082031e-01 2.196847349405288696e-01 3.048058450222015381e-01 1.000000000000000000e+00 -8.226066827774047852e-01 2.290657460689544678e-01 3.068050742149353027e-01 1.000000000000000000e+00 -8.310649991035461426e-01 2.384467571973800659e-01 3.088043034076690674e-01 1.000000000000000000e+00 -8.376778364181518555e-01 2.467512488365173340e-01 3.088811933994293213e-01 1.000000000000000000e+00 -8.424451947212219238e-01 2.539792358875274658e-01 3.070357441902160645e-01 1.000000000000000000e+00 -8.472126126289367676e-01 2.612072229385375977e-01 3.051903247833251953e-01 1.000000000000000000e+00 -8.519800305366516113e-01 2.684352099895477295e-01 3.033448755741119385e-01 1.000000000000000000e+00 -8.567473888397216797e-01 2.756631970405578613e-01 3.014994263648986816e-01 1.000000000000000000e+00 -8.615148067474365234e-01 2.828911840915679932e-01 2.996539771556854248e-01 1.000000000000000000e+00 -8.662822246551513672e-01 2.901191711425781250e-01 2.978085279464721680e-01 1.000000000000000000e+00 -8.710495829582214355e-01 2.973471879959106445e-01 2.959630787372589111e-01 1.000000000000000000e+00 -8.758170008659362793e-01 3.045751750469207764e-01 2.941176593303680420e-01 1.000000000000000000e+00 -8.805844187736511230e-01 3.118031620979309082e-01 2.922722101211547852e-01 1.000000000000000000e+00 -8.853517770767211914e-01 3.190311491489410400e-01 2.904267609119415283e-01 1.000000000000000000e+00 -8.901191949844360352e-01 3.262591361999511719e-01 2.885813117027282715e-01 1.000000000000000000e+00 -8.948865532875061035e-01 3.334871232509613037e-01 2.867358624935150146e-01 1.000000000000000000e+00 -8.996539711952209473e-01 3.407151103019714355e-01 2.848904132843017578e-01 1.000000000000000000e+00 -9.044213891029357910e-01 3.479430973529815674e-01 2.830449938774108887e-01 1.000000000000000000e+00 -9.091887474060058594e-01 3.551710844039916992e-01 2.811995446681976318e-01 1.000000000000000000e+00 -9.139561653137207031e-01 3.623990714550018311e-01 2.793540954589843750e-01 1.000000000000000000e+00 -9.187235832214355469e-01 3.696270585060119629e-01 2.775086462497711182e-01 1.000000000000000000e+00 -9.234909415245056152e-01 3.768550455570220947e-01 2.756631970405578613e-01 1.000000000000000000e+00 -9.282583594322204590e-01 3.840830326080322266e-01 2.738177478313446045e-01 1.000000000000000000e+00 -9.330257773399353027e-01 3.913110196590423584e-01 2.719723284244537354e-01 1.000000000000000000e+00 -9.377931356430053711e-01 3.985390365123748779e-01 2.701268792152404785e-01 1.000000000000000000e+00 -9.425605535507202148e-01 4.057670235633850098e-01 2.682814300060272217e-01 1.000000000000000000e+00 -9.473279714584350586e-01 4.129950106143951416e-01 2.664359807968139648e-01 1.000000000000000000e+00 -9.520953297615051270e-01 4.202229976654052734e-01 2.645905315876007080e-01 1.000000000000000000e+00 -9.568627476692199707e-01 4.274509847164154053e-01 2.627451121807098389e-01 1.000000000000000000e+00 -9.582468271255493164e-01 4.374471306800842285e-01 2.673587203025817871e-01 1.000000000000000000e+00 -9.596309065818786621e-01 4.474432766437530518e-01 2.719723284244537354e-01 1.000000000000000000e+00 -9.610149860382080078e-01 4.574394524097442627e-01 2.765859365463256836e-01 1.000000000000000000e+00 -9.623990654945373535e-01 4.674355983734130859e-01 2.811995446681976318e-01 1.000000000000000000e+00 -9.637831449508666992e-01 4.774317443370819092e-01 2.858131527900695801e-01 1.000000000000000000e+00 -9.651672244071960449e-01 4.874279201030731201e-01 2.904267609119415283e-01 1.000000000000000000e+00 -9.665513038635253906e-01 4.974240660667419434e-01 2.950403690338134766e-01 1.000000000000000000e+00 -9.679353833198547363e-01 5.074202418327331543e-01 2.996539771556854248e-01 1.000000000000000000e+00 -9.693194627761840820e-01 5.174163579940795898e-01 3.042675852775573730e-01 1.000000000000000000e+00 -9.707036018371582031e-01 5.274125337600708008e-01 3.088811933994293213e-01 1.000000000000000000e+00 -9.720876812934875488e-01 5.374087095260620117e-01 3.134948015213012695e-01 1.000000000000000000e+00 -9.734717607498168945e-01 5.474048256874084473e-01 3.181084096431732178e-01 1.000000000000000000e+00 -9.748558402061462402e-01 5.574010014533996582e-01 3.227220177650451660e-01 1.000000000000000000e+00 -9.762399196624755859e-01 5.673971772193908691e-01 3.273356258869171143e-01 1.000000000000000000e+00 -9.776239991188049316e-01 5.773932933807373047e-01 3.319492638111114502e-01 1.000000000000000000e+00 -9.790080785751342773e-01 5.873894691467285156e-01 3.365628719329833984e-01 1.000000000000000000e+00 -9.803921580314636230e-01 5.973856449127197266e-01 3.411764800548553467e-01 1.000000000000000000e+00 -9.817762374877929688e-01 6.073817610740661621e-01 3.457900881767272949e-01 1.000000000000000000e+00 -9.831603169441223145e-01 6.173779368400573730e-01 3.504036962985992432e-01 1.000000000000000000e+00 -9.845443964004516602e-01 6.273741126060485840e-01 3.550173044204711914e-01 1.000000000000000000e+00 -9.859284758567810059e-01 6.373702287673950195e-01 3.596309125423431396e-01 1.000000000000000000e+00 -9.873125553131103516e-01 6.473664045333862305e-01 3.642445206642150879e-01 1.000000000000000000e+00 -9.886966347694396973e-01 6.573625802993774414e-01 3.688581287860870361e-01 1.000000000000000000e+00 -9.900807142257690430e-01 6.673586964607238770e-01 3.734717369079589844e-01 1.000000000000000000e+00 -9.914647936820983887e-01 6.773548722267150879e-01 3.780853450298309326e-01 1.000000000000000000e+00 -9.922337532043457031e-01 6.861976385116577148e-01 3.836216926574707031e-01 1.000000000000000000e+00 -9.923875331878662109e-01 6.938869953155517578e-01 3.900807499885559082e-01 1.000000000000000000e+00 -9.925413131713867188e-01 7.015762925148010254e-01 3.965397775173187256e-01 1.000000000000000000e+00 -9.926950931549072266e-01 7.092656493186950684e-01 4.029988348484039307e-01 1.000000000000000000e+00 -9.928489327430725098e-01 7.169550061225891113e-01 4.094578921794891357e-01 1.000000000000000000e+00 -9.930027127265930176e-01 7.246443629264831543e-01 4.159169495105743408e-01 1.000000000000000000e+00 -9.931564927101135254e-01 7.323337197303771973e-01 4.223760068416595459e-01 1.000000000000000000e+00 -9.933102726936340332e-01 7.400230765342712402e-01 4.288350641727447510e-01 1.000000000000000000e+00 -9.934640526771545410e-01 7.477124333381652832e-01 4.352941215038299561e-01 1.000000000000000000e+00 -9.936178326606750488e-01 7.554017901420593262e-01 4.417531788349151611e-01 1.000000000000000000e+00 -9.937716126441955566e-01 7.630911469459533691e-01 4.482122361660003662e-01 1.000000000000000000e+00 -9.939253926277160645e-01 7.707804441452026367e-01 4.546712934970855713e-01 1.000000000000000000e+00 -9.940791726112365723e-01 7.784698009490966797e-01 4.611303210258483887e-01 1.000000000000000000e+00 -9.942330121994018555e-01 7.861591577529907227e-01 4.675893783569335938e-01 1.000000000000000000e+00 -9.943867921829223633e-01 7.938485145568847656e-01 4.740484356880187988e-01 1.000000000000000000e+00 -9.945405721664428711e-01 8.015378713607788086e-01 4.805074930191040039e-01 1.000000000000000000e+00 -9.946943521499633789e-01 8.092272281646728516e-01 4.869665503501892090e-01 1.000000000000000000e+00 -9.948481321334838867e-01 8.169165849685668945e-01 4.934256076812744141e-01 1.000000000000000000e+00 -9.950019121170043945e-01 8.246059417724609375e-01 4.998846650123596191e-01 1.000000000000000000e+00 -9.951556921005249023e-01 8.322952985763549805e-01 5.063437223434448242e-01 1.000000000000000000e+00 -9.953094720840454102e-01 8.399845957756042480e-01 5.128027796745300293e-01 1.000000000000000000e+00 -9.954633116722106934e-01 8.476739525794982910e-01 5.192618370056152344e-01 1.000000000000000000e+00 -9.956170916557312012e-01 8.553633093833923340e-01 5.257208943367004395e-01 1.000000000000000000e+00 -9.957708716392517090e-01 8.630526661872863770e-01 5.321799516677856445e-01 1.000000000000000000e+00 -9.959246516227722168e-01 8.707420229911804199e-01 5.386390089988708496e-01 1.000000000000000000e+00 -9.960784316062927246e-01 8.784313797950744629e-01 5.450980663299560547e-01 1.000000000000000000e+00 -9.962322115898132324e-01 8.831987977027893066e-01 5.530949831008911133e-01 1.000000000000000000e+00 -9.963859915733337402e-01 8.879661560058593750e-01 5.610918998718261719e-01 1.000000000000000000e+00 -9.965397715568542480e-01 8.927335739135742188e-01 5.690888166427612305e-01 1.000000000000000000e+00 -9.966935515403747559e-01 8.975009322166442871e-01 5.770857334136962891e-01 1.000000000000000000e+00 -9.968473911285400391e-01 9.022683501243591309e-01 5.850826501846313477e-01 1.000000000000000000e+00 -9.970011711120605469e-01 9.070357680320739746e-01 5.930795669555664062e-01 1.000000000000000000e+00 -9.971549510955810547e-01 9.118031263351440430e-01 6.010764837265014648e-01 1.000000000000000000e+00 -9.973087310791015625e-01 9.165705442428588867e-01 6.090734601020812988e-01 1.000000000000000000e+00 -9.974625110626220703e-01 9.213379621505737305e-01 6.170703768730163574e-01 1.000000000000000000e+00 -9.976162910461425781e-01 9.261053204536437988e-01 6.250672936439514160e-01 1.000000000000000000e+00 -9.977700710296630859e-01 9.308727383613586426e-01 6.330642104148864746e-01 1.000000000000000000e+00 -9.979238510131835938e-01 9.356401562690734863e-01 6.410611271858215332e-01 1.000000000000000000e+00 -9.980776906013488770e-01 9.404075145721435547e-01 6.490580439567565918e-01 1.000000000000000000e+00 -9.982314705848693848e-01 9.451749324798583984e-01 6.570549607276916504e-01 1.000000000000000000e+00 -9.983852505683898926e-01 9.499423503875732422e-01 6.650518774986267090e-01 1.000000000000000000e+00 -9.985390305519104004e-01 9.547097086906433105e-01 6.730488538742065430e-01 1.000000000000000000e+00 -9.986928105354309082e-01 9.594771265983581543e-01 6.810457706451416016e-01 1.000000000000000000e+00 -9.988465905189514160e-01 9.642445445060729980e-01 6.890426874160766602e-01 1.000000000000000000e+00 -9.990003705024719238e-01 9.690119028091430664e-01 6.970396041870117188e-01 1.000000000000000000e+00 -9.991541504859924316e-01 9.737793207168579102e-01 7.050365209579467773e-01 1.000000000000000000e+00 -9.993079304695129395e-01 9.785467386245727539e-01 7.130334377288818359e-01 1.000000000000000000e+00 -9.994617700576782227e-01 9.833140969276428223e-01 7.210303544998168945e-01 1.000000000000000000e+00 -9.996155500411987305e-01 9.880815148353576660e-01 7.290272712707519531e-01 1.000000000000000000e+00 -9.997693300247192383e-01 9.928489327430725098e-01 7.370242476463317871e-01 1.000000000000000000e+00 -9.999231100082397461e-01 9.976162910461425781e-01 7.450211644172668457e-01 1.000000000000000000e+00 -9.980776906013488770e-01 9.992310404777526855e-01 7.460207343101501465e-01 1.000000000000000000e+00 -9.942330121994018555e-01 9.976931810379028320e-01 7.400230765342712402e-01 1.000000000000000000e+00 -9.903883337974548340e-01 9.961553215980529785e-01 7.340253591537475586e-01 1.000000000000000000e+00 -9.865436553955078125e-01 9.946174621582031250e-01 7.280277013778686523e-01 1.000000000000000000e+00 -9.826989769935607910e-01 9.930796027183532715e-01 7.220299839973449707e-01 1.000000000000000000e+00 -9.788542985916137695e-01 9.915417432785034180e-01 7.160322666168212891e-01 1.000000000000000000e+00 -9.750096201896667480e-01 9.900038242340087891e-01 7.100346088409423828e-01 1.000000000000000000e+00 -9.711649417877197266e-01 9.884659647941589355e-01 7.040368914604187012e-01 1.000000000000000000e+00 -9.673202633857727051e-01 9.869281053543090820e-01 6.980392336845397949e-01 1.000000000000000000e+00 -9.634755849838256836e-01 9.853902459144592285e-01 6.920415163040161133e-01 1.000000000000000000e+00 -9.596309065818786621e-01 9.838523864746093750e-01 6.860438585281372070e-01 1.000000000000000000e+00 -9.557862281799316406e-01 9.823144674301147461e-01 6.800461411476135254e-01 1.000000000000000000e+00 -9.519415497779846191e-01 9.807766079902648926e-01 6.740484237670898438e-01 1.000000000000000000e+00 -9.480968713760375977e-01 9.792387485504150391e-01 6.680507659912109375e-01 1.000000000000000000e+00 -9.442521929740905762e-01 9.777008891105651855e-01 6.620530486106872559e-01 1.000000000000000000e+00 -9.404075145721435547e-01 9.761630296707153320e-01 6.560553908348083496e-01 1.000000000000000000e+00 -9.365628361701965332e-01 9.746251702308654785e-01 6.500576734542846680e-01 1.000000000000000000e+00 -9.327181577682495117e-01 9.730872511863708496e-01 6.440599560737609863e-01 1.000000000000000000e+00 -9.288735389709472656e-01 9.715493917465209961e-01 6.380622982978820801e-01 1.000000000000000000e+00 -9.250288605690002441e-01 9.700115323066711426e-01 6.320645809173583984e-01 1.000000000000000000e+00 -9.211841821670532227e-01 9.684736728668212891e-01 6.260669231414794922e-01 1.000000000000000000e+00 -9.173395037651062012e-01 9.669358134269714355e-01 6.200692057609558105e-01 1.000000000000000000e+00 -9.134948253631591797e-01 9.653978943824768066e-01 6.140714883804321289e-01 1.000000000000000000e+00 -9.096501469612121582e-01 9.638600349426269531e-01 6.080738306045532227e-01 1.000000000000000000e+00 -9.058054685592651367e-01 9.623221755027770996e-01 6.020761132240295410e-01 1.000000000000000000e+00 -9.019607901573181152e-01 9.607843160629272461e-01 5.960784554481506348e-01 1.000000000000000000e+00 -8.928873538970947266e-01 9.570934176445007324e-01 5.979238748550415039e-01 1.000000000000000000e+00 -8.838139176368713379e-01 9.534025192260742188e-01 5.997692942619323730e-01 1.000000000000000000e+00 -8.747404813766479492e-01 9.497116208076477051e-01 6.016147732734680176e-01 1.000000000000000000e+00 -8.656670451164245605e-01 9.460207819938659668e-01 6.034601926803588867e-01 1.000000000000000000e+00 -8.565936088562011719e-01 9.423298835754394531e-01 6.053056716918945312e-01 1.000000000000000000e+00 -8.475201725959777832e-01 9.386389851570129395e-01 6.071510910987854004e-01 1.000000000000000000e+00 -8.384467363357543945e-01 9.349480867385864258e-01 6.089965105056762695e-01 1.000000000000000000e+00 -8.293733000755310059e-01 9.312571883201599121e-01 6.108419895172119141e-01 1.000000000000000000e+00 -8.202998638153076172e-01 9.275663495063781738e-01 6.126874089241027832e-01 1.000000000000000000e+00 -8.112264275550842285e-01 9.238754510879516602e-01 6.145328879356384277e-01 1.000000000000000000e+00 -8.021529912948608398e-01 9.201845526695251465e-01 6.163783073425292969e-01 1.000000000000000000e+00 -7.930795550346374512e-01 9.164936542510986328e-01 6.182237863540649414e-01 1.000000000000000000e+00 -7.840061783790588379e-01 9.128027558326721191e-01 6.200692057609558105e-01 1.000000000000000000e+00 -7.749327421188354492e-01 9.091118574142456055e-01 6.219146251678466797e-01 1.000000000000000000e+00 -7.658593058586120605e-01 9.054210186004638672e-01 6.237601041793823242e-01 1.000000000000000000e+00 -7.567858695983886719e-01 9.017301201820373535e-01 6.256055235862731934e-01 1.000000000000000000e+00 -7.477124333381652832e-01 8.980392217636108398e-01 6.274510025978088379e-01 1.000000000000000000e+00 -7.386389970779418945e-01 8.943483233451843262e-01 6.292964220046997070e-01 1.000000000000000000e+00 -7.295655608177185059e-01 8.906574249267578125e-01 6.311418414115905762e-01 1.000000000000000000e+00 -7.204921245574951172e-01 8.869665265083312988e-01 6.329873204231262207e-01 1.000000000000000000e+00 -7.114186882972717285e-01 8.832756876945495605e-01 6.348327398300170898e-01 1.000000000000000000e+00 -7.023452520370483398e-01 8.795847892761230469e-01 6.366782188415527344e-01 1.000000000000000000e+00 -6.932718157768249512e-01 8.758938908576965332e-01 6.385236382484436035e-01 1.000000000000000000e+00 -6.841983795166015625e-01 8.722029924392700195e-01 6.403691172599792480e-01 1.000000000000000000e+00 -6.751249432563781738e-01 8.685120940208435059e-01 6.422145366668701172e-01 1.000000000000000000e+00 -6.652826070785522461e-01 8.645905256271362305e-01 6.432141661643981934e-01 1.000000000000000000e+00 -6.546712517738342285e-01 8.604382872581481934e-01 6.433679461479187012e-01 1.000000000000000000e+00 -6.440599560737609863e-01 8.562860488891601562e-01 6.435217261314392090e-01 1.000000000000000000e+00 -6.334486603736877441e-01 8.521338105201721191e-01 6.436755061149597168e-01 1.000000000000000000e+00 -6.228373646736145020e-01 8.479815721511840820e-01 6.438292860984802246e-01 1.000000000000000000e+00 -6.122260689735412598e-01 8.438292741775512695e-01 6.439830660820007324e-01 1.000000000000000000e+00 -6.016147732734680176e-01 8.396770358085632324e-01 6.441368460655212402e-01 1.000000000000000000e+00 -5.910034775733947754e-01 8.355247974395751953e-01 6.442906856536865234e-01 1.000000000000000000e+00 -5.803921818733215332e-01 8.313725590705871582e-01 6.444444656372070312e-01 1.000000000000000000e+00 -5.697808265686035156e-01 8.272203207015991211e-01 6.445982456207275391e-01 1.000000000000000000e+00 -5.591695308685302734e-01 8.230680227279663086e-01 6.447520256042480469e-01 1.000000000000000000e+00 -5.485582351684570312e-01 8.189157843589782715e-01 6.449058055877685547e-01 1.000000000000000000e+00 -5.379469394683837891e-01 8.147635459899902344e-01 6.450595855712890625e-01 1.000000000000000000e+00 -5.273356437683105469e-01 8.106113076210021973e-01 6.452133655548095703e-01 1.000000000000000000e+00 -5.167243480682373047e-01 8.064590692520141602e-01 6.453671455383300781e-01 1.000000000000000000e+00 -5.061130523681640625e-01 8.023068308830261230e-01 6.455209255218505859e-01 1.000000000000000000e+00 -4.955017268657684326e-01 7.981545329093933105e-01 6.456747651100158691e-01 1.000000000000000000e+00 -4.848904311656951904e-01 7.940022945404052734e-01 6.458285450935363770e-01 1.000000000000000000e+00 -4.742791354656219482e-01 7.898500561714172363e-01 6.459823250770568848e-01 1.000000000000000000e+00 -4.636678099632263184e-01 7.856978178024291992e-01 6.461361050605773926e-01 1.000000000000000000e+00 -4.530565142631530762e-01 7.815455794334411621e-01 6.462898850440979004e-01 1.000000000000000000e+00 -4.424452185630798340e-01 7.773932814598083496e-01 6.464436650276184082e-01 1.000000000000000000e+00 -4.318339228630065918e-01 7.732410430908203125e-01 6.465974450111389160e-01 1.000000000000000000e+00 -4.212225973606109619e-01 7.690888047218322754e-01 6.467512249946594238e-01 1.000000000000000000e+00 -4.106113016605377197e-01 7.649365663528442383e-01 6.469050645828247070e-01 1.000000000000000000e+00 -4.000000059604644775e-01 7.607843279838562012e-01 6.470588445663452148e-01 1.000000000000000000e+00 -3.920030891895294189e-01 7.518646717071533203e-01 6.507496833801269531e-01 1.000000000000000000e+00 -3.840061426162719727e-01 7.429450154304504395e-01 6.544405817985534668e-01 1.000000000000000000e+00 -3.760092258453369141e-01 7.340253591537475586e-01 6.581314802169799805e-01 1.000000000000000000e+00 -3.680123090744018555e-01 7.251057028770446777e-01 6.618223786354064941e-01 1.000000000000000000e+00 -3.600153923034667969e-01 7.161861062049865723e-01 6.655132770538330078e-01 1.000000000000000000e+00 -3.520184457302093506e-01 7.072664499282836914e-01 6.692041754722595215e-01 1.000000000000000000e+00 -3.440215289592742920e-01 6.983467936515808105e-01 6.728950142860412598e-01 1.000000000000000000e+00 -3.360246121883392334e-01 6.894271373748779297e-01 6.765859127044677734e-01 1.000000000000000000e+00 -3.280276954174041748e-01 6.805074810981750488e-01 6.802768111228942871e-01 1.000000000000000000e+00 -3.200307488441467285e-01 6.715878248214721680e-01 6.839677095413208008e-01 1.000000000000000000e+00 -3.120338320732116699e-01 6.626682281494140625e-01 6.876586079597473145e-01 1.000000000000000000e+00 -3.040369153022766113e-01 6.537485718727111816e-01 6.913495063781738281e-01 1.000000000000000000e+00 -2.960399985313415527e-01 6.448289155960083008e-01 6.950403451919555664e-01 1.000000000000000000e+00 -2.880430519580841064e-01 6.359092593193054199e-01 6.987312436103820801e-01 1.000000000000000000e+00 -2.800461351871490479e-01 6.269896030426025391e-01 7.024221420288085938e-01 1.000000000000000000e+00 -2.720492184162139893e-01 6.180699467658996582e-01 7.061130404472351074e-01 1.000000000000000000e+00 -2.640523016452789307e-01 6.091503500938415527e-01 7.098039388656616211e-01 1.000000000000000000e+00 -2.560553550720214844e-01 6.002306938171386719e-01 7.134948372840881348e-01 1.000000000000000000e+00 -2.480584383010864258e-01 5.913110375404357910e-01 7.171856760978698730e-01 1.000000000000000000e+00 -2.400615215301513672e-01 5.823913812637329102e-01 7.208765745162963867e-01 1.000000000000000000e+00 -2.320645898580551147e-01 5.734717249870300293e-01 7.245674729347229004e-01 1.000000000000000000e+00 -2.240676730871200562e-01 5.645520687103271484e-01 7.282583713531494141e-01 1.000000000000000000e+00 -2.160707414150238037e-01 5.556324720382690430e-01 7.319492697715759277e-01 1.000000000000000000e+00 -2.080738246440887451e-01 5.467128157615661621e-01 7.356401681900024414e-01 1.000000000000000000e+00 -2.000768929719924927e-01 5.377931594848632812e-01 7.393310070037841797e-01 1.000000000000000000e+00 -1.994617432355880737e-01 5.289503931999206543e-01 7.391003370285034180e-01 1.000000000000000000e+00 -2.062283754348754883e-01 5.201845169067382812e-01 7.349480986595153809e-01 1.000000000000000000e+00 -2.129950076341629028e-01 5.114187002182006836e-01 7.307958602905273438e-01 1.000000000000000000e+00 -2.197616249322891235e-01 5.026528239250183105e-01 7.266436219215393066e-01 1.000000000000000000e+00 -2.265282571315765381e-01 4.938869774341583252e-01 7.224913239479064941e-01 1.000000000000000000e+00 -2.332948893308639526e-01 4.851211011409759521e-01 7.183390855789184570e-01 1.000000000000000000e+00 -2.400615215301513672e-01 4.763552546501159668e-01 7.141868472099304199e-01 1.000000000000000000e+00 -2.468281388282775879e-01 4.675893783569335938e-01 7.100346088409423828e-01 1.000000000000000000e+00 -2.535947859287261963e-01 4.588235318660736084e-01 7.058823704719543457e-01 1.000000000000000000e+00 -2.603614032268524170e-01 4.500576555728912354e-01 7.017301321029663086e-01 1.000000000000000000e+00 -2.671280205249786377e-01 4.412918090820312500e-01 6.975778341293334961e-01 1.000000000000000000e+00 -2.738946676254272461e-01 4.325259625911712646e-01 6.934255957603454590e-01 1.000000000000000000e+00 -2.806612849235534668e-01 4.237600862979888916e-01 6.892733573913574219e-01 1.000000000000000000e+00 -2.874279022216796875e-01 4.149942398071289062e-01 6.851211190223693848e-01 1.000000000000000000e+00 -2.941945493221282959e-01 4.062283635139465332e-01 6.809688806533813477e-01 1.000000000000000000e+00 -3.009611666202545166e-01 3.974625170230865479e-01 6.768165826797485352e-01 1.000000000000000000e+00 -3.077277839183807373e-01 3.886966407299041748e-01 6.726643443107604980e-01 1.000000000000000000e+00 -3.144944310188293457e-01 3.799307942390441895e-01 6.685121059417724609e-01 1.000000000000000000e+00 -3.212610483169555664e-01 3.711649477481842041e-01 6.643598675727844238e-01 1.000000000000000000e+00 -3.280276954174041748e-01 3.623990714550018311e-01 6.602076292037963867e-01 1.000000000000000000e+00 -3.347943127155303955e-01 3.536332249641418457e-01 6.560553908348083496e-01 1.000000000000000000e+00 -3.415609300136566162e-01 3.448673486709594727e-01 6.519030928611755371e-01 1.000000000000000000e+00 -3.483275771141052246e-01 3.361015021800994873e-01 6.477508544921875000e-01 1.000000000000000000e+00 -3.550941944122314453e-01 3.273356258869171143e-01 6.435986161231994629e-01 1.000000000000000000e+00 -3.618608117103576660e-01 3.185697793960571289e-01 6.394463777542114258e-01 1.000000000000000000e+00 -3.686274588108062744e-01 3.098039329051971436e-01 6.352941393852233887e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/Wistia b/fastplotlib/utils/colormaps/Wistia deleted file mode 100644 index e21659ece..000000000 --- a/fastplotlib/utils/colormaps/Wistia +++ /dev/null @@ -1,256 +0,0 @@ -8.941176533699035645e-01 1.000000000000000000e+00 4.784313738346099854e-01 1.000000000000000000e+00 -8.957785367965698242e-01 9.985851645469665527e-01 4.725259542465209961e-01 1.000000000000000000e+00 -8.974394202232360840e-01 9.971703290939331055e-01 4.666205346584320068e-01 1.000000000000000000e+00 -8.991003632545471191e-01 9.957554936408996582e-01 4.607151150703430176e-01 1.000000000000000000e+00 -9.007612466812133789e-01 9.943406581878662109e-01 4.548096954822540283e-01 1.000000000000000000e+00 -9.024221301078796387e-01 9.929258227348327637e-01 4.489042758941650391e-01 1.000000000000000000e+00 -9.040830731391906738e-01 9.915109276771545410e-01 4.429988563060760498e-01 1.000000000000000000e+00 -9.057439565658569336e-01 9.900960922241210938e-01 4.370934367179870605e-01 1.000000000000000000e+00 -9.074048399925231934e-01 9.886812567710876465e-01 4.311880171298980713e-01 1.000000000000000000e+00 -9.090657234191894531e-01 9.872664213180541992e-01 4.252825975418090820e-01 1.000000000000000000e+00 -9.107266664505004883e-01 9.858515858650207520e-01 4.193771481513977051e-01 1.000000000000000000e+00 -9.123875498771667480e-01 9.844367504119873047e-01 4.134717285633087158e-01 1.000000000000000000e+00 -9.140484333038330078e-01 9.830219149589538574e-01 4.075663089752197266e-01 1.000000000000000000e+00 -9.157093167304992676e-01 9.816070795059204102e-01 4.016608893871307373e-01 1.000000000000000000e+00 -9.173702597618103027e-01 9.801922440528869629e-01 3.957554697990417480e-01 1.000000000000000000e+00 -9.190311431884765625e-01 9.787774085998535156e-01 3.898500502109527588e-01 1.000000000000000000e+00 -9.206920266151428223e-01 9.773625731468200684e-01 3.839446306228637695e-01 1.000000000000000000e+00 -9.223529696464538574e-01 9.759477376937866211e-01 3.780392110347747803e-01 1.000000000000000000e+00 -9.240138530731201172e-01 9.745328426361083984e-01 3.721337914466857910e-01 1.000000000000000000e+00 -9.256747364997863770e-01 9.731180071830749512e-01 3.662283718585968018e-01 1.000000000000000000e+00 -9.273356199264526367e-01 9.717031717300415039e-01 3.603229522705078125e-01 1.000000000000000000e+00 -9.289965629577636719e-01 9.702883362770080566e-01 3.544175326824188232e-01 1.000000000000000000e+00 -9.306574463844299316e-01 9.688735008239746094e-01 3.485121130943298340e-01 1.000000000000000000e+00 -9.323183298110961914e-01 9.674586653709411621e-01 3.426066935062408447e-01 1.000000000000000000e+00 -9.339792132377624512e-01 9.660438299179077148e-01 3.367012739181518555e-01 1.000000000000000000e+00 -9.356401562690734863e-01 9.646289944648742676e-01 3.307958543300628662e-01 1.000000000000000000e+00 -9.373010396957397461e-01 9.632141590118408203e-01 3.248904347419738770e-01 1.000000000000000000e+00 -9.389619231224060059e-01 9.617993235588073730e-01 3.189850151538848877e-01 1.000000000000000000e+00 -9.406228661537170410e-01 9.603844881057739258e-01 3.130795955657958984e-01 1.000000000000000000e+00 -9.422837495803833008e-01 9.589696526527404785e-01 3.071741759777069092e-01 1.000000000000000000e+00 -9.439446330070495605e-01 9.575547575950622559e-01 3.012687563896179199e-01 1.000000000000000000e+00 -9.456055164337158203e-01 9.561399221420288086e-01 2.953633069992065430e-01 1.000000000000000000e+00 -9.472664594650268555e-01 9.547250866889953613e-01 2.894578874111175537e-01 1.000000000000000000e+00 -9.489273428916931152e-01 9.533102512359619141e-01 2.835524678230285645e-01 1.000000000000000000e+00 -9.505882263183593750e-01 9.518954157829284668e-01 2.776470482349395752e-01 1.000000000000000000e+00 -9.522491097450256348e-01 9.504805803298950195e-01 2.717416286468505859e-01 1.000000000000000000e+00 -9.539100527763366699e-01 9.490657448768615723e-01 2.658362090587615967e-01 1.000000000000000000e+00 -9.555709362030029297e-01 9.476509094238281250e-01 2.599307894706726074e-01 1.000000000000000000e+00 -9.572318196296691895e-01 9.462360739707946777e-01 2.540253698825836182e-01 1.000000000000000000e+00 -9.588927626609802246e-01 9.448212385177612305e-01 2.481199502944946289e-01 1.000000000000000000e+00 -9.605536460876464844e-01 9.434064030647277832e-01 2.422145307064056396e-01 1.000000000000000000e+00 -9.622145295143127441e-01 9.419915676116943359e-01 2.363091111183166504e-01 1.000000000000000000e+00 -9.638754129409790039e-01 9.405766725540161133e-01 2.304036915302276611e-01 1.000000000000000000e+00 -9.655363559722900391e-01 9.391618371009826660e-01 2.244982719421386719e-01 1.000000000000000000e+00 -9.671972393989562988e-01 9.377470016479492188e-01 2.185928523540496826e-01 1.000000000000000000e+00 -9.688581228256225586e-01 9.363321661949157715e-01 2.126874327659606934e-01 1.000000000000000000e+00 -9.705190062522888184e-01 9.349173307418823242e-01 2.067820131778717041e-01 1.000000000000000000e+00 -9.721799492835998535e-01 9.335024952888488770e-01 2.008765786886215210e-01 1.000000000000000000e+00 -9.738408327102661133e-01 9.320876598358154297e-01 1.949711591005325317e-01 1.000000000000000000e+00 -9.755017161369323730e-01 9.306728243827819824e-01 1.890657395124435425e-01 1.000000000000000000e+00 -9.771626591682434082e-01 9.292579889297485352e-01 1.831603199243545532e-01 1.000000000000000000e+00 -9.788235425949096680e-01 9.278431534767150879e-01 1.772549003362655640e-01 1.000000000000000000e+00 -9.804844260215759277e-01 9.264283180236816406e-01 1.713494807481765747e-01 1.000000000000000000e+00 -9.821453094482421875e-01 9.250134825706481934e-01 1.654440611600875854e-01 1.000000000000000000e+00 -9.838062524795532227e-01 9.235985875129699707e-01 1.595386415719985962e-01 1.000000000000000000e+00 -9.854671359062194824e-01 9.221837520599365234e-01 1.536332219839096069e-01 1.000000000000000000e+00 -9.871280193328857422e-01 9.207689166069030762e-01 1.477278023958206177e-01 1.000000000000000000e+00 -9.887889027595520020e-01 9.193540811538696289e-01 1.418223828077316284e-01 1.000000000000000000e+00 -9.904498457908630371e-01 9.179392457008361816e-01 1.359169483184814453e-01 1.000000000000000000e+00 -9.921107292175292969e-01 9.165244102478027344e-01 1.300115287303924561e-01 1.000000000000000000e+00 -9.937716126441955566e-01 9.151095747947692871e-01 1.241061165928840637e-01 1.000000000000000000e+00 -9.954325556755065918e-01 9.136947393417358398e-01 1.182006895542144775e-01 1.000000000000000000e+00 -9.970934391021728516e-01 9.122799038887023926e-01 1.122952699661254883e-01 1.000000000000000000e+00 -9.987543225288391113e-01 9.108650684356689453e-01 1.063898503780364990e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.091426134109497070e-01 1.015609353780746460e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.064974784851074219e-01 9.996155649423599243e-02 1.000000000000000000e+00 -1.000000000000000000e+00 9.038523435592651367e-01 9.836217015981674194e-02 1.000000000000000000e+00 -1.000000000000000000e+00 9.012072086334228516e-01 9.676278382539749146e-02 1.000000000000000000e+00 -1.000000000000000000e+00 8.985620737075805664e-01 9.516339749097824097e-02 1.000000000000000000e+00 -1.000000000000000000e+00 8.959169387817382812e-01 9.356401115655899048e-02 1.000000000000000000e+00 -1.000000000000000000e+00 8.932718038558959961e-01 9.196463227272033691e-02 1.000000000000000000e+00 -1.000000000000000000e+00 8.906266689300537109e-01 9.036524593830108643e-02 1.000000000000000000e+00 -1.000000000000000000e+00 8.879815340042114258e-01 8.876585960388183594e-02 1.000000000000000000e+00 -1.000000000000000000e+00 8.853363990783691406e-01 8.716647326946258545e-02 1.000000000000000000e+00 -1.000000000000000000e+00 8.826912641525268555e-01 8.556708693504333496e-02 1.000000000000000000e+00 -1.000000000000000000e+00 8.800461292266845703e-01 8.396770805120468140e-02 1.000000000000000000e+00 -1.000000000000000000e+00 8.774009943008422852e-01 8.236832171678543091e-02 1.000000000000000000e+00 -1.000000000000000000e+00 8.747558593750000000e-01 8.076893538236618042e-02 1.000000000000000000e+00 -1.000000000000000000e+00 8.721107244491577148e-01 7.916954904794692993e-02 1.000000000000000000e+00 -1.000000000000000000e+00 8.694655895233154297e-01 7.757016271352767944e-02 1.000000000000000000e+00 -1.000000000000000000e+00 8.668204545974731445e-01 7.597078382968902588e-02 1.000000000000000000e+00 -1.000000000000000000e+00 8.641753196716308594e-01 7.437139749526977539e-02 1.000000000000000000e+00 -1.000000000000000000e+00 8.615301847457885742e-01 7.277201116085052490e-02 1.000000000000000000e+00 -1.000000000000000000e+00 8.588850498199462891e-01 7.117262482643127441e-02 1.000000000000000000e+00 -1.000000000000000000e+00 8.562399148941040039e-01 6.957323849201202393e-02 1.000000000000000000e+00 -1.000000000000000000e+00 8.535947799682617188e-01 6.797385960817337036e-02 1.000000000000000000e+00 -1.000000000000000000e+00 8.509496450424194336e-01 6.637447327375411987e-02 1.000000000000000000e+00 -1.000000000000000000e+00 8.483045101165771484e-01 6.477508693933486938e-02 1.000000000000000000e+00 -1.000000000000000000e+00 8.456593751907348633e-01 6.317570060491561890e-02 1.000000000000000000e+00 -1.000000000000000000e+00 8.430142402648925781e-01 6.157631799578666687e-02 1.000000000000000000e+00 -1.000000000000000000e+00 8.403691053390502930e-01 5.997693166136741638e-02 1.000000000000000000e+00 -1.000000000000000000e+00 8.377239704132080078e-01 5.837754532694816589e-02 1.000000000000000000e+00 -1.000000000000000000e+00 8.350788354873657227e-01 5.677816271781921387e-02 1.000000000000000000e+00 -1.000000000000000000e+00 8.324337005615234375e-01 5.517877638339996338e-02 1.000000000000000000e+00 -1.000000000000000000e+00 8.297885656356811523e-01 5.357939377427101135e-02 1.000000000000000000e+00 -1.000000000000000000e+00 8.271434307098388672e-01 5.198000743985176086e-02 1.000000000000000000e+00 -1.000000000000000000e+00 8.244982957839965820e-01 5.038062110543251038e-02 1.000000000000000000e+00 -1.000000000000000000e+00 8.218531608581542969e-01 4.878123849630355835e-02 1.000000000000000000e+00 -1.000000000000000000e+00 8.192080259323120117e-01 4.718185216188430786e-02 1.000000000000000000e+00 -1.000000000000000000e+00 8.165628314018249512e-01 4.558246955275535583e-02 1.000000000000000000e+00 -1.000000000000000000e+00 8.139176964759826660e-01 4.398308321833610535e-02 1.000000000000000000e+00 -1.000000000000000000e+00 8.112725615501403809e-01 4.238369688391685486e-02 1.000000000000000000e+00 -1.000000000000000000e+00 8.086274266242980957e-01 4.078431427478790283e-02 1.000000000000000000e+00 -1.000000000000000000e+00 8.059822916984558105e-01 3.918492794036865234e-02 1.000000000000000000e+00 -1.000000000000000000e+00 8.033371567726135254e-01 3.758554533123970032e-02 1.000000000000000000e+00 -1.000000000000000000e+00 8.006920218467712402e-01 3.598615899682044983e-02 1.000000000000000000e+00 -1.000000000000000000e+00 7.980468869209289551e-01 3.438677266240119934e-02 1.000000000000000000e+00 -1.000000000000000000e+00 7.954017519950866699e-01 3.278739005327224731e-02 1.000000000000000000e+00 -1.000000000000000000e+00 7.927566170692443848e-01 3.118800371885299683e-02 1.000000000000000000e+00 -1.000000000000000000e+00 7.901114821434020996e-01 2.958861924707889557e-02 1.000000000000000000e+00 -1.000000000000000000e+00 7.874663472175598145e-01 2.798923477530479431e-02 1.000000000000000000e+00 -1.000000000000000000e+00 7.848212122917175293e-01 2.638985030353069305e-02 1.000000000000000000e+00 -1.000000000000000000e+00 7.821760773658752441e-01 2.479046583175659180e-02 1.000000000000000000e+00 -1.000000000000000000e+00 7.795309424400329590e-01 2.319107949733734131e-02 1.000000000000000000e+00 -1.000000000000000000e+00 7.768858075141906738e-01 2.159169502556324005e-02 1.000000000000000000e+00 -1.000000000000000000e+00 7.742406725883483887e-01 1.999231055378913879e-02 1.000000000000000000e+00 -1.000000000000000000e+00 7.715955376625061035e-01 1.839292608201503754e-02 1.000000000000000000e+00 -1.000000000000000000e+00 7.689504027366638184e-01 1.679354161024093628e-02 1.000000000000000000e+00 -1.000000000000000000e+00 7.663052678108215332e-01 1.519415620714426041e-02 1.000000000000000000e+00 -1.000000000000000000e+00 7.636601328849792480e-01 1.359477080404758453e-02 1.000000000000000000e+00 -1.000000000000000000e+00 7.610149979591369629e-01 1.199538633227348328e-02 1.000000000000000000e+00 -1.000000000000000000e+00 7.583698630332946777e-01 1.039600186049938202e-02 1.000000000000000000e+00 -1.000000000000000000e+00 7.557247281074523926e-01 8.796616457402706146e-03 1.000000000000000000e+00 -1.000000000000000000e+00 7.530795931816101074e-01 7.197231985628604889e-03 1.000000000000000000e+00 -1.000000000000000000e+00 7.504344582557678223e-01 5.597847048193216324e-03 1.000000000000000000e+00 -1.000000000000000000e+00 7.477893233299255371e-01 3.998462110757827759e-03 1.000000000000000000e+00 -1.000000000000000000e+00 7.451441884040832520e-01 2.399077173322439194e-03 1.000000000000000000e+00 -1.000000000000000000e+00 7.424990534782409668e-01 7.996924105100333691e-04 1.000000000000000000e+00 -1.000000000000000000e+00 7.402845025062561035e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.385005950927734375e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.367166280746459961e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.349327206611633301e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.331488132476806641e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.313648462295532227e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.295809388160705566e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.277969717979431152e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.260130643844604492e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.242291569709777832e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.224451899528503418e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.206612825393676758e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.188773751258850098e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.170934081077575684e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.153095006942749023e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.135255932807922363e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.117416262626647949e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.099577188491821289e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.081737518310546875e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.063898444175720215e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.046059370040893555e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.028219699859619141e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.010380625724792480e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.992541551589965820e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.974701881408691406e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.956862807273864746e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.939023733139038086e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.921184062957763672e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.903344988822937012e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.885505318641662598e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.867666244506835938e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.849827170372009277e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.831987500190734863e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.814148426055908203e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.796309351921081543e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.778469681739807129e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.760630607604980469e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.742790937423706055e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.724951863288879395e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.707112789154052734e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.689273118972778320e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.671434044837951660e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.653594970703125000e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.635755300521850586e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.617916226387023926e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.600077152252197266e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.582237482070922852e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.564398407936096191e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.546558737754821777e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.528719663619995117e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.510880589485168457e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.493040919303894043e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.475201845169067383e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.457362771034240723e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.439523100852966309e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.421684026718139648e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.403844952583312988e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.386005282402038574e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.368166208267211914e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.350326538085937500e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.332487463951110840e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.314648389816284180e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.296808719635009766e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.278969645500183105e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.998615980148315430e-01 6.259284615516662598e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.996770620346069336e-01 6.238985061645507812e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.994925260543823242e-01 6.218684911727905273e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.993079304695129395e-01 6.198385357856750488e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.991233944892883301e-01 6.178085207939147949e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.989388585090637207e-01 6.157785654067993164e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.987543225288391113e-01 6.137485504150390625e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.985697865486145020e-01 6.117185950279235840e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.983852505683898926e-01 6.096885800361633301e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.982007145881652832e-01 6.076585650444030762e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.980161190032958984e-01 6.056286096572875977e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.978315830230712891e-01 6.035985946655273438e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.976470470428466797e-01 6.015686392784118652e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.974625110626220703e-01 5.995386242866516113e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.972779750823974609e-01 5.975086688995361328e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.970934391021728516e-01 5.954786539077758789e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.969089031219482422e-01 5.934486985206604004e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.967243075370788574e-01 5.914186835289001465e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.965397715568542480e-01 5.893886685371398926e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.963552355766296387e-01 5.873587131500244141e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.961706995964050293e-01 5.853286981582641602e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.959861636161804199e-01 5.832987427711486816e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.958016276359558105e-01 5.812687277793884277e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.956170916557312012e-01 5.792387723922729492e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.954325556755065918e-01 5.772087574005126953e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.952479600906372070e-01 5.751788020133972168e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.950634241104125977e-01 5.731487870216369629e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.948788881301879883e-01 5.711187720298767090e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.946943521499633789e-01 5.690888166427612305e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.945098161697387695e-01 5.670588016510009766e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.943252801895141602e-01 5.650288462638854980e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.941407442092895508e-01 5.629988312721252441e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.939561486244201660e-01 5.609688758850097656e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.937716126441955566e-01 5.589388608932495117e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.935870766639709473e-01 5.569089055061340332e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.934025406837463379e-01 5.548788905143737793e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.932180047035217285e-01 5.528488755226135254e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.930334687232971191e-01 5.508189201354980469e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.928489327430725098e-01 5.487889051437377930e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.926643371582031250e-01 5.467589497566223145e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.924798011779785156e-01 5.447289347648620605e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.922952651977539062e-01 5.426989793777465820e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.921107292175292969e-01 5.406689643859863281e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.919261932373046875e-01 5.386390089988708496e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.917416572570800781e-01 5.366089940071105957e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.915571212768554688e-01 5.345789790153503418e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.913725256919860840e-01 5.325490236282348633e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.911879897117614746e-01 5.305190086364746094e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.910034537315368652e-01 5.284890532493591309e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.908189177513122559e-01 5.264590382575988770e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.906343817710876465e-01 5.244290828704833984e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.904498457908630371e-01 5.223990678787231445e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.902653098106384277e-01 5.203691124916076660e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.900807142257690430e-01 5.183390974998474121e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.898961782455444336e-01 5.163090825080871582e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.897116422653198242e-01 5.142791271209716797e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.895271062850952148e-01 5.122491121292114258e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.893425703048706055e-01 5.102191567420959473e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.891580343246459961e-01 5.081891417503356934e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.889734983444213867e-01 5.061591863632202148e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.887889027595520020e-01 5.041291713714599609e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.886043667793273926e-01 5.020992159843444824e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.884198307991027832e-01 5.000692009925842285e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.882352948188781738e-01 4.980392158031463623e-01 0.000000000000000000e+00 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/YlGn b/fastplotlib/utils/colormaps/YlGn deleted file mode 100644 index 74f49a080..000000000 --- a/fastplotlib/utils/colormaps/YlGn +++ /dev/null @@ -1,256 +0,0 @@ -1.000000000000000000e+00 1.000000000000000000e+00 8.980392217636108398e-01 1.000000000000000000e+00 -9.990157485008239746e-01 9.996309280395507812e-01 8.926259279251098633e-01 1.000000000000000000e+00 -9.980314970016479492e-01 9.992617964744567871e-01 8.872126340866088867e-01 1.000000000000000000e+00 -9.970473051071166992e-01 9.988927245140075684e-01 8.817992806434631348e-01 1.000000000000000000e+00 -9.960630536079406738e-01 9.985236525535583496e-01 8.763859868049621582e-01 1.000000000000000000e+00 -9.950788021087646484e-01 9.981545805931091309e-01 8.709726929664611816e-01 1.000000000000000000e+00 -9.940945506095886230e-01 9.977854490280151367e-01 8.655593991279602051e-01 1.000000000000000000e+00 -9.931103587150573730e-01 9.974163770675659180e-01 8.601461052894592285e-01 1.000000000000000000e+00 -9.921261072158813477e-01 9.970473051071166992e-01 8.547328114509582520e-01 1.000000000000000000e+00 -9.911418557167053223e-01 9.966781735420227051e-01 8.493195176124572754e-01 1.000000000000000000e+00 -9.901576042175292969e-01 9.963091015815734863e-01 8.439061641693115234e-01 1.000000000000000000e+00 -9.891734123229980469e-01 9.959400296211242676e-01 8.384928703308105469e-01 1.000000000000000000e+00 -9.881891608238220215e-01 9.955709576606750488e-01 8.330795764923095703e-01 1.000000000000000000e+00 -9.872049093246459961e-01 9.952018260955810547e-01 8.276662826538085938e-01 1.000000000000000000e+00 -9.862206578254699707e-01 9.948327541351318359e-01 8.222529888153076172e-01 1.000000000000000000e+00 -9.852364659309387207e-01 9.944636821746826172e-01 8.168396949768066406e-01 1.000000000000000000e+00 -9.842522144317626953e-01 9.940945506095886230e-01 8.114264011383056641e-01 1.000000000000000000e+00 -9.832679629325866699e-01 9.937254786491394043e-01 8.060130476951599121e-01 1.000000000000000000e+00 -9.822837114334106445e-01 9.933564066886901855e-01 8.005997538566589355e-01 1.000000000000000000e+00 -9.812995195388793945e-01 9.929873347282409668e-01 7.951864600181579590e-01 1.000000000000000000e+00 -9.803152680397033691e-01 9.926182031631469727e-01 7.897731661796569824e-01 1.000000000000000000e+00 -9.793310165405273438e-01 9.922491312026977539e-01 7.843598723411560059e-01 1.000000000000000000e+00 -9.783467650413513184e-01 9.918800592422485352e-01 7.789465785026550293e-01 1.000000000000000000e+00 -9.773625731468200684e-01 9.915109276771545410e-01 7.735332846641540527e-01 1.000000000000000000e+00 -9.763783216476440430e-01 9.911418557167053223e-01 7.681199312210083008e-01 1.000000000000000000e+00 -9.753940701484680176e-01 9.907727837562561035e-01 7.627066373825073242e-01 1.000000000000000000e+00 -9.744098186492919922e-01 9.904037117958068848e-01 7.572933435440063477e-01 1.000000000000000000e+00 -9.734256267547607422e-01 9.900345802307128906e-01 7.518800497055053711e-01 1.000000000000000000e+00 -9.724413752555847168e-01 9.896655082702636719e-01 7.464667558670043945e-01 1.000000000000000000e+00 -9.714571237564086914e-01 9.892964363098144531e-01 7.410534620285034180e-01 1.000000000000000000e+00 -9.704728722572326660e-01 9.889273643493652344e-01 7.356401681900024414e-01 1.000000000000000000e+00 -9.694886803627014160e-01 9.885582327842712402e-01 7.302268147468566895e-01 1.000000000000000000e+00 -9.681661128997802734e-01 9.880507588386535645e-01 7.251518368721008301e-01 1.000000000000000000e+00 -9.644752144813537598e-01 9.865744113922119141e-01 7.224451899528503418e-01 1.000000000000000000e+00 -9.607843160629272461e-01 9.850980639457702637e-01 7.197385430335998535e-01 1.000000000000000000e+00 -9.570934176445007324e-01 9.836216568946838379e-01 7.170318961143493652e-01 1.000000000000000000e+00 -9.534025192260742188e-01 9.821453094482421875e-01 7.143252491950988770e-01 1.000000000000000000e+00 -9.497116208076477051e-01 9.806689620018005371e-01 7.116186022758483887e-01 1.000000000000000000e+00 -9.460207819938659668e-01 9.791926145553588867e-01 7.089119553565979004e-01 1.000000000000000000e+00 -9.423298835754394531e-01 9.777162671089172363e-01 7.062053084373474121e-01 1.000000000000000000e+00 -9.386389851570129395e-01 9.762399196624755859e-01 7.034986615180969238e-01 1.000000000000000000e+00 -9.349480867385864258e-01 9.747635722160339355e-01 7.007920145988464355e-01 1.000000000000000000e+00 -9.312571883201599121e-01 9.732872247695922852e-01 6.980853676795959473e-01 1.000000000000000000e+00 -9.275663495063781738e-01 9.718108177185058594e-01 6.953787207603454590e-01 1.000000000000000000e+00 -9.238754510879516602e-01 9.703344702720642090e-01 6.926720738410949707e-01 1.000000000000000000e+00 -9.201845526695251465e-01 9.688581228256225586e-01 6.899654269218444824e-01 1.000000000000000000e+00 -9.164936542510986328e-01 9.673817753791809082e-01 6.872587203979492188e-01 1.000000000000000000e+00 -9.128027558326721191e-01 9.659054279327392578e-01 6.845520734786987305e-01 1.000000000000000000e+00 -9.091118574142456055e-01 9.644290804862976074e-01 6.818454265594482422e-01 1.000000000000000000e+00 -9.054210186004638672e-01 9.629527330398559570e-01 6.791387796401977539e-01 1.000000000000000000e+00 -9.017301201820373535e-01 9.614763259887695312e-01 6.764321327209472656e-01 1.000000000000000000e+00 -8.980392217636108398e-01 9.599999785423278809e-01 6.737254858016967773e-01 1.000000000000000000e+00 -8.943483233451843262e-01 9.585236310958862305e-01 6.710188388824462891e-01 1.000000000000000000e+00 -8.906574249267578125e-01 9.570472836494445801e-01 6.683121919631958008e-01 1.000000000000000000e+00 -8.869665265083312988e-01 9.555709362030029297e-01 6.656055450439453125e-01 1.000000000000000000e+00 -8.832756876945495605e-01 9.540945887565612793e-01 6.628988981246948242e-01 1.000000000000000000e+00 -8.795847892761230469e-01 9.526182413101196289e-01 6.601922512054443359e-01 1.000000000000000000e+00 -8.758938908576965332e-01 9.511418938636779785e-01 6.574856042861938477e-01 1.000000000000000000e+00 -8.722029924392700195e-01 9.496654868125915527e-01 6.547789573669433594e-01 1.000000000000000000e+00 -8.685120940208435059e-01 9.481891393661499023e-01 6.520722508430480957e-01 1.000000000000000000e+00 -8.648211956024169922e-01 9.467127919197082520e-01 6.493656039237976074e-01 1.000000000000000000e+00 -8.611303567886352539e-01 9.452364444732666016e-01 6.466589570045471191e-01 1.000000000000000000e+00 -8.574394583702087402e-01 9.437600970268249512e-01 6.439523100852966309e-01 1.000000000000000000e+00 -8.537485599517822266e-01 9.422837495803833008e-01 6.412456631660461426e-01 1.000000000000000000e+00 -8.496270775794982910e-01 9.405920505523681641e-01 6.385697722434997559e-01 1.000000000000000000e+00 -8.442137837409973145e-01 9.382545351982116699e-01 6.359861493110656738e-01 1.000000000000000000e+00 -8.388004899024963379e-01 9.359169602394104004e-01 6.334025263786315918e-01 1.000000000000000000e+00 -8.333871364593505859e-01 9.335793852806091309e-01 6.308189034461975098e-01 1.000000000000000000e+00 -8.279738426208496094e-01 9.312418103218078613e-01 6.282352805137634277e-01 1.000000000000000000e+00 -8.225605487823486328e-01 9.289042949676513672e-01 6.256516575813293457e-01 1.000000000000000000e+00 -8.171472549438476562e-01 9.265667200088500977e-01 6.230680346488952637e-01 1.000000000000000000e+00 -8.117339611053466797e-01 9.242291450500488281e-01 6.204844117164611816e-01 1.000000000000000000e+00 -8.063206672668457031e-01 9.218915700912475586e-01 6.179007887840270996e-01 1.000000000000000000e+00 -8.009073138236999512e-01 9.195539951324462891e-01 6.153171658515930176e-01 1.000000000000000000e+00 -7.954940199851989746e-01 9.172164797782897949e-01 6.127335429191589355e-01 1.000000000000000000e+00 -7.900807261466979980e-01 9.148789048194885254e-01 6.101499199867248535e-01 1.000000000000000000e+00 -7.846674323081970215e-01 9.125413298606872559e-01 6.075662970542907715e-01 1.000000000000000000e+00 -7.792541384696960449e-01 9.102037549018859863e-01 6.049826741218566895e-01 1.000000000000000000e+00 -7.738408446311950684e-01 9.078661799430847168e-01 6.023990511894226074e-01 1.000000000000000000e+00 -7.684275507926940918e-01 9.055286645889282227e-01 5.998154282569885254e-01 1.000000000000000000e+00 -7.630141973495483398e-01 9.031910896301269531e-01 5.972318053245544434e-01 1.000000000000000000e+00 -7.576009035110473633e-01 9.008535146713256836e-01 5.946482419967651367e-01 1.000000000000000000e+00 -7.521876096725463867e-01 8.985159397125244141e-01 5.920646190643310547e-01 1.000000000000000000e+00 -7.467743158340454102e-01 8.961783647537231445e-01 5.894809961318969727e-01 1.000000000000000000e+00 -7.413610219955444336e-01 8.938408493995666504e-01 5.868973731994628906e-01 1.000000000000000000e+00 -7.359477281570434570e-01 8.915032744407653809e-01 5.843137502670288086e-01 1.000000000000000000e+00 -7.305344343185424805e-01 8.891656994819641113e-01 5.817301273345947266e-01 1.000000000000000000e+00 -7.251210808753967285e-01 8.868281245231628418e-01 5.791465044021606445e-01 1.000000000000000000e+00 -7.197077870368957520e-01 8.844906091690063477e-01 5.765628814697265625e-01 1.000000000000000000e+00 -7.142944931983947754e-01 8.821530342102050781e-01 5.739792585372924805e-01 1.000000000000000000e+00 -7.088811993598937988e-01 8.798154592514038086e-01 5.713956356048583984e-01 1.000000000000000000e+00 -7.034679055213928223e-01 8.774778842926025391e-01 5.688120126724243164e-01 1.000000000000000000e+00 -6.980546116828918457e-01 8.751403093338012695e-01 5.662283897399902344e-01 1.000000000000000000e+00 -6.926413178443908691e-01 8.728027939796447754e-01 5.636447668075561523e-01 1.000000000000000000e+00 -6.872279644012451172e-01 8.704652190208435059e-01 5.610611438751220703e-01 1.000000000000000000e+00 -6.818146705627441406e-01 8.681276440620422363e-01 5.584775209426879883e-01 1.000000000000000000e+00 -6.759861707687377930e-01 8.656055331230163574e-01 5.558938980102539062e-01 1.000000000000000000e+00 -6.694656014442443848e-01 8.627758622169494629e-01 5.533102750778198242e-01 1.000000000000000000e+00 -6.629450321197509766e-01 8.599461913108825684e-01 5.507266521453857422e-01 1.000000000000000000e+00 -6.564244627952575684e-01 8.571165204048156738e-01 5.481430292129516602e-01 1.000000000000000000e+00 -6.499038934707641602e-01 8.542867898941040039e-01 5.455594062805175781e-01 1.000000000000000000e+00 -6.433833241462707520e-01 8.514571189880371094e-01 5.429757833480834961e-01 1.000000000000000000e+00 -6.368627548217773438e-01 8.486274480819702148e-01 5.403921604156494141e-01 1.000000000000000000e+00 -6.303421854972839355e-01 8.457977771759033203e-01 5.378085374832153320e-01 1.000000000000000000e+00 -6.238216161727905273e-01 8.429681062698364258e-01 5.352249145507812500e-01 1.000000000000000000e+00 -6.173010468482971191e-01 8.401384353637695312e-01 5.326412916183471680e-01 1.000000000000000000e+00 -6.107804775238037109e-01 8.373087048530578613e-01 5.300576686859130859e-01 1.000000000000000000e+00 -6.042599081993103027e-01 8.344790339469909668e-01 5.274740457534790039e-01 1.000000000000000000e+00 -5.977393388748168945e-01 8.316493630409240723e-01 5.248904228210449219e-01 1.000000000000000000e+00 -5.912187695503234863e-01 8.288196921348571777e-01 5.223067998886108398e-01 1.000000000000000000e+00 -5.846982002258300781e-01 8.259900212287902832e-01 5.197231769561767578e-01 1.000000000000000000e+00 -5.781776309013366699e-01 8.231603503227233887e-01 5.171395540237426758e-01 1.000000000000000000e+00 -5.716570615768432617e-01 8.203306198120117188e-01 5.145559310913085938e-01 1.000000000000000000e+00 -5.651364922523498535e-01 8.175009489059448242e-01 5.119723081588745117e-01 1.000000000000000000e+00 -5.586159229278564453e-01 8.146712779998779297e-01 5.093886852264404297e-01 1.000000000000000000e+00 -5.520953536033630371e-01 8.118416070938110352e-01 5.068050622940063477e-01 1.000000000000000000e+00 -5.455747842788696289e-01 8.090119361877441406e-01 5.042214393615722656e-01 1.000000000000000000e+00 -5.390542149543762207e-01 8.061822652816772461e-01 5.016378164291381836e-01 1.000000000000000000e+00 -5.325336456298828125e-01 8.033525347709655762e-01 4.990542232990264893e-01 1.000000000000000000e+00 -5.260130763053894043e-01 8.005228638648986816e-01 4.964706003665924072e-01 1.000000000000000000e+00 -5.194925069808959961e-01 7.976931929588317871e-01 4.938869774341583252e-01 1.000000000000000000e+00 -5.129719376564025879e-01 7.948635220527648926e-01 4.913033545017242432e-01 1.000000000000000000e+00 -5.064513683319091797e-01 7.920338511466979980e-01 4.887197315692901611e-01 1.000000000000000000e+00 -4.999307990074157715e-01 7.892041802406311035e-01 4.861361086368560791e-01 1.000000000000000000e+00 -4.934102296829223633e-01 7.863744497299194336e-01 4.835524857044219971e-01 1.000000000000000000e+00 -4.868896603584289551e-01 7.835447788238525391e-01 4.809688627719879150e-01 1.000000000000000000e+00 -4.803690910339355469e-01 7.807151079177856445e-01 4.783852398395538330e-01 1.000000000000000000e+00 -4.738485217094421387e-01 7.778854370117187500e-01 4.758016169071197510e-01 1.000000000000000000e+00 -4.672049283981323242e-01 7.748097181320190430e-01 4.727873802185058594e-01 1.000000000000000000e+00 -4.604382812976837158e-01 7.714878916740417480e-01 4.693425595760345459e-01 1.000000000000000000e+00 -4.536716639995574951e-01 7.681660652160644531e-01 4.658977389335632324e-01 1.000000000000000000e+00 -4.469050467014312744e-01 7.648442983627319336e-01 4.624528884887695312e-01 1.000000000000000000e+00 -4.401383996009826660e-01 7.615224719047546387e-01 4.590080678462982178e-01 1.000000000000000000e+00 -4.333717823028564453e-01 7.582007050514221191e-01 4.555632472038269043e-01 1.000000000000000000e+00 -4.266051650047302246e-01 7.548788785934448242e-01 4.521184265613555908e-01 1.000000000000000000e+00 -4.198385179042816162e-01 7.515571117401123047e-01 4.486735761165618896e-01 1.000000000000000000e+00 -4.130719006061553955e-01 7.482352852821350098e-01 4.452287554740905762e-01 1.000000000000000000e+00 -4.063052535057067871e-01 7.449135184288024902e-01 4.417839348316192627e-01 1.000000000000000000e+00 -3.995386362075805664e-01 7.415916919708251953e-01 4.383391141891479492e-01 1.000000000000000000e+00 -3.927720189094543457e-01 7.382699251174926758e-01 4.348942637443542480e-01 1.000000000000000000e+00 -3.860053718090057373e-01 7.349480986595153809e-01 4.314494431018829346e-01 1.000000000000000000e+00 -3.792387545108795166e-01 7.316262722015380859e-01 4.280046224594116211e-01 1.000000000000000000e+00 -3.724721372127532959e-01 7.283045053482055664e-01 4.245597720146179199e-01 1.000000000000000000e+00 -3.657054901123046875e-01 7.249826788902282715e-01 4.211149513721466064e-01 1.000000000000000000e+00 -3.589388728141784668e-01 7.216609120368957520e-01 4.176701307296752930e-01 1.000000000000000000e+00 -3.521722555160522461e-01 7.183390855789184570e-01 4.142253100872039795e-01 1.000000000000000000e+00 -3.454056084156036377e-01 7.150173187255859375e-01 4.107804596424102783e-01 1.000000000000000000e+00 -3.386389911174774170e-01 7.116954922676086426e-01 4.073356389999389648e-01 1.000000000000000000e+00 -3.318723440170288086e-01 7.083737254142761230e-01 4.038908183574676514e-01 1.000000000000000000e+00 -3.251057267189025879e-01 7.050518989562988281e-01 4.004459679126739502e-01 1.000000000000000000e+00 -3.183391094207763672e-01 7.017301321029663086e-01 3.970011472702026367e-01 1.000000000000000000e+00 -3.115724623203277588e-01 6.984083056449890137e-01 3.935563266277313232e-01 1.000000000000000000e+00 -3.048058450222015381e-01 6.950864791870117188e-01 3.901115059852600098e-01 1.000000000000000000e+00 -2.980392277240753174e-01 6.917647123336791992e-01 3.866666555404663086e-01 1.000000000000000000e+00 -2.912725806236267090e-01 6.884428858757019043e-01 3.832218348979949951e-01 1.000000000000000000e+00 -2.845059633255004883e-01 6.851211190223693848e-01 3.797770142555236816e-01 1.000000000000000000e+00 -2.777393162250518799e-01 6.817992925643920898e-01 3.763321936130523682e-01 1.000000000000000000e+00 -2.709726989269256592e-01 6.784775257110595703e-01 3.728873431682586670e-01 1.000000000000000000e+00 -2.642060816287994385e-01 6.751556992530822754e-01 3.694425225257873535e-01 1.000000000000000000e+00 -2.574394345283508301e-01 6.718339323997497559e-01 3.659977018833160400e-01 1.000000000000000000e+00 -2.525951564311981201e-01 6.675893664360046387e-01 3.627066612243652344e-01 1.000000000000000000e+00 -2.489042729139328003e-01 6.627912521362304688e-01 3.595078885555267334e-01 1.000000000000000000e+00 -2.452133744955062866e-01 6.579930782318115234e-01 3.563091158866882324e-01 1.000000000000000000e+00 -2.415224909782409668e-01 6.531949043273925781e-01 3.531103432178497314e-01 1.000000000000000000e+00 -2.378316074609756470e-01 6.483967900276184082e-01 3.499115705490112305e-01 1.000000000000000000e+00 -2.341407090425491333e-01 6.435986161231994629e-01 3.467127978801727295e-01 1.000000000000000000e+00 -2.304498255252838135e-01 6.388004422187805176e-01 3.435140252113342285e-01 1.000000000000000000e+00 -2.267589420080184937e-01 6.340023279190063477e-01 3.403152525424957275e-01 1.000000000000000000e+00 -2.230680435895919800e-01 6.292041540145874023e-01 3.371164798736572266e-01 1.000000000000000000e+00 -2.193771600723266602e-01 6.244059801101684570e-01 3.339177370071411133e-01 1.000000000000000000e+00 -2.156862765550613403e-01 6.196078658103942871e-01 3.307189643383026123e-01 1.000000000000000000e+00 -2.119953930377960205e-01 6.148096919059753418e-01 3.275201916694641113e-01 1.000000000000000000e+00 -2.083044946193695068e-01 6.100115180015563965e-01 3.243214190006256104e-01 1.000000000000000000e+00 -2.046136111021041870e-01 6.052134037017822266e-01 3.211226463317871094e-01 1.000000000000000000e+00 -2.009227275848388672e-01 6.004152297973632812e-01 3.179238736629486084e-01 1.000000000000000000e+00 -1.972318291664123535e-01 5.956170558929443359e-01 3.147251009941101074e-01 1.000000000000000000e+00 -1.935409456491470337e-01 5.908189415931701660e-01 3.115263283252716064e-01 1.000000000000000000e+00 -1.898500621318817139e-01 5.860207676887512207e-01 3.083275556564331055e-01 1.000000000000000000e+00 -1.861591637134552002e-01 5.812225937843322754e-01 3.051287829875946045e-01 1.000000000000000000e+00 -1.824682801961898804e-01 5.764244794845581055e-01 3.019300401210784912e-01 1.000000000000000000e+00 -1.787773966789245605e-01 5.716263055801391602e-01 2.987312674522399902e-01 1.000000000000000000e+00 -1.750864982604980469e-01 5.668281316757202148e-01 2.955324947834014893e-01 1.000000000000000000e+00 -1.713956147432327271e-01 5.620300173759460449e-01 2.923337221145629883e-01 1.000000000000000000e+00 -1.677047312259674072e-01 5.572318434715270996e-01 2.891349494457244873e-01 1.000000000000000000e+00 -1.640138477087020874e-01 5.524336695671081543e-01 2.859361767768859863e-01 1.000000000000000000e+00 -1.603229492902755737e-01 5.476354956626892090e-01 2.827374041080474854e-01 1.000000000000000000e+00 -1.566320657730102539e-01 5.428373813629150391e-01 2.795386314392089844e-01 1.000000000000000000e+00 -1.529411822557449341e-01 5.380392074584960938e-01 2.763398587703704834e-01 1.000000000000000000e+00 -1.492502838373184204e-01 5.332410335540771484e-01 2.731410861015319824e-01 1.000000000000000000e+00 -1.455594003200531006e-01 5.284429192543029785e-01 2.699423432350158691e-01 1.000000000000000000e+00 -1.418685168027877808e-01 5.236447453498840332e-01 2.667435705661773682e-01 1.000000000000000000e+00 -1.381776183843612671e-01 5.188465714454650879e-01 2.635447978973388672e-01 1.000000000000000000e+00 -1.340253800153732300e-01 5.150634646415710449e-01 2.616378366947174072e-01 1.000000000000000000e+00 -1.297193318605422974e-01 5.116186141967773438e-01 2.601614892482757568e-01 1.000000000000000000e+00 -1.254132986068725586e-01 5.081737637519836426e-01 2.586851119995117188e-01 1.000000000000000000e+00 -1.211072653532028198e-01 5.047289729118347168e-01 2.572087645530700684e-01 1.000000000000000000e+00 -1.168012320995330811e-01 5.012841224670410156e-01 2.557324171066284180e-01 1.000000000000000000e+00 -1.124951913952827454e-01 4.978393018245697021e-01 2.542560696601867676e-01 1.000000000000000000e+00 -1.081891581416130066e-01 4.943944513797760010e-01 2.527796924114227295e-01 1.000000000000000000e+00 -1.038831248879432678e-01 4.909496307373046875e-01 2.513033449649810791e-01 1.000000000000000000e+00 -9.957708418369293213e-02 4.875048100948333740e-01 2.498269826173782349e-01 1.000000000000000000e+00 -9.527105093002319336e-02 4.840599894523620605e-01 2.483506351709365845e-01 1.000000000000000000e+00 -9.096501022577285767e-02 4.806151390075683594e-01 2.468742728233337402e-01 1.000000000000000000e+00 -8.665897697210311890e-02 4.771703183650970459e-01 2.453979253768920898e-01 1.000000000000000000e+00 -8.235294371843338013e-02 4.737254977226257324e-01 2.439215630292892456e-01 1.000000000000000000e+00 -7.804690301418304443e-02 4.702806472778320312e-01 2.424452155828475952e-01 1.000000000000000000e+00 -7.374086976051330566e-02 4.668358266353607178e-01 2.409688532352447510e-01 1.000000000000000000e+00 -6.943482905626296997e-02 4.633910059928894043e-01 2.394925057888031006e-01 1.000000000000000000e+00 -6.512879580259323120e-02 4.599461853504180908e-01 2.380161434412002563e-01 1.000000000000000000e+00 -6.082275882363319397e-02 4.565013349056243896e-01 2.365397959947586060e-01 1.000000000000000000e+00 -5.651672556996345520e-02 4.530565142631530762e-01 2.350634336471557617e-01 1.000000000000000000e+00 -5.221068859100341797e-02 4.496116936206817627e-01 2.335870862007141113e-01 1.000000000000000000e+00 -4.790465161204338074e-02 4.461668729782104492e-01 2.321107238531112671e-01 1.000000000000000000e+00 -4.359861463308334351e-02 4.427220225334167480e-01 2.306343764066696167e-01 1.000000000000000000e+00 -3.929258137941360474e-02 4.392772018909454346e-01 2.291580140590667725e-01 1.000000000000000000e+00 -3.498654440045356750e-02 4.358323812484741211e-01 2.276816666126251221e-01 1.000000000000000000e+00 -3.068050742149353027e-02 4.323875308036804199e-01 2.262053042650222778e-01 1.000000000000000000e+00 -2.637447044253349304e-02 4.289427101612091064e-01 2.247289568185806274e-01 1.000000000000000000e+00 -2.206843532621860504e-02 4.254978895187377930e-01 2.232525944709777832e-01 1.000000000000000000e+00 -1.776239834725856781e-02 4.220530688762664795e-01 2.217762470245361328e-01 1.000000000000000000e+00 -1.345636323094367981e-02 4.186082184314727783e-01 2.202998846769332886e-01 1.000000000000000000e+00 -9.150327183306217194e-03 4.151633977890014648e-01 2.188235223293304443e-01 1.000000000000000000e+00 -4.844290670007467270e-03 4.117185771465301514e-01 2.173471748828887939e-01 1.000000000000000000e+00 -5.382545059546828270e-04 4.082737267017364502e-01 2.158708125352859497e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.040753543376922607e-01 2.141791582107543945e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.997693061828613281e-01 2.124567478895187378e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.954632878303527832e-01 2.107343375682830811e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.911572396755218506e-01 2.090119123458862305e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.868512213230133057e-01 2.072895020246505737e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.825451731681823730e-01 2.055670917034149170e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.782391250133514404e-01 2.038446813821792603e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.739331066608428955e-01 2.021222561597824097e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.696270585060119629e-01 2.003998458385467529e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.653210401535034180e-01 1.986774355173110962e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.610149919986724854e-01 1.969550102949142456e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.567089438438415527e-01 1.952325999736785889e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.524029254913330078e-01 1.935101896524429321e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.480968773365020752e-01 1.917877793312072754e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.437908589839935303e-01 1.900653541088104248e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.394848108291625977e-01 1.883429437875747681e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.351787626743316650e-01 1.866205334663391113e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.308727443218231201e-01 1.848981231451034546e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.265666961669921875e-01 1.831756979227066040e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.222606778144836426e-01 1.814532876014709473e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.179546296596527100e-01 1.797308772802352905e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.136486113071441650e-01 1.780084520578384399e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.093425631523132324e-01 1.762860417366027832e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.050365149974822998e-01 1.745636314153671265e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.007304966449737549e-01 1.728412210941314697e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.964244484901428223e-01 1.711187958717346191e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.921184301376342773e-01 1.693963855504989624e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.878123819828033447e-01 1.676739752292633057e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.835063338279724121e-01 1.659515500068664551e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.792003154754638672e-01 1.642291396856307983e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.748942673206329346e-01 1.625067293643951416e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.705882489681243896e-01 1.607843190431594849e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/YlGnBu b/fastplotlib/utils/colormaps/YlGnBu deleted file mode 100644 index fc81887cc..000000000 --- a/fastplotlib/utils/colormaps/YlGnBu +++ /dev/null @@ -1,256 +0,0 @@ -1.000000000000000000e+00 1.000000000000000000e+00 8.509804010391235352e-01 1.000000000000000000e+00 -9.977854490280151367e-01 9.991387724876403809e-01 8.460592031478881836e-01 1.000000000000000000e+00 -9.955709576606750488e-01 9.982776045799255371e-01 8.411380052566528320e-01 1.000000000000000000e+00 -9.933564066886901855e-01 9.974163770675659180e-01 8.362168669700622559e-01 1.000000000000000000e+00 -9.911418557167053223e-01 9.965551495552062988e-01 8.312956690788269043e-01 1.000000000000000000e+00 -9.889273643493652344e-01 9.956939816474914551e-01 8.263744711875915527e-01 1.000000000000000000e+00 -9.867128133773803711e-01 9.948327541351318359e-01 8.214532732963562012e-01 1.000000000000000000e+00 -9.844982624053955078e-01 9.939715266227722168e-01 8.165320754051208496e-01 1.000000000000000000e+00 -9.822837114334106445e-01 9.931103587150573730e-01 8.116109371185302734e-01 1.000000000000000000e+00 -9.800692200660705566e-01 9.922491312026977539e-01 8.066897392272949219e-01 1.000000000000000000e+00 -9.778546690940856934e-01 9.913879036903381348e-01 8.017685413360595703e-01 1.000000000000000000e+00 -9.756401181221008301e-01 9.905267357826232910e-01 7.968473434448242188e-01 1.000000000000000000e+00 -9.734256267547607422e-01 9.896655082702636719e-01 7.919262051582336426e-01 1.000000000000000000e+00 -9.712110757827758789e-01 9.888042807579040527e-01 7.870050072669982910e-01 1.000000000000000000e+00 -9.689965248107910156e-01 9.879431128501892090e-01 7.820838093757629395e-01 1.000000000000000000e+00 -9.667820334434509277e-01 9.870818853378295898e-01 7.771626114845275879e-01 1.000000000000000000e+00 -9.645674824714660645e-01 9.862206578254699707e-01 7.722414731979370117e-01 1.000000000000000000e+00 -9.623529314994812012e-01 9.853594899177551270e-01 7.673202753067016602e-01 1.000000000000000000e+00 -9.601383805274963379e-01 9.844982624053955078e-01 7.623990774154663086e-01 1.000000000000000000e+00 -9.579238891601562500e-01 9.836370348930358887e-01 7.574778795242309570e-01 1.000000000000000000e+00 -9.557093381881713867e-01 9.827758669853210449e-01 7.525566816329956055e-01 1.000000000000000000e+00 -9.534947872161865234e-01 9.819146394729614258e-01 7.476355433464050293e-01 1.000000000000000000e+00 -9.512802958488464355e-01 9.810534119606018066e-01 7.427143454551696777e-01 1.000000000000000000e+00 -9.490657448768615723e-01 9.801922440528869629e-01 7.377931475639343262e-01 1.000000000000000000e+00 -9.468511939048767090e-01 9.793310165405273438e-01 7.328719496726989746e-01 1.000000000000000000e+00 -9.446367025375366211e-01 9.784698486328125000e-01 7.279508113861083984e-01 1.000000000000000000e+00 -9.424221515655517578e-01 9.776086211204528809e-01 7.230296134948730469e-01 1.000000000000000000e+00 -9.402076005935668945e-01 9.767473936080932617e-01 7.181084156036376953e-01 1.000000000000000000e+00 -9.379931092262268066e-01 9.758862257003784180e-01 7.131872177124023438e-01 1.000000000000000000e+00 -9.357785582542419434e-01 9.750249981880187988e-01 7.082660794258117676e-01 1.000000000000000000e+00 -9.335640072822570801e-01 9.741637706756591797e-01 7.033448815345764160e-01 1.000000000000000000e+00 -9.313494563102722168e-01 9.733026027679443359e-01 6.984236836433410645e-01 1.000000000000000000e+00 -9.288273453712463379e-01 9.723183512687683105e-01 6.941637992858886719e-01 1.000000000000000000e+00 -9.241522550582885742e-01 9.704728722572326660e-01 6.945328712463378906e-01 1.000000000000000000e+00 -9.194771051406860352e-01 9.686274528503417969e-01 6.949019432067871094e-01 1.000000000000000000e+00 -9.148020148277282715e-01 9.667820334434509277e-01 6.952710747718811035e-01 1.000000000000000000e+00 -9.101268649101257324e-01 9.649365544319152832e-01 6.956401467323303223e-01 1.000000000000000000e+00 -9.054517745971679688e-01 9.630911350250244141e-01 6.960092186927795410e-01 1.000000000000000000e+00 -9.007766246795654297e-01 9.612456560134887695e-01 6.963782906532287598e-01 1.000000000000000000e+00 -8.961014747619628906e-01 9.594002366065979004e-01 6.967474222183227539e-01 1.000000000000000000e+00 -8.914263844490051270e-01 9.575547575950622559e-01 6.971164941787719727e-01 1.000000000000000000e+00 -8.867512345314025879e-01 9.557093381881713867e-01 6.974855661392211914e-01 1.000000000000000000e+00 -8.820761442184448242e-01 9.538639187812805176e-01 6.978546977043151855e-01 1.000000000000000000e+00 -8.774009943008422852e-01 9.520184397697448730e-01 6.982237696647644043e-01 1.000000000000000000e+00 -8.727259039878845215e-01 9.501730203628540039e-01 6.985928416252136230e-01 1.000000000000000000e+00 -8.680507540702819824e-01 9.483275413513183594e-01 6.989619135856628418e-01 1.000000000000000000e+00 -8.633756041526794434e-01 9.464821219444274902e-01 6.993310451507568359e-01 1.000000000000000000e+00 -8.587005138397216797e-01 9.446367025375366211e-01 6.997001171112060547e-01 1.000000000000000000e+00 -8.540253639221191406e-01 9.427912235260009766e-01 7.000691890716552734e-01 1.000000000000000000e+00 -8.493502736091613770e-01 9.409458041191101074e-01 7.004383206367492676e-01 1.000000000000000000e+00 -8.446751236915588379e-01 9.391003251075744629e-01 7.008073925971984863e-01 1.000000000000000000e+00 -8.399999737739562988e-01 9.372549057006835938e-01 7.011764645576477051e-01 1.000000000000000000e+00 -8.353248834609985352e-01 9.354094862937927246e-01 7.015455365180969238e-01 1.000000000000000000e+00 -8.306497335433959961e-01 9.335640072822570801e-01 7.019146680831909180e-01 1.000000000000000000e+00 -8.259746432304382324e-01 9.317185878753662109e-01 7.022837400436401367e-01 1.000000000000000000e+00 -8.212994933128356934e-01 9.298731088638305664e-01 7.026528120040893555e-01 1.000000000000000000e+00 -8.166244029998779297e-01 9.280276894569396973e-01 7.030219435691833496e-01 1.000000000000000000e+00 -8.119492530822753906e-01 9.261822104454040527e-01 7.033910155296325684e-01 1.000000000000000000e+00 -8.072741031646728516e-01 9.243367910385131836e-01 7.037600874900817871e-01 1.000000000000000000e+00 -8.025990128517150879e-01 9.224913716316223145e-01 7.041291594505310059e-01 1.000000000000000000e+00 -7.979238629341125488e-01 9.206458926200866699e-01 7.044982910156250000e-01 1.000000000000000000e+00 -7.932487726211547852e-01 9.188004732131958008e-01 7.048673629760742188e-01 1.000000000000000000e+00 -7.885736227035522461e-01 9.169549942016601562e-01 7.052364349365234375e-01 1.000000000000000000e+00 -7.838984727859497070e-01 9.151095747947692871e-01 7.056055068969726562e-01 1.000000000000000000e+00 -7.781776189804077148e-01 9.128642678260803223e-01 7.060976624488830566e-01 1.000000000000000000e+00 -7.693194746971130371e-01 9.094194769859313965e-01 7.069588899612426758e-01 1.000000000000000000e+00 -7.604613900184631348e-01 9.059746265411376953e-01 7.078200578689575195e-01 1.000000000000000000e+00 -7.516032457351684570e-01 9.025297760963439941e-01 7.086812853813171387e-01 1.000000000000000000e+00 -7.427451014518737793e-01 8.990849852561950684e-01 7.095425128936767578e-01 1.000000000000000000e+00 -7.338869571685791016e-01 8.956401348114013672e-01 7.104036808013916016e-01 1.000000000000000000e+00 -7.250288128852844238e-01 8.921952843666076660e-01 7.112649083137512207e-01 1.000000000000000000e+00 -7.161707282066345215e-01 8.887504935264587402e-01 7.121260762214660645e-01 1.000000000000000000e+00 -7.073125839233398438e-01 8.853056430816650391e-01 7.129873037338256836e-01 1.000000000000000000e+00 -6.984544396400451660e-01 8.818608522415161133e-01 7.138485312461853027e-01 1.000000000000000000e+00 -6.895962953567504883e-01 8.784160017967224121e-01 7.147096991539001465e-01 1.000000000000000000e+00 -6.807381510734558105e-01 8.749711513519287109e-01 7.155709266662597656e-01 1.000000000000000000e+00 -6.718800663948059082e-01 8.715263605117797852e-01 7.164321541786193848e-01 1.000000000000000000e+00 -6.630219221115112305e-01 8.680815100669860840e-01 7.172933220863342285e-01 1.000000000000000000e+00 -6.541637778282165527e-01 8.646366596221923828e-01 7.181545495986938477e-01 1.000000000000000000e+00 -6.453056335449218750e-01 8.611918687820434570e-01 7.190157771110534668e-01 1.000000000000000000e+00 -6.364475488662719727e-01 8.577470183372497559e-01 7.198769450187683105e-01 1.000000000000000000e+00 -6.275894045829772949e-01 8.543021678924560547e-01 7.207381725311279297e-01 1.000000000000000000e+00 -6.187312602996826172e-01 8.508573770523071289e-01 7.215994000434875488e-01 1.000000000000000000e+00 -6.098731160163879395e-01 8.474125266075134277e-01 7.224605679512023926e-01 1.000000000000000000e+00 -6.010149717330932617e-01 8.439676761627197266e-01 7.233217954635620117e-01 1.000000000000000000e+00 -5.921568870544433594e-01 8.405228853225708008e-01 7.241830229759216309e-01 1.000000000000000000e+00 -5.832987427711486816e-01 8.370780348777770996e-01 7.250441908836364746e-01 1.000000000000000000e+00 -5.744405984878540039e-01 8.336332440376281738e-01 7.259054183959960938e-01 1.000000000000000000e+00 -5.655824542045593262e-01 8.301883935928344727e-01 7.267666459083557129e-01 1.000000000000000000e+00 -5.567243099212646484e-01 8.267435431480407715e-01 7.276278138160705566e-01 1.000000000000000000e+00 -5.478662252426147461e-01 8.232987523078918457e-01 7.284890413284301758e-01 1.000000000000000000e+00 -5.390080809593200684e-01 8.198539018630981445e-01 7.293502688407897949e-01 1.000000000000000000e+00 -5.301499366760253906e-01 8.164090514183044434e-01 7.302114367485046387e-01 1.000000000000000000e+00 -5.212917923927307129e-01 8.129642605781555176e-01 7.310726642608642578e-01 1.000000000000000000e+00 -5.124337077140808105e-01 8.095194101333618164e-01 7.319338917732238770e-01 1.000000000000000000e+00 -5.035755634307861328e-01 8.060745596885681152e-01 7.327950596809387207e-01 1.000000000000000000e+00 -4.951787889003753662e-01 8.028604388236999512e-01 7.337485551834106445e-01 1.000000000000000000e+00 -4.875509440898895264e-01 8.000307679176330566e-01 7.348558306694030762e-01 1.000000000000000000e+00 -4.799230992794036865e-01 7.972010970115661621e-01 7.359631061553955078e-01 1.000000000000000000e+00 -4.722952842712402344e-01 7.943713665008544922e-01 7.370703816413879395e-01 1.000000000000000000e+00 -4.646674394607543945e-01 7.915416955947875977e-01 7.381775975227355957e-01 1.000000000000000000e+00 -4.570395946502685547e-01 7.887120246887207031e-01 7.392848730087280273e-01 1.000000000000000000e+00 -4.494117498397827148e-01 7.858823537826538086e-01 7.403921484947204590e-01 1.000000000000000000e+00 -4.417839348316192627e-01 7.830526828765869141e-01 7.414994239807128906e-01 1.000000000000000000e+00 -4.341560900211334229e-01 7.802230119705200195e-01 7.426066994667053223e-01 1.000000000000000000e+00 -4.265282452106475830e-01 7.773932814598083496e-01 7.437139749526977539e-01 1.000000000000000000e+00 -4.189004302024841309e-01 7.745636105537414551e-01 7.448212504386901855e-01 1.000000000000000000e+00 -4.112725853919982910e-01 7.717339396476745605e-01 7.459284663200378418e-01 1.000000000000000000e+00 -4.036447405815124512e-01 7.689042687416076660e-01 7.470357418060302734e-01 1.000000000000000000e+00 -3.960169255733489990e-01 7.660745978355407715e-01 7.481430172920227051e-01 1.000000000000000000e+00 -3.883890807628631592e-01 7.632449269294738770e-01 7.492502927780151367e-01 1.000000000000000000e+00 -3.807612359523773193e-01 7.604151964187622070e-01 7.503575682640075684e-01 1.000000000000000000e+00 -3.731334209442138672e-01 7.575855255126953125e-01 7.514648437500000000e-01 1.000000000000000000e+00 -3.655055761337280273e-01 7.547558546066284180e-01 7.525720596313476562e-01 1.000000000000000000e+00 -3.578777313232421875e-01 7.519261837005615234e-01 7.536793351173400879e-01 1.000000000000000000e+00 -3.502499163150787354e-01 7.490965127944946289e-01 7.547866106033325195e-01 1.000000000000000000e+00 -3.426220715045928955e-01 7.462668418884277344e-01 7.558938860893249512e-01 1.000000000000000000e+00 -3.349942266941070557e-01 7.434371113777160645e-01 7.570011615753173828e-01 1.000000000000000000e+00 -3.273664116859436035e-01 7.406074404716491699e-01 7.581084370613098145e-01 1.000000000000000000e+00 -3.197385668754577637e-01 7.377777695655822754e-01 7.592157125473022461e-01 1.000000000000000000e+00 -3.121107220649719238e-01 7.349480986595153809e-01 7.603229284286499023e-01 1.000000000000000000e+00 -3.044828772544860840e-01 7.321184277534484863e-01 7.614302039146423340e-01 1.000000000000000000e+00 -2.968550622463226318e-01 7.292887568473815918e-01 7.625374794006347656e-01 1.000000000000000000e+00 -2.892272174358367920e-01 7.264590263366699219e-01 7.636447548866271973e-01 1.000000000000000000e+00 -2.815993726253509521e-01 7.236293554306030273e-01 7.647520303726196289e-01 1.000000000000000000e+00 -2.739715576171875000e-01 7.207996845245361328e-01 7.658593058586120605e-01 1.000000000000000000e+00 -2.663437128067016602e-01 7.179700136184692383e-01 7.669665217399597168e-01 1.000000000000000000e+00 -2.587158679962158203e-01 7.151403427124023438e-01 7.680737972259521484e-01 1.000000000000000000e+00 -2.526874244213104248e-01 7.114494442939758301e-01 7.683814167976379395e-01 1.000000000000000000e+00 -2.482583671808242798e-01 7.068973183631896973e-01 7.678892612457275391e-01 1.000000000000000000e+00 -2.438292950391769409e-01 7.023452520370483398e-01 7.673971652984619141e-01 1.000000000000000000e+00 -2.394002377986907959e-01 6.977931857109069824e-01 7.669050097465515137e-01 1.000000000000000000e+00 -2.349711656570434570e-01 6.932410597801208496e-01 7.664129137992858887e-01 1.000000000000000000e+00 -2.305420935153961182e-01 6.886889934539794922e-01 7.659208178520202637e-01 1.000000000000000000e+00 -2.261130362749099731e-01 6.841368675231933594e-01 7.654286623001098633e-01 1.000000000000000000e+00 -2.216839641332626343e-01 6.795848011970520020e-01 7.649365663528442383e-01 1.000000000000000000e+00 -2.172549068927764893e-01 6.750326752662658691e-01 7.644444704055786133e-01 1.000000000000000000e+00 -2.128258347511291504e-01 6.704806089401245117e-01 7.639523148536682129e-01 1.000000000000000000e+00 -2.083967775106430054e-01 6.659284830093383789e-01 7.634602189064025879e-01 1.000000000000000000e+00 -2.039677053689956665e-01 6.613764166831970215e-01 7.629680633544921875e-01 1.000000000000000000e+00 -1.995386332273483276e-01 6.568242907524108887e-01 7.624759674072265625e-01 1.000000000000000000e+00 -1.951095759868621826e-01 6.522722244262695312e-01 7.619838714599609375e-01 1.000000000000000000e+00 -1.906805038452148438e-01 6.477200984954833984e-01 7.614917159080505371e-01 1.000000000000000000e+00 -1.862514466047286987e-01 6.431680321693420410e-01 7.609996199607849121e-01 1.000000000000000000e+00 -1.818223744630813599e-01 6.386159062385559082e-01 7.605075240135192871e-01 1.000000000000000000e+00 -1.773933172225952148e-01 6.340638399124145508e-01 7.600153684616088867e-01 1.000000000000000000e+00 -1.729642450809478760e-01 6.295117139816284180e-01 7.595232725143432617e-01 1.000000000000000000e+00 -1.685351729393005371e-01 6.249596476554870605e-01 7.590311169624328613e-01 1.000000000000000000e+00 -1.641061156988143921e-01 6.204075217247009277e-01 7.585390210151672363e-01 1.000000000000000000e+00 -1.596770435571670532e-01 6.158554553985595703e-01 7.580469250679016113e-01 1.000000000000000000e+00 -1.552479863166809082e-01 6.113033294677734375e-01 7.575547695159912109e-01 1.000000000000000000e+00 -1.508189141750335693e-01 6.067512631416320801e-01 7.570626735687255859e-01 1.000000000000000000e+00 -1.463898569345474243e-01 6.021991372108459473e-01 7.565705776214599609e-01 1.000000000000000000e+00 -1.419607847929000854e-01 5.976470708847045898e-01 7.560784220695495605e-01 1.000000000000000000e+00 -1.375317126512527466e-01 5.930949449539184570e-01 7.555863261222839355e-01 1.000000000000000000e+00 -1.331026554107666016e-01 5.885428786277770996e-01 7.550941705703735352e-01 1.000000000000000000e+00 -1.286735832691192627e-01 5.839907526969909668e-01 7.546020746231079102e-01 1.000000000000000000e+00 -1.242445185780525208e-01 5.794386863708496094e-01 7.541099786758422852e-01 1.000000000000000000e+00 -1.198154538869857788e-01 5.748865604400634766e-01 7.536178231239318848e-01 1.000000000000000000e+00 -1.153863891959190369e-01 5.703344941139221191e-01 7.531257271766662598e-01 1.000000000000000000e+00 -1.141099557280540466e-01 5.647059082984924316e-01 7.510957121849060059e-01 1.000000000000000000e+00 -1.147251054644584656e-01 5.584313869476318359e-01 7.481430172920227051e-01 1.000000000000000000e+00 -1.153402552008628845e-01 5.521568655967712402e-01 7.451903223991394043e-01 1.000000000000000000e+00 -1.159554049372673035e-01 5.458823442459106445e-01 7.422376275062561035e-01 1.000000000000000000e+00 -1.165705472230911255e-01 5.396078228950500488e-01 7.392848730087280273e-01 1.000000000000000000e+00 -1.171856969594955444e-01 5.333333611488342285e-01 7.363321781158447266e-01 1.000000000000000000e+00 -1.178008466958999634e-01 5.270588397979736328e-01 7.333794832229614258e-01 1.000000000000000000e+00 -1.184159964323043823e-01 5.207843184471130371e-01 7.304267883300781250e-01 1.000000000000000000e+00 -1.190311387181282043e-01 5.145097970962524414e-01 7.274740338325500488e-01 1.000000000000000000e+00 -1.196462884545326233e-01 5.082352757453918457e-01 7.245213389396667480e-01 1.000000000000000000e+00 -1.202614381909370422e-01 5.019608139991760254e-01 7.215686440467834473e-01 1.000000000000000000e+00 -1.208765879273414612e-01 4.956862628459930420e-01 7.186158895492553711e-01 1.000000000000000000e+00 -1.214917376637458801e-01 4.894117712974548340e-01 7.156631946563720703e-01 1.000000000000000000e+00 -1.221068799495697021e-01 4.831372499465942383e-01 7.127104997634887695e-01 1.000000000000000000e+00 -1.227220296859741211e-01 4.768627583980560303e-01 7.097578048706054688e-01 1.000000000000000000e+00 -1.233371794223785400e-01 4.705882370471954346e-01 7.068050503730773926e-01 1.000000000000000000e+00 -1.239523291587829590e-01 4.643137156963348389e-01 7.038523554801940918e-01 1.000000000000000000e+00 -1.245674714446067810e-01 4.580392241477966309e-01 7.008996605873107910e-01 1.000000000000000000e+00 -1.251826286315917969e-01 4.517647027969360352e-01 6.979469656944274902e-01 1.000000000000000000e+00 -1.257977634668350220e-01 4.454901814460754395e-01 6.949942111968994141e-01 1.000000000000000000e+00 -1.264129132032394409e-01 4.392156898975372314e-01 6.920415163040161133e-01 1.000000000000000000e+00 -1.270280629396438599e-01 4.329411685466766357e-01 6.890888214111328125e-01 1.000000000000000000e+00 -1.276432126760482788e-01 4.266666769981384277e-01 6.861361265182495117e-01 1.000000000000000000e+00 -1.282583624124526978e-01 4.203921556472778320e-01 6.831833720207214355e-01 1.000000000000000000e+00 -1.288735121488571167e-01 4.141176342964172363e-01 6.802306771278381348e-01 1.000000000000000000e+00 -1.294886618852615356e-01 4.078431427478790283e-01 6.772779822349548340e-01 1.000000000000000000e+00 -1.301038116216659546e-01 4.015686213970184326e-01 6.743252873420715332e-01 1.000000000000000000e+00 -1.307189613580703735e-01 3.952941298484802246e-01 6.713725328445434570e-01 1.000000000000000000e+00 -1.313340961933135986e-01 3.890196084976196289e-01 6.684198379516601562e-01 1.000000000000000000e+00 -1.319492459297180176e-01 3.827450871467590332e-01 6.654671430587768555e-01 1.000000000000000000e+00 -1.325643956661224365e-01 3.764705955982208252e-01 6.625143885612487793e-01 1.000000000000000000e+00 -1.331795454025268555e-01 3.701960742473602295e-01 6.595616936683654785e-01 1.000000000000000000e+00 -1.336101442575454712e-01 3.647520244121551514e-01 6.569780707359313965e-01 1.000000000000000000e+00 -1.339792460203170776e-01 3.595847785472869873e-01 6.545174717903137207e-01 1.000000000000000000e+00 -1.343483328819274902e-01 3.544175326824188232e-01 6.520568728446960449e-01 1.000000000000000000e+00 -1.347174197435379028e-01 3.492502868175506592e-01 6.495963335037231445e-01 1.000000000000000000e+00 -1.350865066051483154e-01 3.440830409526824951e-01 6.471357345581054688e-01 1.000000000000000000e+00 -1.354555934667587280e-01 3.389157950878143311e-01 6.446751356124877930e-01 1.000000000000000000e+00 -1.358246803283691406e-01 3.337485492229461670e-01 6.422145366668701172e-01 1.000000000000000000e+00 -1.361937671899795532e-01 3.285813033580780029e-01 6.397539377212524414e-01 1.000000000000000000e+00 -1.365628540515899658e-01 3.234140574932098389e-01 6.372933387756347656e-01 1.000000000000000000e+00 -1.369319558143615723e-01 3.182468414306640625e-01 6.348327398300170898e-01 1.000000000000000000e+00 -1.373010426759719849e-01 3.130795955657958984e-01 6.323721408843994141e-01 1.000000000000000000e+00 -1.376701295375823975e-01 3.079123497009277344e-01 6.299116015434265137e-01 1.000000000000000000e+00 -1.380392163991928101e-01 3.027451038360595703e-01 6.274510025978088379e-01 1.000000000000000000e+00 -1.384083032608032227e-01 2.975778579711914062e-01 6.249904036521911621e-01 1.000000000000000000e+00 -1.387773901224136353e-01 2.924106121063232422e-01 6.225298047065734863e-01 1.000000000000000000e+00 -1.391464769840240479e-01 2.872433662414550781e-01 6.200692057609558105e-01 1.000000000000000000e+00 -1.395155638456344604e-01 2.820761203765869141e-01 6.176086068153381348e-01 1.000000000000000000e+00 -1.398846656084060669e-01 2.769088745117187500e-01 6.151480078697204590e-01 1.000000000000000000e+00 -1.402537524700164795e-01 2.717416286468505859e-01 6.126874089241027832e-01 1.000000000000000000e+00 -1.406228393316268921e-01 2.665743827819824219e-01 6.102268099784851074e-01 1.000000000000000000e+00 -1.409919261932373047e-01 2.614071369171142578e-01 6.077662706375122070e-01 1.000000000000000000e+00 -1.413610130548477173e-01 2.562399208545684814e-01 6.053056716918945312e-01 1.000000000000000000e+00 -1.417300999164581299e-01 2.510726749897003174e-01 6.028450727462768555e-01 1.000000000000000000e+00 -1.420991867780685425e-01 2.459054142236709595e-01 6.003844738006591797e-01 1.000000000000000000e+00 -1.424682885408401489e-01 2.407381832599639893e-01 5.979238748550415039e-01 1.000000000000000000e+00 -1.428373754024505615e-01 2.355709373950958252e-01 5.954632759094238281e-01 1.000000000000000000e+00 -1.432064622640609741e-01 2.304036915302276611e-01 5.930026769638061523e-01 1.000000000000000000e+00 -1.435755491256713867e-01 2.252364456653594971e-01 5.905420780181884766e-01 1.000000000000000000e+00 -1.439446359872817993e-01 2.200691998004913330e-01 5.880814790725708008e-01 1.000000000000000000e+00 -1.443137228488922119e-01 2.149019539356231689e-01 5.856209397315979004e-01 1.000000000000000000e+00 -1.446828097105026245e-01 2.097347229719161987e-01 5.831603407859802246e-01 1.000000000000000000e+00 -1.450518965721130371e-01 2.045674771070480347e-01 5.806997418403625488e-01 1.000000000000000000e+00 -1.419761627912521362e-01 2.014455944299697876e-01 5.739331245422363281e-01 1.000000000000000000e+00 -1.384083032608032227e-01 1.986159235239028931e-01 5.665513277053833008e-01 1.000000000000000000e+00 -1.348404437303543091e-01 1.957862377166748047e-01 5.591695308685302734e-01 1.000000000000000000e+00 -1.312725841999053955e-01 1.929565519094467163e-01 5.517877936363220215e-01 1.000000000000000000e+00 -1.277047246694564819e-01 1.901268810033798218e-01 5.444059967994689941e-01 1.000000000000000000e+00 -1.241368725895881653e-01 1.872971951961517334e-01 5.370241999626159668e-01 1.000000000000000000e+00 -1.205690130591392517e-01 1.844675093889236450e-01 5.296424627304077148e-01 1.000000000000000000e+00 -1.170011535286903381e-01 1.816378384828567505e-01 5.222606658935546875e-01 1.000000000000000000e+00 -1.134332939982414246e-01 1.788081526756286621e-01 5.148788690567016602e-01 1.000000000000000000e+00 -1.098654344677925110e-01 1.759784668684005737e-01 5.074971318244934082e-01 1.000000000000000000e+00 -1.062975749373435974e-01 1.731487959623336792e-01 5.001153349876403809e-01 1.000000000000000000e+00 -1.027297228574752808e-01 1.703191101551055908e-01 4.927335679531097412e-01 1.000000000000000000e+00 -9.916186332702636719e-02 1.674894243478775024e-01 4.853518009185791016e-01 1.000000000000000000e+00 -9.559400379657745361e-02 1.646597534418106079e-01 4.779700040817260742e-01 1.000000000000000000e+00 -9.202614426612854004e-02 1.618300676345825195e-01 4.705882370471954346e-01 1.000000000000000000e+00 -8.845828473567962646e-02 1.590003818273544312e-01 4.632064700126647949e-01 1.000000000000000000e+00 -8.489042520523071289e-02 1.561707109212875366e-01 4.558246731758117676e-01 1.000000000000000000e+00 -8.132256567478179932e-02 1.533410251140594482e-01 4.484429061412811279e-01 1.000000000000000000e+00 -7.775470614433288574e-02 1.505113393068313599e-01 4.410611391067504883e-01 1.000000000000000000e+00 -7.418685406446456909e-02 1.476816534996032715e-01 4.336793422698974609e-01 1.000000000000000000e+00 -7.061899453401565552e-02 1.448519825935363770e-01 4.262975752353668213e-01 1.000000000000000000e+00 -6.705113500356674194e-02 1.420222967863082886e-01 4.189158082008361816e-01 1.000000000000000000e+00 -6.348327547311782837e-02 1.391926109790802002e-01 4.115340113639831543e-01 1.000000000000000000e+00 -5.991541594266891479e-02 1.363629400730133057e-01 4.041522443294525146e-01 1.000000000000000000e+00 -5.634756013751029968e-02 1.335332542657852173e-01 3.967704772949218750e-01 1.000000000000000000e+00 -5.277970060706138611e-02 1.307035684585571289e-01 3.893887102603912354e-01 1.000000000000000000e+00 -4.921184107661247253e-02 1.278738975524902344e-01 3.820069134235382080e-01 1.000000000000000000e+00 -4.564398154616355896e-02 1.250442117452621460e-01 3.746251463890075684e-01 1.000000000000000000e+00 -4.207612574100494385e-02 1.222145333886146545e-01 3.672433793544769287e-01 1.000000000000000000e+00 -3.850826621055603027e-02 1.193848550319671631e-01 3.598615825176239014e-01 1.000000000000000000e+00 -3.494040668010711670e-02 1.165551692247390747e-01 3.524798154830932617e-01 1.000000000000000000e+00 -3.137255087494850159e-02 1.137254908680915833e-01 3.450980484485626221e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/YlOrBr b/fastplotlib/utils/colormaps/YlOrBr deleted file mode 100644 index f23bd5af1..000000000 --- a/fastplotlib/utils/colormaps/YlOrBr +++ /dev/null @@ -1,256 +0,0 @@ -1.000000000000000000e+00 1.000000000000000000e+00 8.980392217636108398e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.990157485008239746e-01 8.929949998855590820e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.980314970016479492e-01 8.879507780075073242e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.970473051071166992e-01 8.829065561294555664e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.960630536079406738e-01 8.778623342514038086e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.950788021087646484e-01 8.728181719779968262e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.940945506095886230e-01 8.677739500999450684e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.931103587150573730e-01 8.627297282218933105e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.921261072158813477e-01 8.576855063438415527e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.911418557167053223e-01 8.526412844657897949e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.901576042175292969e-01 8.475970625877380371e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.891734123229980469e-01 8.425528407096862793e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.881891608238220215e-01 8.375086784362792969e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.872049093246459961e-01 8.324644565582275391e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.862206578254699707e-01 8.274202346801757812e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.852364659309387207e-01 8.223760128021240234e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.842522144317626953e-01 8.173317909240722656e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.832679629325866699e-01 8.122875690460205078e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.822837114334106445e-01 8.072433471679687500e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.812995195388793945e-01 8.021991252899169922e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.803152680397033691e-01 7.971549630165100098e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.793310165405273438e-01 7.921107411384582520e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.783467650413513184e-01 7.870665192604064941e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.773625731468200684e-01 7.820222973823547363e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.763783216476440430e-01 7.769780755043029785e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.753940701484680176e-01 7.719338536262512207e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.744098186492919922e-01 7.668896317481994629e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.734256267547607422e-01 7.618454694747924805e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.724413752555847168e-01 7.568012475967407227e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.714571237564086914e-01 7.517570257186889648e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.704728722572326660e-01 7.467128038406372070e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.694886803627014160e-01 7.416685819625854492e-01 1.000000000000000000e+00 -9.999846220016479492e-01 9.683198928833007812e-01 7.365936040878295898e-01 1.000000000000000000e+00 -9.998615980148315430e-01 9.658592939376831055e-01 7.313033342361450195e-01 1.000000000000000000e+00 -9.997385740280151367e-01 9.633986949920654297e-01 7.260130643844604492e-01 1.000000000000000000e+00 -9.996155500411987305e-01 9.609380960464477539e-01 7.207227945327758789e-01 1.000000000000000000e+00 -9.994925260543823242e-01 9.584774971008300781e-01 7.154325246810913086e-01 1.000000000000000000e+00 -9.993695020675659180e-01 9.560168981552124023e-01 7.101422548294067383e-01 1.000000000000000000e+00 -9.992464184761047363e-01 9.535562992095947266e-01 7.048519849777221680e-01 1.000000000000000000e+00 -9.991233944892883301e-01 9.510957598686218262e-01 6.995617151260375977e-01 1.000000000000000000e+00 -9.990003705024719238e-01 9.486351609230041504e-01 6.942714452743530273e-01 1.000000000000000000e+00 -9.988773465156555176e-01 9.461745619773864746e-01 6.889811754226684570e-01 1.000000000000000000e+00 -9.987543225288391113e-01 9.437139630317687988e-01 6.836909055709838867e-01 1.000000000000000000e+00 -9.986312985420227051e-01 9.412533640861511230e-01 6.784006357192993164e-01 1.000000000000000000e+00 -9.985082745552062988e-01 9.387927651405334473e-01 6.731103658676147461e-01 1.000000000000000000e+00 -9.983852505683898926e-01 9.363321661949157715e-01 6.678200960159301758e-01 1.000000000000000000e+00 -9.982622265815734863e-01 9.338715672492980957e-01 6.625297665596008301e-01 1.000000000000000000e+00 -9.981392025947570801e-01 9.314109683036804199e-01 6.572394967079162598e-01 1.000000000000000000e+00 -9.980161190032958984e-01 9.289504289627075195e-01 6.519492268562316895e-01 1.000000000000000000e+00 -9.978930950164794922e-01 9.264898300170898438e-01 6.466589570045471191e-01 1.000000000000000000e+00 -9.977700710296630859e-01 9.240292310714721680e-01 6.413686871528625488e-01 1.000000000000000000e+00 -9.976470470428466797e-01 9.215686321258544922e-01 6.360784173011779785e-01 1.000000000000000000e+00 -9.975240230560302734e-01 9.191080331802368164e-01 6.307881474494934082e-01 1.000000000000000000e+00 -9.974009990692138672e-01 9.166474342346191406e-01 6.254978775978088379e-01 1.000000000000000000e+00 -9.972779750823974609e-01 9.141868352890014648e-01 6.202076077461242676e-01 1.000000000000000000e+00 -9.971549510955810547e-01 9.117262363433837891e-01 6.149173378944396973e-01 1.000000000000000000e+00 -9.970319271087646484e-01 9.092656373977661133e-01 6.096270680427551270e-01 1.000000000000000000e+00 -9.969089031219482422e-01 9.068050980567932129e-01 6.043367981910705566e-01 1.000000000000000000e+00 -9.967858791351318359e-01 9.043444991111755371e-01 5.990465283393859863e-01 1.000000000000000000e+00 -9.966627955436706543e-01 9.018839001655578613e-01 5.937562584877014160e-01 1.000000000000000000e+00 -9.965397715568542480e-01 8.994233012199401855e-01 5.884659886360168457e-01 1.000000000000000000e+00 -9.964167475700378418e-01 8.969627022743225098e-01 5.831757187843322754e-01 1.000000000000000000e+00 -9.962937235832214355e-01 8.945021033287048340e-01 5.778854489326477051e-01 1.000000000000000000e+00 -9.961706995964050293e-01 8.920415043830871582e-01 5.725951790809631348e-01 1.000000000000000000e+00 -9.960784316062927246e-01 8.892425894737243652e-01 5.665974617004394531e-01 1.000000000000000000e+00 -9.960784316062927246e-01 8.854286670684814453e-01 5.584775209426879883e-01 1.000000000000000000e+00 -9.960784316062927246e-01 8.816147446632385254e-01 5.503575801849365234e-01 1.000000000000000000e+00 -9.960784316062927246e-01 8.778008222579956055e-01 5.422375798225402832e-01 1.000000000000000000e+00 -9.960784316062927246e-01 8.739868998527526855e-01 5.341176390647888184e-01 1.000000000000000000e+00 -9.960784316062927246e-01 8.701730370521545410e-01 5.259976983070373535e-01 1.000000000000000000e+00 -9.960784316062927246e-01 8.663591146469116211e-01 5.178777575492858887e-01 1.000000000000000000e+00 -9.960784316062927246e-01 8.625451922416687012e-01 5.097577571868896484e-01 1.000000000000000000e+00 -9.960784316062927246e-01 8.587312698364257812e-01 5.016378164291381836e-01 1.000000000000000000e+00 -9.960784316062927246e-01 8.549173474311828613e-01 4.935178756713867188e-01 1.000000000000000000e+00 -9.960784316062927246e-01 8.511034250259399414e-01 4.853979349136352539e-01 1.000000000000000000e+00 -9.960784316062927246e-01 8.472895026206970215e-01 4.772779643535614014e-01 1.000000000000000000e+00 -9.960784316062927246e-01 8.434755802154541016e-01 4.691580235958099365e-01 1.000000000000000000e+00 -9.960784316062927246e-01 8.396616578102111816e-01 4.610380530357360840e-01 1.000000000000000000e+00 -9.960784316062927246e-01 8.358477354049682617e-01 4.529181122779846191e-01 1.000000000000000000e+00 -9.960784316062927246e-01 8.320338129997253418e-01 4.447981417179107666e-01 1.000000000000000000e+00 -9.960784316062927246e-01 8.282198905944824219e-01 4.366782009601593018e-01 1.000000000000000000e+00 -9.960784316062927246e-01 8.244059681892395020e-01 4.285582602024078369e-01 1.000000000000000000e+00 -9.960784316062927246e-01 8.205921053886413574e-01 4.204382896423339844e-01 1.000000000000000000e+00 -9.960784316062927246e-01 8.167781829833984375e-01 4.123183488845825195e-01 1.000000000000000000e+00 -9.960784316062927246e-01 8.129642605781555176e-01 4.041983783245086670e-01 1.000000000000000000e+00 -9.960784316062927246e-01 8.091503381729125977e-01 3.960784375667572021e-01 1.000000000000000000e+00 -9.960784316062927246e-01 8.053364157676696777e-01 3.879584670066833496e-01 1.000000000000000000e+00 -9.960784316062927246e-01 8.015224933624267578e-01 3.798385262489318848e-01 1.000000000000000000e+00 -9.960784316062927246e-01 7.977085709571838379e-01 3.717185556888580322e-01 1.000000000000000000e+00 -9.960784316062927246e-01 7.938946485519409180e-01 3.635986149311065674e-01 1.000000000000000000e+00 -9.960784316062927246e-01 7.900807261466979980e-01 3.554786741733551025e-01 1.000000000000000000e+00 -9.960784316062927246e-01 7.862668037414550781e-01 3.473587036132812500e-01 1.000000000000000000e+00 -9.960784316062927246e-01 7.824528813362121582e-01 3.392387628555297852e-01 1.000000000000000000e+00 -9.960784316062927246e-01 7.786389589309692383e-01 3.311187922954559326e-01 1.000000000000000000e+00 -9.960784316062927246e-01 7.748250961303710938e-01 3.229988515377044678e-01 1.000000000000000000e+00 -9.960784316062927246e-01 7.710111737251281738e-01 3.148788809776306152e-01 1.000000000000000000e+00 -9.960784316062927246e-01 7.666435837745666504e-01 3.080507516860961914e-01 1.000000000000000000e+00 -9.960784316062927246e-01 7.613533139228820801e-01 3.033756315708160400e-01 1.000000000000000000e+00 -9.960784316062927246e-01 7.560630440711975098e-01 2.987005114555358887e-01 1.000000000000000000e+00 -9.960784316062927246e-01 7.507727742195129395e-01 2.940253615379333496e-01 1.000000000000000000e+00 -9.960784316062927246e-01 7.454825043678283691e-01 2.893502414226531982e-01 1.000000000000000000e+00 -9.960784316062927246e-01 7.401922345161437988e-01 2.846751213073730469e-01 1.000000000000000000e+00 -9.960784316062927246e-01 7.349019646644592285e-01 2.800000011920928955e-01 1.000000000000000000e+00 -9.960784316062927246e-01 7.296116948127746582e-01 2.753248810768127441e-01 1.000000000000000000e+00 -9.960784316062927246e-01 7.243214249610900879e-01 2.706497609615325928e-01 1.000000000000000000e+00 -9.960784316062927246e-01 7.190311551094055176e-01 2.659746110439300537e-01 1.000000000000000000e+00 -9.960784316062927246e-01 7.137408852577209473e-01 2.612994909286499023e-01 1.000000000000000000e+00 -9.960784316062927246e-01 7.084506154060363770e-01 2.566243708133697510e-01 1.000000000000000000e+00 -9.960784316062927246e-01 7.031603455543518066e-01 2.519492506980895996e-01 1.000000000000000000e+00 -9.960784316062927246e-01 6.978700757026672363e-01 2.472741305828094482e-01 1.000000000000000000e+00 -9.960784316062927246e-01 6.925798058509826660e-01 2.425989955663681030e-01 1.000000000000000000e+00 -9.960784316062927246e-01 6.872894763946533203e-01 2.379238754510879517e-01 1.000000000000000000e+00 -9.960784316062927246e-01 6.819992065429687500e-01 2.332487553358078003e-01 1.000000000000000000e+00 -9.960784316062927246e-01 6.767089366912841797e-01 2.285736203193664551e-01 1.000000000000000000e+00 -9.960784316062927246e-01 6.714186668395996094e-01 2.238985002040863037e-01 1.000000000000000000e+00 -9.960784316062927246e-01 6.661283969879150391e-01 2.192233800888061523e-01 1.000000000000000000e+00 -9.960784316062927246e-01 6.608381271362304688e-01 2.145482450723648071e-01 1.000000000000000000e+00 -9.960784316062927246e-01 6.555478572845458984e-01 2.098731249570846558e-01 1.000000000000000000e+00 -9.960784316062927246e-01 6.502575874328613281e-01 2.051980048418045044e-01 1.000000000000000000e+00 -9.960784316062927246e-01 6.449673175811767578e-01 2.005228698253631592e-01 1.000000000000000000e+00 -9.960784316062927246e-01 6.396770477294921875e-01 1.958477497100830078e-01 1.000000000000000000e+00 -9.960784316062927246e-01 6.343867778778076172e-01 1.911726295948028564e-01 1.000000000000000000e+00 -9.960784316062927246e-01 6.290965080261230469e-01 1.864974945783615112e-01 1.000000000000000000e+00 -9.960784316062927246e-01 6.238062381744384766e-01 1.818223744630813599e-01 1.000000000000000000e+00 -9.960784316062927246e-01 6.185159683227539062e-01 1.771472543478012085e-01 1.000000000000000000e+00 -9.960784316062927246e-01 6.132256984710693359e-01 1.724721193313598633e-01 1.000000000000000000e+00 -9.960784316062927246e-01 6.079354286193847656e-01 1.677969992160797119e-01 1.000000000000000000e+00 -9.960784316062927246e-01 6.026451587677001953e-01 1.631218791007995605e-01 1.000000000000000000e+00 -9.949711561203002930e-01 5.974779129028320312e-01 1.594925075769424438e-01 1.000000000000000000e+00 -9.927566051483154297e-01 5.924336910247802734e-01 1.569088846445083618e-01 1.000000000000000000e+00 -9.905421137809753418e-01 5.873894691467285156e-01 1.543252617120742798e-01 1.000000000000000000e+00 -9.883275628089904785e-01 5.823452472686767578e-01 1.517416387796401978e-01 1.000000000000000000e+00 -9.861130118370056152e-01 5.773010253906250000e-01 1.491580158472061157e-01 1.000000000000000000e+00 -9.838985204696655273e-01 5.722568035125732422e-01 1.465743929147720337e-01 1.000000000000000000e+00 -9.816839694976806641e-01 5.672125816345214844e-01 1.439907699823379517e-01 1.000000000000000000e+00 -9.794694185256958008e-01 5.621684193611145020e-01 1.414071470499038696e-01 1.000000000000000000e+00 -9.772549271583557129e-01 5.571241974830627441e-01 1.388235241174697876e-01 1.000000000000000000e+00 -9.750403761863708496e-01 5.520799756050109863e-01 1.362399011850357056e-01 1.000000000000000000e+00 -9.728258252143859863e-01 5.470357537269592285e-01 1.336562931537628174e-01 1.000000000000000000e+00 -9.706112742424011230e-01 5.419915318489074707e-01 1.310726702213287354e-01 1.000000000000000000e+00 -9.683967828750610352e-01 5.369473099708557129e-01 1.284890472888946533e-01 1.000000000000000000e+00 -9.661822319030761719e-01 5.319030880928039551e-01 1.259054243564605713e-01 1.000000000000000000e+00 -9.639676809310913086e-01 5.268589258193969727e-01 1.233218014240264893e-01 1.000000000000000000e+00 -9.617531895637512207e-01 5.218147039413452148e-01 1.207381784915924072e-01 1.000000000000000000e+00 -9.595386385917663574e-01 5.167704820632934570e-01 1.181545555591583252e-01 1.000000000000000000e+00 -9.573240876197814941e-01 5.117262601852416992e-01 1.155709326267242432e-01 1.000000000000000000e+00 -9.551095962524414062e-01 5.066820383071899414e-01 1.129873096942901611e-01 1.000000000000000000e+00 -9.528950452804565430e-01 5.016378164291381836e-01 1.104036942124366760e-01 1.000000000000000000e+00 -9.506804943084716797e-01 4.965936243534088135e-01 1.078200712800025940e-01 1.000000000000000000e+00 -9.484660029411315918e-01 4.915494024753570557e-01 1.052364483475685120e-01 1.000000000000000000e+00 -9.462514519691467285e-01 4.865051805973052979e-01 1.026528254151344299e-01 1.000000000000000000e+00 -9.440369009971618652e-01 4.814609885215759277e-01 1.000692024827003479e-01 1.000000000000000000e+00 -9.418223500251770020e-01 4.764167666435241699e-01 9.748557955026626587e-02 1.000000000000000000e+00 -9.396078586578369141e-01 4.713725447654724121e-01 9.490196406841278076e-02 1.000000000000000000e+00 -9.373933076858520508e-01 4.663283228874206543e-01 9.231834113597869873e-02 1.000000000000000000e+00 -9.351787567138671875e-01 4.612841308116912842e-01 8.973471820354461670e-02 1.000000000000000000e+00 -9.329642653465270996e-01 4.562399089336395264e-01 8.715109527111053467e-02 1.000000000000000000e+00 -9.307497143745422363e-01 4.511956870555877686e-01 8.456747233867645264e-02 1.000000000000000000e+00 -9.285351634025573730e-01 4.461514949798583984e-01 8.198384940624237061e-02 1.000000000000000000e+00 -9.263206720352172852e-01 4.411072731018066406e-01 7.940023392438888550e-02 1.000000000000000000e+00 -9.230296015739440918e-01 4.364475309848785400e-01 7.704728841781616211e-02 1.000000000000000000e+00 -9.190926551818847656e-01 4.320184588432312012e-01 7.483275979757308960e-02 1.000000000000000000e+00 -9.151557087898254395e-01 4.275893867015838623e-01 7.261822372674942017e-02 1.000000000000000000e+00 -9.112187623977661133e-01 4.231603145599365234e-01 7.040368765592575073e-02 1.000000000000000000e+00 -9.072818160057067871e-01 4.187312424182891846e-01 6.818915903568267822e-02 1.000000000000000000e+00 -9.033448696136474609e-01 4.143022000789642334e-01 6.597462296485900879e-02 1.000000000000000000e+00 -8.994079232215881348e-01 4.098731279373168945e-01 6.376009434461593628e-02 1.000000000000000000e+00 -8.954709768295288086e-01 4.054440557956695557e-01 6.154555827379226685e-02 1.000000000000000000e+00 -8.915340304374694824e-01 4.010149836540222168e-01 5.933102592825889587e-02 1.000000000000000000e+00 -8.875970840454101562e-01 3.965859413146972656e-01 5.711649358272552490e-02 1.000000000000000000e+00 -8.836601376533508301e-01 3.921568691730499268e-01 5.490196123719215393e-02 1.000000000000000000e+00 -8.797231912612915039e-01 3.877277970314025879e-01 5.268742889165878296e-02 1.000000000000000000e+00 -8.757862448692321777e-01 3.832987248897552490e-01 5.047289654612541199e-02 1.000000000000000000e+00 -8.718492984771728516e-01 3.788696527481079102e-01 4.825836047530174255e-02 1.000000000000000000e+00 -8.679123520851135254e-01 3.744406104087829590e-01 4.604382812976837158e-02 1.000000000000000000e+00 -8.639754056930541992e-01 3.700115382671356201e-01 4.382929578423500061e-02 1.000000000000000000e+00 -8.600384593009948730e-01 3.655824661254882812e-01 4.161476343870162964e-02 1.000000000000000000e+00 -8.561015129089355469e-01 3.611533939838409424e-01 3.940023109316825867e-02 1.000000000000000000e+00 -8.521645665168762207e-01 3.567243516445159912e-01 3.718569874763488770e-02 1.000000000000000000e+00 -8.482276201248168945e-01 3.522952795028686523e-01 3.497116640210151672e-02 1.000000000000000000e+00 -8.442906737327575684e-01 3.478662073612213135e-01 3.275663033127784729e-02 1.000000000000000000e+00 -8.403537273406982422e-01 3.434371352195739746e-01 3.054209984838962555e-02 1.000000000000000000e+00 -8.364167809486389160e-01 3.390080630779266357e-01 2.832756564021110535e-02 1.000000000000000000e+00 -8.324798345565795898e-01 3.345790207386016846e-01 2.611303329467773438e-02 1.000000000000000000e+00 -8.285428881645202637e-01 3.301499485969543457e-01 2.389850094914436340e-02 1.000000000000000000e+00 -8.246059417724609375e-01 3.257208764553070068e-01 2.168396860361099243e-02 1.000000000000000000e+00 -8.206689953804016113e-01 3.212918043136596680e-01 1.946943439543247223e-02 1.000000000000000000e+00 -8.167320489883422852e-01 3.168627321720123291e-01 1.725490204989910126e-02 1.000000000000000000e+00 -8.127951025962829590e-01 3.124336898326873779e-01 1.504036877304315567e-02 1.000000000000000000e+00 -8.088581562042236328e-01 3.080046176910400391e-01 1.282583642750978470e-02 1.000000000000000000e+00 -8.049212098121643066e-01 3.035755455493927002e-01 1.061130315065383911e-02 1.000000000000000000e+00 -8.009842634201049805e-01 2.991464734077453613e-01 8.396770805120468140e-03 1.000000000000000000e+00 -7.952941060066223145e-01 2.958246767520904541e-01 8.027682080864906311e-03 1.000000000000000000e+00 -7.890195846557617188e-01 2.928719818592071533e-01 8.273741230368614197e-03 1.000000000000000000e+00 -7.827451229095458984e-01 2.899192571640014648e-01 8.519800379872322083e-03 1.000000000000000000e+00 -7.764706015586853027e-01 2.869665622711181641e-01 8.765859529376029968e-03 1.000000000000000000e+00 -7.701960802078247070e-01 2.840138375759124756e-01 9.011918678879737854e-03 1.000000000000000000e+00 -7.639215588569641113e-01 2.810611426830291748e-01 9.257977828383445740e-03 1.000000000000000000e+00 -7.576470375061035156e-01 2.781084179878234863e-01 9.504036977887153625e-03 1.000000000000000000e+00 -7.513725757598876953e-01 2.751557230949401855e-01 9.750096127390861511e-03 1.000000000000000000e+00 -7.450980544090270996e-01 2.722029983997344971e-01 9.996155276894569397e-03 1.000000000000000000e+00 -7.388235330581665039e-01 2.692502737045288086e-01 1.024221442639827728e-02 1.000000000000000000e+00 -7.325490117073059082e-01 2.662975788116455078e-01 1.048827357590198517e-02 1.000000000000000000e+00 -7.262744903564453125e-01 2.633448541164398193e-01 1.073433272540569305e-02 1.000000000000000000e+00 -7.200000286102294922e-01 2.603921592235565186e-01 1.098039187490940094e-02 1.000000000000000000e+00 -7.137255072593688965e-01 2.574394345283508301e-01 1.122645102441310883e-02 1.000000000000000000e+00 -7.074509859085083008e-01 2.544867396354675293e-01 1.147251017391681671e-02 1.000000000000000000e+00 -7.011764645576477051e-01 2.515340149402618408e-01 1.171856932342052460e-02 1.000000000000000000e+00 -6.949019432067871094e-01 2.485813200473785400e-01 1.196462940424680710e-02 1.000000000000000000e+00 -6.886274218559265137e-01 2.456286102533340454e-01 1.221068855375051498e-02 1.000000000000000000e+00 -6.823529601097106934e-01 2.426759004592895508e-01 1.245674770325422287e-02 1.000000000000000000e+00 -6.760784387588500977e-01 2.397231906652450562e-01 1.270280685275793076e-02 1.000000000000000000e+00 -6.698039174079895020e-01 2.367704659700393677e-01 1.294886600226163864e-02 1.000000000000000000e+00 -6.635293960571289062e-01 2.338177561759948730e-01 1.319492515176534653e-02 1.000000000000000000e+00 -6.572548747062683105e-01 2.308650463819503784e-01 1.344098430126905441e-02 1.000000000000000000e+00 -6.509804129600524902e-01 2.279123365879058838e-01 1.368704345077276230e-02 1.000000000000000000e+00 -6.447058916091918945e-01 2.249596267938613892e-01 1.393310260027647018e-02 1.000000000000000000e+00 -6.384313702583312988e-01 2.220069169998168945e-01 1.417916174978017807e-02 1.000000000000000000e+00 -6.321568489074707031e-01 2.190542072057723999e-01 1.442522089928388596e-02 1.000000000000000000e+00 -6.258823275566101074e-01 2.161014974117279053e-01 1.467128004878759384e-02 1.000000000000000000e+00 -6.196078658103942871e-01 2.131487876176834106e-01 1.491733919829130173e-02 1.000000000000000000e+00 -6.133333444595336914e-01 2.101960778236389160e-01 1.516339834779500961e-02 1.000000000000000000e+00 -6.070588231086730957e-01 2.072433680295944214e-01 1.540945749729871750e-02 1.000000000000000000e+00 -6.007843017578125000e-01 2.042906582355499268e-01 1.565551757812500000e-02 1.000000000000000000e+00 -5.945097804069519043e-01 2.023068070411682129e-01 1.590157672762870789e-02 1.000000000000000000e+00 -5.882353186607360840e-01 2.004613578319549561e-01 1.614763587713241577e-02 1.000000000000000000e+00 -5.819607973098754883e-01 1.986159235239028931e-01 1.639369502663612366e-02 1.000000000000000000e+00 -5.756862759590148926e-01 1.967704743146896362e-01 1.663975417613983154e-02 1.000000000000000000e+00 -5.694117546081542969e-01 1.949250251054763794e-01 1.688581332564353943e-02 1.000000000000000000e+00 -5.631372332572937012e-01 1.930795907974243164e-01 1.713187247514724731e-02 1.000000000000000000e+00 -5.568627715110778809e-01 1.912341415882110596e-01 1.737793162465095520e-02 1.000000000000000000e+00 -5.505882501602172852e-01 1.893886923789978027e-01 1.762399077415466309e-02 1.000000000000000000e+00 -5.443137288093566895e-01 1.875432580709457397e-01 1.787004992365837097e-02 1.000000000000000000e+00 -5.380392074584960938e-01 1.856978088617324829e-01 1.811610907316207886e-02 1.000000000000000000e+00 -5.317646861076354980e-01 1.838523596525192261e-01 1.836216822266578674e-02 1.000000000000000000e+00 -5.254902243614196777e-01 1.820069253444671631e-01 1.860822737216949463e-02 1.000000000000000000e+00 -5.192157030105590820e-01 1.801614761352539062e-01 1.885428652167320251e-02 1.000000000000000000e+00 -5.129411816596984863e-01 1.783160269260406494e-01 1.910034567117691040e-02 1.000000000000000000e+00 -5.066666603088378906e-01 1.764705926179885864e-01 1.934640482068061829e-02 1.000000000000000000e+00 -5.003921389579772949e-01 1.746251434087753296e-01 1.959246397018432617e-02 1.000000000000000000e+00 -4.941176474094390869e-01 1.727796941995620728e-01 1.983852311968803406e-02 1.000000000000000000e+00 -4.878431260585784912e-01 1.709342598915100098e-01 2.008458226919174194e-02 1.000000000000000000e+00 -4.815686345100402832e-01 1.690888106822967529e-01 2.033064141869544983e-02 1.000000000000000000e+00 -4.752941131591796875e-01 1.672433614730834961e-01 2.057670056819915771e-02 1.000000000000000000e+00 -4.690196216106414795e-01 1.653979271650314331e-01 2.082275971770286560e-02 1.000000000000000000e+00 -4.627451002597808838e-01 1.635524779558181763e-01 2.106881886720657349e-02 1.000000000000000000e+00 -4.564705789089202881e-01 1.617070287466049194e-01 2.131487801671028137e-02 1.000000000000000000e+00 -4.501960873603820801e-01 1.598615944385528564e-01 2.156093902885913849e-02 1.000000000000000000e+00 -4.439215660095214844e-01 1.580161452293395996e-01 2.180699817836284637e-02 1.000000000000000000e+00 -4.376470446586608887e-01 1.561707109212875366e-01 2.205305732786655426e-02 1.000000000000000000e+00 -4.313725531101226807e-01 1.543252617120742798e-01 2.229911647737026215e-02 1.000000000000000000e+00 -4.250980317592620850e-01 1.524798125028610229e-01 2.254517562687397003e-02 1.000000000000000000e+00 -4.188235402107238770e-01 1.506343781948089600e-01 2.279123477637767792e-02 1.000000000000000000e+00 -4.125490188598632812e-01 1.487889289855957031e-01 2.303729392588138580e-02 1.000000000000000000e+00 -4.062744975090026855e-01 1.469434797763824463e-01 2.328335307538509369e-02 1.000000000000000000e+00 -4.000000059604644775e-01 1.450980454683303833e-01 2.352941222488880157e-02 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/YlOrRd b/fastplotlib/utils/colormaps/YlOrRd deleted file mode 100644 index 84c507f5c..000000000 --- a/fastplotlib/utils/colormaps/YlOrRd +++ /dev/null @@ -1,256 +0,0 @@ -1.000000000000000000e+00 1.000000000000000000e+00 8.000000119209289551e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.977854490280151367e-01 7.945867180824279785e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.955709576606750488e-01 7.891734242439270020e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.933564066886901855e-01 7.837600708007812500e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.911418557167053223e-01 7.783467769622802734e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.889273643493652344e-01 7.729334831237792969e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.867128133773803711e-01 7.675201892852783203e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.844982624053955078e-01 7.621068954467773438e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.822837114334106445e-01 7.566936016082763672e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.800692200660705566e-01 7.512802481651306152e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.778546690940856934e-01 7.458669543266296387e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.756401181221008301e-01 7.404536604881286621e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.734256267547607422e-01 7.350403666496276855e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.712110757827758789e-01 7.296270728111267090e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.689965248107910156e-01 7.242137789726257324e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.667820334434509277e-01 7.188004851341247559e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.645674824714660645e-01 7.133871316909790039e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.623529314994812012e-01 7.079738378524780273e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.601383805274963379e-01 7.025605440139770508e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.579238891601562500e-01 6.971472501754760742e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.557093381881713867e-01 6.917339563369750977e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.534947872161865234e-01 6.863206624984741211e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.512802958488464355e-01 6.809073686599731445e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.490657448768615723e-01 6.754940152168273926e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.468511939048767090e-01 6.700807213783264160e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.446367025375366211e-01 6.646674275398254395e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.424221515655517578e-01 6.592541337013244629e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.402076005935668945e-01 6.538408398628234863e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.379931092262268066e-01 6.484275460243225098e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.357785582542419434e-01 6.430142521858215332e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.335640072822570801e-01 6.376008987426757812e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.313494563102722168e-01 6.321876049041748047e-01 1.000000000000000000e+00 -9.999846220016479492e-01 9.291042089462280273e-01 6.268050670623779297e-01 1.000000000000000000e+00 -9.998615980148315430e-01 9.266436100006103516e-01 6.216378211975097656e-01 1.000000000000000000e+00 -9.997385740280151367e-01 9.241830110549926758e-01 6.164705753326416016e-01 1.000000000000000000e+00 -9.996155500411987305e-01 9.217224121093750000e-01 6.113033294677734375e-01 1.000000000000000000e+00 -9.994925260543823242e-01 9.192618131637573242e-01 6.061360836029052734e-01 1.000000000000000000e+00 -9.993695020675659180e-01 9.168012142181396484e-01 6.009688377380371094e-01 1.000000000000000000e+00 -9.992464184761047363e-01 9.143406152725219727e-01 5.958015918731689453e-01 1.000000000000000000e+00 -9.991233944892883301e-01 9.118800759315490723e-01 5.906343460083007812e-01 1.000000000000000000e+00 -9.990003705024719238e-01 9.094194769859313965e-01 5.854671001434326172e-01 1.000000000000000000e+00 -9.988773465156555176e-01 9.069588780403137207e-01 5.802999138832092285e-01 1.000000000000000000e+00 -9.987543225288391113e-01 9.044982790946960449e-01 5.751326680183410645e-01 1.000000000000000000e+00 -9.986312985420227051e-01 9.020376801490783691e-01 5.699654221534729004e-01 1.000000000000000000e+00 -9.985082745552062988e-01 8.995770812034606934e-01 5.647981762886047363e-01 1.000000000000000000e+00 -9.983852505683898926e-01 8.971164822578430176e-01 5.596309304237365723e-01 1.000000000000000000e+00 -9.982622265815734863e-01 8.946558833122253418e-01 5.544636845588684082e-01 1.000000000000000000e+00 -9.981392025947570801e-01 8.921952843666076660e-01 5.492964386940002441e-01 1.000000000000000000e+00 -9.980161190032958984e-01 8.897347450256347656e-01 5.441291928291320801e-01 1.000000000000000000e+00 -9.978930950164794922e-01 8.872741460800170898e-01 5.389619469642639160e-01 1.000000000000000000e+00 -9.977700710296630859e-01 8.848135471343994141e-01 5.337947010993957520e-01 1.000000000000000000e+00 -9.976470470428466797e-01 8.823529481887817383e-01 5.286274552345275879e-01 1.000000000000000000e+00 -9.975240230560302734e-01 8.798923492431640625e-01 5.234602093696594238e-01 1.000000000000000000e+00 -9.974009990692138672e-01 8.774317502975463867e-01 5.182929635047912598e-01 1.000000000000000000e+00 -9.972779750823974609e-01 8.749711513519287109e-01 5.131257176399230957e-01 1.000000000000000000e+00 -9.971549510955810547e-01 8.725105524063110352e-01 5.079584717750549316e-01 1.000000000000000000e+00 -9.970319271087646484e-01 8.700499534606933594e-01 5.027912259101867676e-01 1.000000000000000000e+00 -9.969089031219482422e-01 8.675894141197204590e-01 4.976239800453186035e-01 1.000000000000000000e+00 -9.967858791351318359e-01 8.651288151741027832e-01 4.924567341804504395e-01 1.000000000000000000e+00 -9.966627955436706543e-01 8.626682162284851074e-01 4.872895181179046631e-01 1.000000000000000000e+00 -9.965397715568542480e-01 8.602076172828674316e-01 4.821222722530364990e-01 1.000000000000000000e+00 -9.964167475700378418e-01 8.577470183372497559e-01 4.769550263881683350e-01 1.000000000000000000e+00 -9.962937235832214355e-01 8.552864193916320801e-01 4.717877805233001709e-01 1.000000000000000000e+00 -9.961706995964050293e-01 8.528258204460144043e-01 4.666205346584320068e-01 1.000000000000000000e+00 -9.960784316062927246e-01 8.497808575630187988e-01 4.614532887935638428e-01 1.000000000000000000e+00 -9.960784316062927246e-01 8.449826836585998535e-01 4.562860429286956787e-01 1.000000000000000000e+00 -9.960784316062927246e-01 8.401845693588256836e-01 4.511187970638275146e-01 1.000000000000000000e+00 -9.960784316062927246e-01 8.353863954544067383e-01 4.459515511989593506e-01 1.000000000000000000e+00 -9.960784316062927246e-01 8.305882215499877930e-01 4.407843053340911865e-01 1.000000000000000000e+00 -9.960784316062927246e-01 8.257901072502136230e-01 4.356170594692230225e-01 1.000000000000000000e+00 -9.960784316062927246e-01 8.209919333457946777e-01 4.304498136043548584e-01 1.000000000000000000e+00 -9.960784316062927246e-01 8.161937594413757324e-01 4.252825975418090820e-01 1.000000000000000000e+00 -9.960784316062927246e-01 8.113956451416015625e-01 4.201153516769409180e-01 1.000000000000000000e+00 -9.960784316062927246e-01 8.065974712371826172e-01 4.149481058120727539e-01 1.000000000000000000e+00 -9.960784316062927246e-01 8.017992973327636719e-01 4.097808599472045898e-01 1.000000000000000000e+00 -9.960784316062927246e-01 7.970011830329895020e-01 4.046136140823364258e-01 1.000000000000000000e+00 -9.960784316062927246e-01 7.922030091285705566e-01 3.994463682174682617e-01 1.000000000000000000e+00 -9.960784316062927246e-01 7.874048352241516113e-01 3.942791223526000977e-01 1.000000000000000000e+00 -9.960784316062927246e-01 7.826066613197326660e-01 3.891118764877319336e-01 1.000000000000000000e+00 -9.960784316062927246e-01 7.778085470199584961e-01 3.839446306228637695e-01 1.000000000000000000e+00 -9.960784316062927246e-01 7.730103731155395508e-01 3.787773847579956055e-01 1.000000000000000000e+00 -9.960784316062927246e-01 7.682121992111206055e-01 3.736101388931274414e-01 1.000000000000000000e+00 -9.960784316062927246e-01 7.634140849113464355e-01 3.684428930282592773e-01 1.000000000000000000e+00 -9.960784316062927246e-01 7.586159110069274902e-01 3.632756769657135010e-01 1.000000000000000000e+00 -9.960784316062927246e-01 7.538177371025085449e-01 3.581084311008453369e-01 1.000000000000000000e+00 -9.960784316062927246e-01 7.490196228027343750e-01 3.529411852359771729e-01 1.000000000000000000e+00 -9.960784316062927246e-01 7.442214488983154297e-01 3.477739393711090088e-01 1.000000000000000000e+00 -9.960784316062927246e-01 7.394232749938964844e-01 3.426066935062408447e-01 1.000000000000000000e+00 -9.960784316062927246e-01 7.346251606941223145e-01 3.374394476413726807e-01 1.000000000000000000e+00 -9.960784316062927246e-01 7.298269867897033691e-01 3.322722017765045166e-01 1.000000000000000000e+00 -9.960784316062927246e-01 7.250288128852844238e-01 3.271049559116363525e-01 1.000000000000000000e+00 -9.960784316062927246e-01 7.202306985855102539e-01 3.219377100467681885e-01 1.000000000000000000e+00 -9.960784316062927246e-01 7.154325246810913086e-01 3.167704641819000244e-01 1.000000000000000000e+00 -9.960784316062927246e-01 7.106343507766723633e-01 3.116032183170318604e-01 1.000000000000000000e+00 -9.960784316062927246e-01 7.058362364768981934e-01 3.064359724521636963e-01 1.000000000000000000e+00 -9.960784316062927246e-01 7.010380625724792480e-01 3.012687563896179199e-01 1.000000000000000000e+00 -9.960322976112365723e-01 6.963321566581726074e-01 2.973010241985321045e-01 1.000000000000000000e+00 -9.959092736244201660e-01 6.917800903320312500e-01 2.953325510025024414e-01 1.000000000000000000e+00 -9.957862496376037598e-01 6.872279644012451172e-01 2.933640778064727783e-01 1.000000000000000000e+00 -9.956632256507873535e-01 6.826758980751037598e-01 2.913956046104431152e-01 1.000000000000000000e+00 -9.955402016639709473e-01 6.781237721443176270e-01 2.894271314144134521e-01 1.000000000000000000e+00 -9.954171180725097656e-01 6.735717058181762695e-01 2.874586582183837891e-01 1.000000000000000000e+00 -9.952940940856933594e-01 6.690195798873901367e-01 2.854901850223541260e-01 1.000000000000000000e+00 -9.951710700988769531e-01 6.644675135612487793e-01 2.835217118263244629e-01 1.000000000000000000e+00 -9.950480461120605469e-01 6.599153876304626465e-01 2.815532386302947998e-01 1.000000000000000000e+00 -9.949250221252441406e-01 6.553633213043212891e-01 2.795847654342651367e-01 1.000000000000000000e+00 -9.948019981384277344e-01 6.508112549781799316e-01 2.776162922382354736e-01 1.000000000000000000e+00 -9.946789741516113281e-01 6.462591290473937988e-01 2.756478190422058105e-01 1.000000000000000000e+00 -9.945559501647949219e-01 6.417070627212524414e-01 2.736793458461761475e-01 1.000000000000000000e+00 -9.944329261779785156e-01 6.371549367904663086e-01 2.717108726501464844e-01 1.000000000000000000e+00 -9.943099021911621094e-01 6.326028704643249512e-01 2.697423994541168213e-01 1.000000000000000000e+00 -9.941868782043457031e-01 6.280507445335388184e-01 2.677739262580871582e-01 1.000000000000000000e+00 -9.940637946128845215e-01 6.234986782073974609e-01 2.658054530620574951e-01 1.000000000000000000e+00 -9.939407706260681152e-01 6.189465522766113281e-01 2.638369798660278320e-01 1.000000000000000000e+00 -9.938177466392517090e-01 6.143944859504699707e-01 2.618685066699981689e-01 1.000000000000000000e+00 -9.936947226524353027e-01 6.098423600196838379e-01 2.599000334739685059e-01 1.000000000000000000e+00 -9.935716986656188965e-01 6.052902936935424805e-01 2.579315602779388428e-01 1.000000000000000000e+00 -9.934486746788024902e-01 6.007381677627563477e-01 2.559630870819091797e-01 1.000000000000000000e+00 -9.933256506919860840e-01 5.961861014366149902e-01 2.539946138858795166e-01 1.000000000000000000e+00 -9.932026267051696777e-01 5.916339755058288574e-01 2.520261406898498535e-01 1.000000000000000000e+00 -9.930796027183532715e-01 5.870819091796875000e-01 2.500576674938201904e-01 1.000000000000000000e+00 -9.929565787315368652e-01 5.825297832489013672e-01 2.480891942977905273e-01 1.000000000000000000e+00 -9.928335547447204590e-01 5.779777169227600098e-01 2.461207211017608643e-01 1.000000000000000000e+00 -9.927104711532592773e-01 5.734255909919738770e-01 2.441522479057312012e-01 1.000000000000000000e+00 -9.925874471664428711e-01 5.688735246658325195e-01 2.421837747097015381e-01 1.000000000000000000e+00 -9.924644231796264648e-01 5.643213987350463867e-01 2.402153015136718750e-01 1.000000000000000000e+00 -9.923413991928100586e-01 5.597693324089050293e-01 2.382468283176422119e-01 1.000000000000000000e+00 -9.922183752059936523e-01 5.552172064781188965e-01 2.362783551216125488e-01 1.000000000000000000e+00 -9.920953512191772461e-01 5.490657687187194824e-01 2.341868579387664795e-01 1.000000000000000000e+00 -9.919723272323608398e-01 5.413148999214172363e-01 2.319723218679428101e-01 1.000000000000000000e+00 -9.918493032455444336e-01 5.335640311241149902e-01 2.297577857971191406e-01 1.000000000000000000e+00 -9.917262792587280273e-01 5.258131623268127441e-01 2.275432497262954712e-01 1.000000000000000000e+00 -9.916032552719116211e-01 5.180622935295104980e-01 2.253287136554718018e-01 1.000000000000000000e+00 -9.914801716804504395e-01 5.103114247322082520e-01 2.231141924858093262e-01 1.000000000000000000e+00 -9.913571476936340332e-01 5.025605559349060059e-01 2.208996564149856567e-01 1.000000000000000000e+00 -9.912341237068176270e-01 4.948096871376037598e-01 2.186851203441619873e-01 1.000000000000000000e+00 -9.911110997200012207e-01 4.870588183403015137e-01 2.164705842733383179e-01 1.000000000000000000e+00 -9.909880757331848145e-01 4.793079495429992676e-01 2.142560482025146484e-01 1.000000000000000000e+00 -9.908650517463684082e-01 4.715570807456970215e-01 2.120415270328521729e-01 1.000000000000000000e+00 -9.907420277595520020e-01 4.638062417507171631e-01 2.098269909620285034e-01 1.000000000000000000e+00 -9.906190037727355957e-01 4.560553729534149170e-01 2.076124548912048340e-01 1.000000000000000000e+00 -9.904959797859191895e-01 4.483045041561126709e-01 2.053979188203811646e-01 1.000000000000000000e+00 -9.903729557991027832e-01 4.405536353588104248e-01 2.031833976507186890e-01 1.000000000000000000e+00 -9.902499318122863770e-01 4.328027665615081787e-01 2.009688615798950195e-01 1.000000000000000000e+00 -9.901268482208251953e-01 4.250518977642059326e-01 1.987543255090713501e-01 1.000000000000000000e+00 -9.900038242340087891e-01 4.173010289669036865e-01 1.965397894382476807e-01 1.000000000000000000e+00 -9.898808002471923828e-01 4.095501601696014404e-01 1.943252533674240112e-01 1.000000000000000000e+00 -9.897577762603759766e-01 4.017993211746215820e-01 1.921107321977615356e-01 1.000000000000000000e+00 -9.896347522735595703e-01 3.940484523773193359e-01 1.898961961269378662e-01 1.000000000000000000e+00 -9.895117282867431641e-01 3.862975835800170898e-01 1.876816600561141968e-01 1.000000000000000000e+00 -9.893887042999267578e-01 3.785467147827148438e-01 1.854671239852905273e-01 1.000000000000000000e+00 -9.892656803131103516e-01 3.707958459854125977e-01 1.832525879144668579e-01 1.000000000000000000e+00 -9.891426563262939453e-01 3.630449771881103516e-01 1.810380667448043823e-01 1.000000000000000000e+00 -9.890196323394775391e-01 3.552941083908081055e-01 1.788235306739807129e-01 1.000000000000000000e+00 -9.888965487480163574e-01 3.475432395935058594e-01 1.766089946031570435e-01 1.000000000000000000e+00 -9.887735247611999512e-01 3.397924005985260010e-01 1.743944585323333740e-01 1.000000000000000000e+00 -9.886505007743835449e-01 3.320415318012237549e-01 1.721799373626708984e-01 1.000000000000000000e+00 -9.885274767875671387e-01 3.242906630039215088e-01 1.699654012918472290e-01 1.000000000000000000e+00 -9.884044528007507324e-01 3.165397942066192627e-01 1.677508652210235596e-01 1.000000000000000000e+00 -9.882814288139343262e-01 3.087889254093170166e-01 1.655363291501998901e-01 1.000000000000000000e+00 -9.863129854202270508e-01 3.018838763236999512e-01 1.636293679475784302e-01 1.000000000000000000e+00 -9.832372069358825684e-01 2.954863607883453369e-01 1.619069576263427734e-01 1.000000000000000000e+00 -9.801614880561828613e-01 2.890888154506683350e-01 1.601845473051071167e-01 1.000000000000000000e+00 -9.770857095718383789e-01 2.826912701129913330e-01 1.584621369838714600e-01 1.000000000000000000e+00 -9.740099906921386719e-01 2.762937247753143311e-01 1.567397117614746094e-01 1.000000000000000000e+00 -9.709342718124389648e-01 2.698961794376373291e-01 1.550173014402389526e-01 1.000000000000000000e+00 -9.678584933280944824e-01 2.634986639022827148e-01 1.532948911190032959e-01 1.000000000000000000e+00 -9.647827744483947754e-01 2.571011185646057129e-01 1.515724658966064453e-01 1.000000000000000000e+00 -9.617070555686950684e-01 2.507035732269287109e-01 1.498500555753707886e-01 1.000000000000000000e+00 -9.586312770843505859e-01 2.443060427904129028e-01 1.481276452541351318e-01 1.000000000000000000e+00 -9.555555582046508789e-01 2.379084974527359009e-01 1.464052349328994751e-01 1.000000000000000000e+00 -9.524798393249511719e-01 2.315109521150588989e-01 1.446828097105026245e-01 1.000000000000000000e+00 -9.494040608406066895e-01 2.251134216785430908e-01 1.429603993892669678e-01 1.000000000000000000e+00 -9.463283419609069824e-01 2.187158763408660889e-01 1.412379890680313110e-01 1.000000000000000000e+00 -9.432526230812072754e-01 2.123183459043502808e-01 1.395155638456344604e-01 1.000000000000000000e+00 -9.401768445968627930e-01 2.059208005666732788e-01 1.377931535243988037e-01 1.000000000000000000e+00 -9.371011257171630859e-01 1.995232552289962769e-01 1.360707432031631470e-01 1.000000000000000000e+00 -9.340253472328186035e-01 1.931257247924804688e-01 1.343483328819274902e-01 1.000000000000000000e+00 -9.309496283531188965e-01 1.867281794548034668e-01 1.326259076595306396e-01 1.000000000000000000e+00 -9.278739094734191895e-01 1.803306490182876587e-01 1.309034973382949829e-01 1.000000000000000000e+00 -9.247981309890747070e-01 1.739331036806106567e-01 1.291810870170593262e-01 1.000000000000000000e+00 -9.217224121093750000e-01 1.675355583429336548e-01 1.274586766958236694e-01 1.000000000000000000e+00 -9.186466932296752930e-01 1.611380279064178467e-01 1.257362514734268188e-01 1.000000000000000000e+00 -9.155709147453308105e-01 1.547404825687408447e-01 1.240138411521911621e-01 1.000000000000000000e+00 -9.124951958656311035e-01 1.483429521322250366e-01 1.222914233803749084e-01 1.000000000000000000e+00 -9.094194769859313965e-01 1.419454067945480347e-01 1.205690130591392517e-01 1.000000000000000000e+00 -9.063436985015869141e-01 1.355478614568710327e-01 1.188465952873229980e-01 1.000000000000000000e+00 -9.032679796218872070e-01 1.291503310203552246e-01 1.171241849660873413e-01 1.000000000000000000e+00 -9.001922607421875000e-01 1.227527856826782227e-01 1.154017671942710876e-01 1.000000000000000000e+00 -8.971164822578430176e-01 1.163552477955818176e-01 1.136793568730354309e-01 1.000000000000000000e+00 -8.940407633781433105e-01 1.099577099084854126e-01 1.119569391012191772e-01 1.000000000000000000e+00 -8.909649848937988281e-01 1.035601720213890076e-01 1.102345287799835205e-01 1.000000000000000000e+00 -8.866897225379943848e-01 9.956170618534088135e-02 1.107266470789909363e-01 1.000000000000000000e+00 -8.820146322250366211e-01 9.636294096708297729e-02 1.119569391012191772e-01 1.000000000000000000e+00 -8.773394823074340820e-01 9.316416829824447632e-02 1.131872385740280151e-01 1.000000000000000000e+00 -8.726643323898315430e-01 8.996539562940597534e-02 1.144175305962562561e-01 1.000000000000000000e+00 -8.679892420768737793e-01 8.676663041114807129e-02 1.156478300690650940e-01 1.000000000000000000e+00 -8.633140921592712402e-01 8.356785774230957031e-02 1.168781220912933350e-01 1.000000000000000000e+00 -8.586390018463134766e-01 8.036909252405166626e-02 1.181084215641021729e-01 1.000000000000000000e+00 -8.539638519287109375e-01 7.717031985521316528e-02 1.193387135863304138e-01 1.000000000000000000e+00 -8.492887616157531738e-01 7.397154718637466431e-02 1.205690130591392517e-01 1.000000000000000000e+00 -8.446136116981506348e-01 7.077278196811676025e-02 1.217993050813674927e-01 1.000000000000000000e+00 -8.399384617805480957e-01 6.757400929927825928e-02 1.230296045541763306e-01 1.000000000000000000e+00 -8.352633714675903320e-01 6.437523663043975830e-02 1.242598965764045715e-01 1.000000000000000000e+00 -8.305882215499877930e-01 6.117647141218185425e-02 1.254902034997940063e-01 1.000000000000000000e+00 -8.259131312370300293e-01 5.797770246863365173e-02 1.267204880714416504e-01 1.000000000000000000e+00 -8.212379813194274902e-01 5.477892979979515076e-02 1.279507875442504883e-01 1.000000000000000000e+00 -8.165628314018249512e-01 5.158016085624694824e-02 1.291810870170593262e-01 1.000000000000000000e+00 -8.118877410888671875e-01 4.838139191269874573e-02 1.304113864898681641e-01 1.000000000000000000e+00 -8.072125911712646484e-01 4.518262296915054321e-02 1.316416710615158081e-01 1.000000000000000000e+00 -8.025375008583068848e-01 4.198385402560234070e-02 1.328719705343246460e-01 1.000000000000000000e+00 -7.978623509407043457e-01 3.878508135676383972e-02 1.341022700071334839e-01 1.000000000000000000e+00 -7.931872606277465820e-01 3.558631241321563721e-02 1.353325694799423218e-01 1.000000000000000000e+00 -7.885121107101440430e-01 3.238754346966743469e-02 1.365628540515899658e-01 1.000000000000000000e+00 -7.838369607925415039e-01 2.918877266347408295e-02 1.377931535243988037e-01 1.000000000000000000e+00 -7.791618704795837402e-01 2.599000371992588043e-02 1.390234529972076416e-01 1.000000000000000000e+00 -7.744867205619812012e-01 2.279123477637767792e-02 1.402537524700164795e-01 1.000000000000000000e+00 -7.698116302490234375e-01 1.959246397018432617e-02 1.414840519428253174e-01 1.000000000000000000e+00 -7.651364803314208984e-01 1.639369502663612366e-02 1.427143365144729614e-01 1.000000000000000000e+00 -7.604613900184631348e-01 1.319492515176534653e-02 1.439446359872817993e-01 1.000000000000000000e+00 -7.557862401008605957e-01 9.996155276894569397e-03 1.451749354600906372e-01 1.000000000000000000e+00 -7.511110901832580566e-01 6.797385402023792267e-03 1.464052349328994751e-01 1.000000000000000000e+00 -7.464359998703002930e-01 3.598615992814302444e-03 1.476355195045471191e-01 1.000000000000000000e+00 -7.417608499526977539e-01 3.998462052550166845e-04 1.488658189773559570e-01 1.000000000000000000e+00 -7.346097826957702637e-01 0.000000000000000000e+00 1.490196138620376587e-01 1.000000000000000000e+00 -7.271049618721008301e-01 0.000000000000000000e+00 1.490196138620376587e-01 1.000000000000000000e+00 -7.196001410484313965e-01 0.000000000000000000e+00 1.490196138620376587e-01 1.000000000000000000e+00 -7.120953202247619629e-01 0.000000000000000000e+00 1.490196138620376587e-01 1.000000000000000000e+00 -7.045905590057373047e-01 0.000000000000000000e+00 1.490196138620376587e-01 1.000000000000000000e+00 -6.970857381820678711e-01 0.000000000000000000e+00 1.490196138620376587e-01 1.000000000000000000e+00 -6.895809173583984375e-01 0.000000000000000000e+00 1.490196138620376587e-01 1.000000000000000000e+00 -6.820760965347290039e-01 0.000000000000000000e+00 1.490196138620376587e-01 1.000000000000000000e+00 -6.745713353157043457e-01 0.000000000000000000e+00 1.490196138620376587e-01 1.000000000000000000e+00 -6.670665144920349121e-01 0.000000000000000000e+00 1.490196138620376587e-01 1.000000000000000000e+00 -6.595616936683654785e-01 0.000000000000000000e+00 1.490196138620376587e-01 1.000000000000000000e+00 -6.520568728446960449e-01 0.000000000000000000e+00 1.490196138620376587e-01 1.000000000000000000e+00 -6.445521116256713867e-01 0.000000000000000000e+00 1.490196138620376587e-01 1.000000000000000000e+00 -6.370472908020019531e-01 0.000000000000000000e+00 1.490196138620376587e-01 1.000000000000000000e+00 -6.295424699783325195e-01 0.000000000000000000e+00 1.490196138620376587e-01 1.000000000000000000e+00 -6.220376491546630859e-01 0.000000000000000000e+00 1.490196138620376587e-01 1.000000000000000000e+00 -6.145328879356384277e-01 0.000000000000000000e+00 1.490196138620376587e-01 1.000000000000000000e+00 -6.070280671119689941e-01 0.000000000000000000e+00 1.490196138620376587e-01 1.000000000000000000e+00 -5.995232462882995605e-01 0.000000000000000000e+00 1.490196138620376587e-01 1.000000000000000000e+00 -5.920184254646301270e-01 0.000000000000000000e+00 1.490196138620376587e-01 1.000000000000000000e+00 -5.845136642456054688e-01 0.000000000000000000e+00 1.490196138620376587e-01 1.000000000000000000e+00 -5.770088434219360352e-01 0.000000000000000000e+00 1.490196138620376587e-01 1.000000000000000000e+00 -5.695040225982666016e-01 0.000000000000000000e+00 1.490196138620376587e-01 1.000000000000000000e+00 -5.619992017745971680e-01 0.000000000000000000e+00 1.490196138620376587e-01 1.000000000000000000e+00 -5.544944405555725098e-01 0.000000000000000000e+00 1.490196138620376587e-01 1.000000000000000000e+00 -5.469896197319030762e-01 0.000000000000000000e+00 1.490196138620376587e-01 1.000000000000000000e+00 -5.394847989082336426e-01 0.000000000000000000e+00 1.490196138620376587e-01 1.000000000000000000e+00 -5.319799780845642090e-01 0.000000000000000000e+00 1.490196138620376587e-01 1.000000000000000000e+00 -5.244752168655395508e-01 0.000000000000000000e+00 1.490196138620376587e-01 1.000000000000000000e+00 -5.169703960418701172e-01 0.000000000000000000e+00 1.490196138620376587e-01 1.000000000000000000e+00 -5.094655752182006836e-01 0.000000000000000000e+00 1.490196138620376587e-01 1.000000000000000000e+00 -5.019608139991760254e-01 0.000000000000000000e+00 1.490196138620376587e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/__init__.py b/fastplotlib/utils/colormaps/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/fastplotlib/utils/colormaps/afmhot b/fastplotlib/utils/colormaps/afmhot deleted file mode 100644 index 41158d67c..000000000 --- a/fastplotlib/utils/colormaps/afmhot +++ /dev/null @@ -1,256 +0,0 @@ -0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.843137718737125397e-03 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.568627543747425079e-02 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.352941222488880157e-02 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.137255087494850159e-02 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.921568766236305237e-02 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.705882444977760315e-02 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.490196123719215393e-02 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.274510174989700317e-02 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.058823853731155396e-02 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.843137532472610474e-02 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.627451211214065552e-02 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.411764889955520630e-02 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.019607856869697571e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.098039224743843079e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.176470592617988586e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.254902034997940063e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.333333402872085571e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.411764770746231079e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.490196138620376587e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.568627506494522095e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.647058874368667603e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.725490242242813110e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.803921610116958618e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.882352977991104126e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.960784345865249634e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.039215713739395142e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.117647081613540649e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.196078449487686157e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.274509817361831665e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.352941185235977173e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.431372553110122681e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.509804069995880127e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.588235437870025635e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.666666805744171143e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.745098173618316650e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.823529541492462158e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.901960909366607666e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.980392277240753174e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.058823645114898682e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.137255012989044189e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.215686380863189697e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.294117748737335205e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.372549116611480713e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.450980484485626221e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.529411852359771729e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.607843220233917236e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.686274588108062744e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.764705955982208252e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.843137323856353760e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.921568691730499268e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.000000059604644775e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.078431427478790283e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.156862795352935791e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.235294163227081299e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.313725531101226807e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.392156898975372314e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.470588266849517822e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.549019634723663330e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.627451002597808838e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.705882370471954346e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.784313738346099854e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.862745106220245361e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.941176474094390869e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.019608139991760254e-01 1.960784429684281349e-03 0.000000000000000000e+00 1.000000000000000000e+00 -5.098039507865905762e-01 9.803921915590763092e-03 0.000000000000000000e+00 1.000000000000000000e+00 -5.176470875740051270e-01 1.764705963432788849e-02 0.000000000000000000e+00 1.000000000000000000e+00 -5.254902243614196777e-01 2.549019642174243927e-02 0.000000000000000000e+00 1.000000000000000000e+00 -5.333333611488342285e-01 3.333333507180213928e-02 0.000000000000000000e+00 1.000000000000000000e+00 -5.411764979362487793e-01 4.117647185921669006e-02 0.000000000000000000e+00 1.000000000000000000e+00 -5.490196347236633301e-01 4.901960864663124084e-02 0.000000000000000000e+00 1.000000000000000000e+00 -5.568627715110778809e-01 5.686274543404579163e-02 0.000000000000000000e+00 1.000000000000000000e+00 -5.647059082984924316e-01 6.470588594675064087e-02 0.000000000000000000e+00 1.000000000000000000e+00 -5.725490450859069824e-01 7.254902273416519165e-02 0.000000000000000000e+00 1.000000000000000000e+00 -5.803921818733215332e-01 8.039215952157974243e-02 0.000000000000000000e+00 1.000000000000000000e+00 -5.882353186607360840e-01 8.823529630899429321e-02 0.000000000000000000e+00 1.000000000000000000e+00 -5.960784554481506348e-01 9.607843309640884399e-02 0.000000000000000000e+00 1.000000000000000000e+00 -6.039215922355651855e-01 1.039215698838233948e-01 0.000000000000000000e+00 1.000000000000000000e+00 -6.117647290229797363e-01 1.117647066712379456e-01 0.000000000000000000e+00 1.000000000000000000e+00 -6.196078658103942871e-01 1.196078434586524963e-01 0.000000000000000000e+00 1.000000000000000000e+00 -6.274510025978088379e-01 1.274509876966476440e-01 0.000000000000000000e+00 1.000000000000000000e+00 -6.352941393852233887e-01 1.352941244840621948e-01 0.000000000000000000e+00 1.000000000000000000e+00 -6.431372761726379395e-01 1.431372612714767456e-01 0.000000000000000000e+00 1.000000000000000000e+00 -6.509804129600524902e-01 1.509803980588912964e-01 0.000000000000000000e+00 1.000000000000000000e+00 -6.588235497474670410e-01 1.588235348463058472e-01 0.000000000000000000e+00 1.000000000000000000e+00 -6.666666865348815918e-01 1.666666716337203979e-01 0.000000000000000000e+00 1.000000000000000000e+00 -6.745098233222961426e-01 1.745098084211349487e-01 0.000000000000000000e+00 1.000000000000000000e+00 -6.823529601097106934e-01 1.823529452085494995e-01 0.000000000000000000e+00 1.000000000000000000e+00 -6.901960968971252441e-01 1.901960819959640503e-01 0.000000000000000000e+00 1.000000000000000000e+00 -6.980392336845397949e-01 1.980392187833786011e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.058823704719543457e-01 2.058823555707931519e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.137255072593688965e-01 2.137254923582077026e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.215686440467834473e-01 2.215686291456222534e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.294117808341979980e-01 2.294117659330368042e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.372549176216125488e-01 2.372549027204513550e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.450980544090270996e-01 2.450980395078659058e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.529411911964416504e-01 2.529411911964416504e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.607843279838562012e-01 2.607843279838562012e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.686274647712707520e-01 2.686274647712707520e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.764706015586853027e-01 2.764706015586853027e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.843137383460998535e-01 2.843137383460998535e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.921568751335144043e-01 2.921568751335144043e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.000000119209289551e-01 3.000000119209289551e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.078431487083435059e-01 3.078431487083435059e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.156862854957580566e-01 3.156862854957580566e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.235294222831726074e-01 3.235294222831726074e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.313725590705871582e-01 3.313725590705871582e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.392156958580017090e-01 3.392156958580017090e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.470588326454162598e-01 3.470588326454162598e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.549019694328308105e-01 3.549019694328308105e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.627451062202453613e-01 3.627451062202453613e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.705882430076599121e-01 3.705882430076599121e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.784313797950744629e-01 3.784313797950744629e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.862745165824890137e-01 3.862745165824890137e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.941176533699035645e-01 3.941176533699035645e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.019607901573181152e-01 4.019607901573181152e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.098039269447326660e-01 4.098039269447326660e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.176470637321472168e-01 4.176470637321472168e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.254902005195617676e-01 4.254902005195617676e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.333333373069763184e-01 4.333333373069763184e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.411764740943908691e-01 4.411764740943908691e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.490196108818054199e-01 4.490196108818054199e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.568627476692199707e-01 4.568627476692199707e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.647058844566345215e-01 4.647058844566345215e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.725490212440490723e-01 4.725490212440490723e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.803921580314636230e-01 4.803921580314636230e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.882352948188781738e-01 4.882352948188781738e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.960784316062927246e-01 4.960784316062927246e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.039215683937072754e-01 3.921568859368562698e-03 1.000000000000000000e+00 -1.000000000000000000e+00 5.117647051811218262e-01 1.176470611244440079e-02 1.000000000000000000e+00 -1.000000000000000000e+00 5.196078419685363770e-01 1.960784383118152618e-02 1.000000000000000000e+00 -1.000000000000000000e+00 5.274509787559509277e-01 2.745098061859607697e-02 1.000000000000000000e+00 -1.000000000000000000e+00 5.352941155433654785e-01 3.529411926865577698e-02 1.000000000000000000e+00 -1.000000000000000000e+00 5.431372523307800293e-01 4.313725605607032776e-02 1.000000000000000000e+00 -1.000000000000000000e+00 5.509803891181945801e-01 5.098039284348487854e-02 1.000000000000000000e+00 -1.000000000000000000e+00 5.588235259056091309e-01 5.882352963089942932e-02 1.000000000000000000e+00 -1.000000000000000000e+00 5.666666626930236816e-01 6.666667014360427856e-02 1.000000000000000000e+00 -1.000000000000000000e+00 5.745097994804382324e-01 7.450980693101882935e-02 1.000000000000000000e+00 -1.000000000000000000e+00 5.823529362678527832e-01 8.235294371843338013e-02 1.000000000000000000e+00 -1.000000000000000000e+00 5.901960730552673340e-01 9.019608050584793091e-02 1.000000000000000000e+00 -1.000000000000000000e+00 5.980392098426818848e-01 9.803921729326248169e-02 1.000000000000000000e+00 -1.000000000000000000e+00 6.058823466300964355e-01 1.058823540806770325e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.137254834175109863e-01 1.137254908680915833e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.215686202049255371e-01 1.215686276555061340e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.294117569923400879e-01 1.294117718935012817e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.372548937797546387e-01 1.372549086809158325e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.450980305671691895e-01 1.450980454683303833e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.529411673545837402e-01 1.529411822557449341e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.607843041419982910e-01 1.607843190431594849e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.686274409294128418e-01 1.686274558305740356e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.764705777168273926e-01 1.764705926179885864e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.843137145042419434e-01 1.843137294054031372e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.921568512916564941e-01 1.921568661928176880e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.999999880790710449e-01 2.000000029802322388e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.078431248664855957e-01 2.078431397676467896e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.156862616539001465e-01 2.156862765550613403e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.235293984413146973e-01 2.235294133424758911e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.313725352287292480e-01 2.313725501298904419e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.392156720161437988e-01 2.392156869173049927e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.470588088035583496e-01 2.470588237047195435e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.549019455909729004e-01 2.549019753932952881e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.627450823783874512e-01 2.627451121807098389e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.705882191658020020e-01 2.705882489681243896e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.784313559532165527e-01 2.784313857555389404e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.862744927406311035e-01 2.862745225429534912e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.941176295280456543e-01 2.941176593303680420e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.019607663154602051e-01 3.019607961177825928e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.098039031028747559e-01 3.098039329051971436e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.176470398902893066e-01 3.176470696926116943e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.254901766777038574e-01 3.254902064800262451e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.333333134651184082e-01 3.333333432674407959e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.411764502525329590e-01 3.411764800548553467e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.490195870399475098e-01 3.490196168422698975e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.568627238273620605e-01 3.568627536296844482e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.647058606147766113e-01 3.647058904170989990e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.725489974021911621e-01 3.725490272045135498e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.803921341896057129e-01 3.803921639919281006e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.882352709770202637e-01 3.882353007793426514e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.960784077644348145e-01 3.960784375667572021e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.039215445518493652e-01 4.039215743541717529e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.117646813392639160e-01 4.117647111415863037e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.196078181266784668e-01 4.196078479290008545e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.274509549140930176e-01 4.274509847164154053e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.352940917015075684e-01 4.352941215038299561e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.431372284889221191e-01 4.431372582912445068e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.509803652763366699e-01 4.509803950786590576e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.588235020637512207e-01 4.588235318660736084e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.666666388511657715e-01 4.666666686534881592e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.745097756385803223e-01 4.745098054409027100e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.823529124259948730e-01 4.823529422283172607e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.901960492134094238e-01 4.901960790157318115e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.980391860008239746e-01 4.980392158031463623e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 5.058823823928833008e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 5.137255191802978516e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 5.215686559677124023e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 5.294117927551269531e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 5.372549295425415039e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 5.450980663299560547e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 5.529412031173706055e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 5.607843399047851562e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 5.686274766921997070e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 5.764706134796142578e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 5.843137502670288086e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 5.921568870544433594e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 6.000000238418579102e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 6.078431606292724609e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 6.156862974166870117e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 6.235294342041015625e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 6.313725709915161133e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 6.392157077789306641e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 6.470588445663452148e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 6.549019813537597656e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 6.627451181411743164e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 6.705882549285888672e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 6.784313917160034180e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 6.862745285034179688e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 6.941176652908325195e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 7.019608020782470703e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 7.098039388656616211e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 7.176470756530761719e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 7.254902124404907227e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 7.333333492279052734e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 7.411764860153198242e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 7.490196228027343750e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 7.568627595901489258e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 7.647058963775634766e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 7.725490331649780273e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 7.803921699523925781e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 7.882353067398071289e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 7.960784435272216797e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 8.039215803146362305e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 8.117647171020507812e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 8.196078538894653320e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 8.274509906768798828e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 8.352941274642944336e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 8.431372642517089844e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 8.509804010391235352e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 8.588235378265380859e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 8.666666746139526367e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 8.745098114013671875e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 8.823529481887817383e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 8.901960849761962891e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 8.980392217636108398e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 9.058823585510253906e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 9.137254953384399414e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 9.215686321258544922e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 9.294117689132690430e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 9.372549057006835938e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 9.450980424880981445e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 9.529411792755126953e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 9.607843160629272461e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 9.686274528503417969e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 9.764705896377563477e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 9.843137264251708984e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 9.921568632125854492e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/autumn b/fastplotlib/utils/colormaps/autumn deleted file mode 100644 index b6c4be628..000000000 --- a/fastplotlib/utils/colormaps/autumn +++ /dev/null @@ -1,256 +0,0 @@ -1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.921568859368562698e-03 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.843137718737125397e-03 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.176470611244440079e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.568627543747425079e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.960784383118152618e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.352941222488880157e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.745098061859607697e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.137255087494850159e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.529411926865577698e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.921568766236305237e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.313725605607032776e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.705882444977760315e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.098039284348487854e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.490196123719215393e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.882352963089942932e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.274510174989700317e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.666667014360427856e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.058823853731155396e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.450980693101882935e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.843137532472610474e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.235294371843338013e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.627451211214065552e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.019608050584793091e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.411764889955520630e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.803921729326248169e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.019607856869697571e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.058823540806770325e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.098039224743843079e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.137254908680915833e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.176470592617988586e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.215686276555061340e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.254902034997940063e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.294117718935012817e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.333333402872085571e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.372549086809158325e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.411764770746231079e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.450980454683303833e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.490196138620376587e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.529411822557449341e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.568627506494522095e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.607843190431594849e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.647058874368667603e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.686274558305740356e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.725490242242813110e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.764705926179885864e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.803921610116958618e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.843137294054031372e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.882352977991104126e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.921568661928176880e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.960784345865249634e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.000000029802322388e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.039215713739395142e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.078431397676467896e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.117647081613540649e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.156862765550613403e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.196078449487686157e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.235294133424758911e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.274509817361831665e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.313725501298904419e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.352941185235977173e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.392156869173049927e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.431372553110122681e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.470588237047195435e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.509804069995880127e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.549019753932952881e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.588235437870025635e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.627451121807098389e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.666666805744171143e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.705882489681243896e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.745098173618316650e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.784313857555389404e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.823529541492462158e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.862745225429534912e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.901960909366607666e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.941176593303680420e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.980392277240753174e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.019607961177825928e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.058823645114898682e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.098039329051971436e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.137255012989044189e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.176470696926116943e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.215686380863189697e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.254902064800262451e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.294117748737335205e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.333333432674407959e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.372549116611480713e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.411764800548553467e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.450980484485626221e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.490196168422698975e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.529411852359771729e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.568627536296844482e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.607843220233917236e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.647058904170989990e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.686274588108062744e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.725490272045135498e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.764705955982208252e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.803921639919281006e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.843137323856353760e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.882353007793426514e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.921568691730499268e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.960784375667572021e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.000000059604644775e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.039215743541717529e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.078431427478790283e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.117647111415863037e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.156862795352935791e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.196078479290008545e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.235294163227081299e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.274509847164154053e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.313725531101226807e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.352941215038299561e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.392156898975372314e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.431372582912445068e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.470588266849517822e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.509803950786590576e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.549019634723663330e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.588235318660736084e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.627451002597808838e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.666666686534881592e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.705882370471954346e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.745098054409027100e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.784313738346099854e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.823529422283172607e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.862745106220245361e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.901960790157318115e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.941176474094390869e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.980392158031463623e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.019608139991760254e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.058823823928833008e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.098039507865905762e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.137255191802978516e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.176470875740051270e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.215686559677124023e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.254902243614196777e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.294117927551269531e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.333333611488342285e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.372549295425415039e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.411764979362487793e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.450980663299560547e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.490196347236633301e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.529412031173706055e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.568627715110778809e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.607843399047851562e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.647059082984924316e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.686274766921997070e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.725490450859069824e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.764706134796142578e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.803921818733215332e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.843137502670288086e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.882353186607360840e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.921568870544433594e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.960784554481506348e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.000000238418579102e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.039215922355651855e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.078431606292724609e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.117647290229797363e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.156862974166870117e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.196078658103942871e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.235294342041015625e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.274510025978088379e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.313725709915161133e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.352941393852233887e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.392157077789306641e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.431372761726379395e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.470588445663452148e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.509804129600524902e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.549019813537597656e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.588235497474670410e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.627451181411743164e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.666666865348815918e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.705882549285888672e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.745098233222961426e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.784313917160034180e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.823529601097106934e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.862745285034179688e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.901960968971252441e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.941176652908325195e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.980392336845397949e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.019608020782470703e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.058823704719543457e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.098039388656616211e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.137255072593688965e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.176470756530761719e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.215686440467834473e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.254902124404907227e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.294117808341979980e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.333333492279052734e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.372549176216125488e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.411764860153198242e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.450980544090270996e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.490196228027343750e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.529411911964416504e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.568627595901489258e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.607843279838562012e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.647058963775634766e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.686274647712707520e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.725490331649780273e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.764706015586853027e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.803921699523925781e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.843137383460998535e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.882353067398071289e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.921568751335144043e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.960784435272216797e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.000000119209289551e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.039215803146362305e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.078431487083435059e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.117647171020507812e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.156862854957580566e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.196078538894653320e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.235294222831726074e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.274509906768798828e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.313725590705871582e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.352941274642944336e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.392156958580017090e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.431372642517089844e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.470588326454162598e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.509804010391235352e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.549019694328308105e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.588235378265380859e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.627451062202453613e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.666666746139526367e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.705882430076599121e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.745098114013671875e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.784313797950744629e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.823529481887817383e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.862745165824890137e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.901960849761962891e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.941176533699035645e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.980392217636108398e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.019607901573181152e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.058823585510253906e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.098039269447326660e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.137254953384399414e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.176470637321472168e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.215686321258544922e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.254902005195617676e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.294117689132690430e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.333333373069763184e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.372549057006835938e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.411764740943908691e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.450980424880981445e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.490196108818054199e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.529411792755126953e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.568627476692199707e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.607843160629272461e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.647058844566345215e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.686274528503417969e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.725490212440490723e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.764705896377563477e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.803921580314636230e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.843137264251708984e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.882352948188781738e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.921568632125854492e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.960784316062927246e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/binary b/fastplotlib/utils/colormaps/binary deleted file mode 100644 index 19ae9bd30..000000000 --- a/fastplotlib/utils/colormaps/binary +++ /dev/null @@ -1,256 +0,0 @@ -1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -9.960784316062927246e-01 9.960784316062927246e-01 9.960784316062927246e-01 1.000000000000000000e+00 -9.921568632125854492e-01 9.921568632125854492e-01 9.921568632125854492e-01 1.000000000000000000e+00 -9.882352948188781738e-01 9.882352948188781738e-01 9.882352948188781738e-01 1.000000000000000000e+00 -9.843137264251708984e-01 9.843137264251708984e-01 9.843137264251708984e-01 1.000000000000000000e+00 -9.803921580314636230e-01 9.803921580314636230e-01 9.803921580314636230e-01 1.000000000000000000e+00 -9.764705896377563477e-01 9.764705896377563477e-01 9.764705896377563477e-01 1.000000000000000000e+00 -9.725490212440490723e-01 9.725490212440490723e-01 9.725490212440490723e-01 1.000000000000000000e+00 -9.686274528503417969e-01 9.686274528503417969e-01 9.686274528503417969e-01 1.000000000000000000e+00 -9.647058844566345215e-01 9.647058844566345215e-01 9.647058844566345215e-01 1.000000000000000000e+00 -9.607843160629272461e-01 9.607843160629272461e-01 9.607843160629272461e-01 1.000000000000000000e+00 -9.568627476692199707e-01 9.568627476692199707e-01 9.568627476692199707e-01 1.000000000000000000e+00 -9.529411792755126953e-01 9.529411792755126953e-01 9.529411792755126953e-01 1.000000000000000000e+00 -9.490196108818054199e-01 9.490196108818054199e-01 9.490196108818054199e-01 1.000000000000000000e+00 -9.450980424880981445e-01 9.450980424880981445e-01 9.450980424880981445e-01 1.000000000000000000e+00 -9.411764740943908691e-01 9.411764740943908691e-01 9.411764740943908691e-01 1.000000000000000000e+00 -9.372549057006835938e-01 9.372549057006835938e-01 9.372549057006835938e-01 1.000000000000000000e+00 -9.333333373069763184e-01 9.333333373069763184e-01 9.333333373069763184e-01 1.000000000000000000e+00 -9.294117689132690430e-01 9.294117689132690430e-01 9.294117689132690430e-01 1.000000000000000000e+00 -9.254902005195617676e-01 9.254902005195617676e-01 9.254902005195617676e-01 1.000000000000000000e+00 -9.215686321258544922e-01 9.215686321258544922e-01 9.215686321258544922e-01 1.000000000000000000e+00 -9.176470637321472168e-01 9.176470637321472168e-01 9.176470637321472168e-01 1.000000000000000000e+00 -9.137254953384399414e-01 9.137254953384399414e-01 9.137254953384399414e-01 1.000000000000000000e+00 -9.098039269447326660e-01 9.098039269447326660e-01 9.098039269447326660e-01 1.000000000000000000e+00 -9.058823585510253906e-01 9.058823585510253906e-01 9.058823585510253906e-01 1.000000000000000000e+00 -9.019607901573181152e-01 9.019607901573181152e-01 9.019607901573181152e-01 1.000000000000000000e+00 -8.980392217636108398e-01 8.980392217636108398e-01 8.980392217636108398e-01 1.000000000000000000e+00 -8.941176533699035645e-01 8.941176533699035645e-01 8.941176533699035645e-01 1.000000000000000000e+00 -8.901960849761962891e-01 8.901960849761962891e-01 8.901960849761962891e-01 1.000000000000000000e+00 -8.862745165824890137e-01 8.862745165824890137e-01 8.862745165824890137e-01 1.000000000000000000e+00 -8.823529481887817383e-01 8.823529481887817383e-01 8.823529481887817383e-01 1.000000000000000000e+00 -8.784313797950744629e-01 8.784313797950744629e-01 8.784313797950744629e-01 1.000000000000000000e+00 -8.745098114013671875e-01 8.745098114013671875e-01 8.745098114013671875e-01 1.000000000000000000e+00 -8.705882430076599121e-01 8.705882430076599121e-01 8.705882430076599121e-01 1.000000000000000000e+00 -8.666666746139526367e-01 8.666666746139526367e-01 8.666666746139526367e-01 1.000000000000000000e+00 -8.627451062202453613e-01 8.627451062202453613e-01 8.627451062202453613e-01 1.000000000000000000e+00 -8.588235378265380859e-01 8.588235378265380859e-01 8.588235378265380859e-01 1.000000000000000000e+00 -8.549019694328308105e-01 8.549019694328308105e-01 8.549019694328308105e-01 1.000000000000000000e+00 -8.509804010391235352e-01 8.509804010391235352e-01 8.509804010391235352e-01 1.000000000000000000e+00 -8.470588326454162598e-01 8.470588326454162598e-01 8.470588326454162598e-01 1.000000000000000000e+00 -8.431372642517089844e-01 8.431372642517089844e-01 8.431372642517089844e-01 1.000000000000000000e+00 -8.392156958580017090e-01 8.392156958580017090e-01 8.392156958580017090e-01 1.000000000000000000e+00 -8.352941274642944336e-01 8.352941274642944336e-01 8.352941274642944336e-01 1.000000000000000000e+00 -8.313725590705871582e-01 8.313725590705871582e-01 8.313725590705871582e-01 1.000000000000000000e+00 -8.274509906768798828e-01 8.274509906768798828e-01 8.274509906768798828e-01 1.000000000000000000e+00 -8.235294222831726074e-01 8.235294222831726074e-01 8.235294222831726074e-01 1.000000000000000000e+00 -8.196078538894653320e-01 8.196078538894653320e-01 8.196078538894653320e-01 1.000000000000000000e+00 -8.156862854957580566e-01 8.156862854957580566e-01 8.156862854957580566e-01 1.000000000000000000e+00 -8.117647171020507812e-01 8.117647171020507812e-01 8.117647171020507812e-01 1.000000000000000000e+00 -8.078431487083435059e-01 8.078431487083435059e-01 8.078431487083435059e-01 1.000000000000000000e+00 -8.039215803146362305e-01 8.039215803146362305e-01 8.039215803146362305e-01 1.000000000000000000e+00 -8.000000119209289551e-01 8.000000119209289551e-01 8.000000119209289551e-01 1.000000000000000000e+00 -7.960784435272216797e-01 7.960784435272216797e-01 7.960784435272216797e-01 1.000000000000000000e+00 -7.921568751335144043e-01 7.921568751335144043e-01 7.921568751335144043e-01 1.000000000000000000e+00 -7.882353067398071289e-01 7.882353067398071289e-01 7.882353067398071289e-01 1.000000000000000000e+00 -7.843137383460998535e-01 7.843137383460998535e-01 7.843137383460998535e-01 1.000000000000000000e+00 -7.803921699523925781e-01 7.803921699523925781e-01 7.803921699523925781e-01 1.000000000000000000e+00 -7.764706015586853027e-01 7.764706015586853027e-01 7.764706015586853027e-01 1.000000000000000000e+00 -7.725490331649780273e-01 7.725490331649780273e-01 7.725490331649780273e-01 1.000000000000000000e+00 -7.686274647712707520e-01 7.686274647712707520e-01 7.686274647712707520e-01 1.000000000000000000e+00 -7.647058963775634766e-01 7.647058963775634766e-01 7.647058963775634766e-01 1.000000000000000000e+00 -7.607843279838562012e-01 7.607843279838562012e-01 7.607843279838562012e-01 1.000000000000000000e+00 -7.568627595901489258e-01 7.568627595901489258e-01 7.568627595901489258e-01 1.000000000000000000e+00 -7.529411911964416504e-01 7.529411911964416504e-01 7.529411911964416504e-01 1.000000000000000000e+00 -7.490196228027343750e-01 7.490196228027343750e-01 7.490196228027343750e-01 1.000000000000000000e+00 -7.450980544090270996e-01 7.450980544090270996e-01 7.450980544090270996e-01 1.000000000000000000e+00 -7.411764860153198242e-01 7.411764860153198242e-01 7.411764860153198242e-01 1.000000000000000000e+00 -7.372549176216125488e-01 7.372549176216125488e-01 7.372549176216125488e-01 1.000000000000000000e+00 -7.333333492279052734e-01 7.333333492279052734e-01 7.333333492279052734e-01 1.000000000000000000e+00 -7.294117808341979980e-01 7.294117808341979980e-01 7.294117808341979980e-01 1.000000000000000000e+00 -7.254902124404907227e-01 7.254902124404907227e-01 7.254902124404907227e-01 1.000000000000000000e+00 -7.215686440467834473e-01 7.215686440467834473e-01 7.215686440467834473e-01 1.000000000000000000e+00 -7.176470756530761719e-01 7.176470756530761719e-01 7.176470756530761719e-01 1.000000000000000000e+00 -7.137255072593688965e-01 7.137255072593688965e-01 7.137255072593688965e-01 1.000000000000000000e+00 -7.098039388656616211e-01 7.098039388656616211e-01 7.098039388656616211e-01 1.000000000000000000e+00 -7.058823704719543457e-01 7.058823704719543457e-01 7.058823704719543457e-01 1.000000000000000000e+00 -7.019608020782470703e-01 7.019608020782470703e-01 7.019608020782470703e-01 1.000000000000000000e+00 -6.980392336845397949e-01 6.980392336845397949e-01 6.980392336845397949e-01 1.000000000000000000e+00 -6.941176652908325195e-01 6.941176652908325195e-01 6.941176652908325195e-01 1.000000000000000000e+00 -6.901960968971252441e-01 6.901960968971252441e-01 6.901960968971252441e-01 1.000000000000000000e+00 -6.862745285034179688e-01 6.862745285034179688e-01 6.862745285034179688e-01 1.000000000000000000e+00 -6.823529601097106934e-01 6.823529601097106934e-01 6.823529601097106934e-01 1.000000000000000000e+00 -6.784313917160034180e-01 6.784313917160034180e-01 6.784313917160034180e-01 1.000000000000000000e+00 -6.745098233222961426e-01 6.745098233222961426e-01 6.745098233222961426e-01 1.000000000000000000e+00 -6.705882549285888672e-01 6.705882549285888672e-01 6.705882549285888672e-01 1.000000000000000000e+00 -6.666666865348815918e-01 6.666666865348815918e-01 6.666666865348815918e-01 1.000000000000000000e+00 -6.627451181411743164e-01 6.627451181411743164e-01 6.627451181411743164e-01 1.000000000000000000e+00 -6.588235497474670410e-01 6.588235497474670410e-01 6.588235497474670410e-01 1.000000000000000000e+00 -6.549019813537597656e-01 6.549019813537597656e-01 6.549019813537597656e-01 1.000000000000000000e+00 -6.509804129600524902e-01 6.509804129600524902e-01 6.509804129600524902e-01 1.000000000000000000e+00 -6.470588445663452148e-01 6.470588445663452148e-01 6.470588445663452148e-01 1.000000000000000000e+00 -6.431372761726379395e-01 6.431372761726379395e-01 6.431372761726379395e-01 1.000000000000000000e+00 -6.392157077789306641e-01 6.392157077789306641e-01 6.392157077789306641e-01 1.000000000000000000e+00 -6.352941393852233887e-01 6.352941393852233887e-01 6.352941393852233887e-01 1.000000000000000000e+00 -6.313725709915161133e-01 6.313725709915161133e-01 6.313725709915161133e-01 1.000000000000000000e+00 -6.274510025978088379e-01 6.274510025978088379e-01 6.274510025978088379e-01 1.000000000000000000e+00 -6.235294342041015625e-01 6.235294342041015625e-01 6.235294342041015625e-01 1.000000000000000000e+00 -6.196078658103942871e-01 6.196078658103942871e-01 6.196078658103942871e-01 1.000000000000000000e+00 -6.156862974166870117e-01 6.156862974166870117e-01 6.156862974166870117e-01 1.000000000000000000e+00 -6.117647290229797363e-01 6.117647290229797363e-01 6.117647290229797363e-01 1.000000000000000000e+00 -6.078431606292724609e-01 6.078431606292724609e-01 6.078431606292724609e-01 1.000000000000000000e+00 -6.039215922355651855e-01 6.039215922355651855e-01 6.039215922355651855e-01 1.000000000000000000e+00 -6.000000238418579102e-01 6.000000238418579102e-01 6.000000238418579102e-01 1.000000000000000000e+00 -5.960784554481506348e-01 5.960784554481506348e-01 5.960784554481506348e-01 1.000000000000000000e+00 -5.921568870544433594e-01 5.921568870544433594e-01 5.921568870544433594e-01 1.000000000000000000e+00 -5.882353186607360840e-01 5.882353186607360840e-01 5.882353186607360840e-01 1.000000000000000000e+00 -5.843137502670288086e-01 5.843137502670288086e-01 5.843137502670288086e-01 1.000000000000000000e+00 -5.803921818733215332e-01 5.803921818733215332e-01 5.803921818733215332e-01 1.000000000000000000e+00 -5.764706134796142578e-01 5.764706134796142578e-01 5.764706134796142578e-01 1.000000000000000000e+00 -5.725490450859069824e-01 5.725490450859069824e-01 5.725490450859069824e-01 1.000000000000000000e+00 -5.686274766921997070e-01 5.686274766921997070e-01 5.686274766921997070e-01 1.000000000000000000e+00 -5.647059082984924316e-01 5.647059082984924316e-01 5.647059082984924316e-01 1.000000000000000000e+00 -5.607843399047851562e-01 5.607843399047851562e-01 5.607843399047851562e-01 1.000000000000000000e+00 -5.568627715110778809e-01 5.568627715110778809e-01 5.568627715110778809e-01 1.000000000000000000e+00 -5.529412031173706055e-01 5.529412031173706055e-01 5.529412031173706055e-01 1.000000000000000000e+00 -5.490196347236633301e-01 5.490196347236633301e-01 5.490196347236633301e-01 1.000000000000000000e+00 -5.450980663299560547e-01 5.450980663299560547e-01 5.450980663299560547e-01 1.000000000000000000e+00 -5.411764979362487793e-01 5.411764979362487793e-01 5.411764979362487793e-01 1.000000000000000000e+00 -5.372549295425415039e-01 5.372549295425415039e-01 5.372549295425415039e-01 1.000000000000000000e+00 -5.333333611488342285e-01 5.333333611488342285e-01 5.333333611488342285e-01 1.000000000000000000e+00 -5.294117927551269531e-01 5.294117927551269531e-01 5.294117927551269531e-01 1.000000000000000000e+00 -5.254902243614196777e-01 5.254902243614196777e-01 5.254902243614196777e-01 1.000000000000000000e+00 -5.215686559677124023e-01 5.215686559677124023e-01 5.215686559677124023e-01 1.000000000000000000e+00 -5.176470875740051270e-01 5.176470875740051270e-01 5.176470875740051270e-01 1.000000000000000000e+00 -5.137255191802978516e-01 5.137255191802978516e-01 5.137255191802978516e-01 1.000000000000000000e+00 -5.098039507865905762e-01 5.098039507865905762e-01 5.098039507865905762e-01 1.000000000000000000e+00 -5.058823823928833008e-01 5.058823823928833008e-01 5.058823823928833008e-01 1.000000000000000000e+00 -5.019608139991760254e-01 5.019608139991760254e-01 5.019608139991760254e-01 1.000000000000000000e+00 -4.980392158031463623e-01 4.980392158031463623e-01 4.980392158031463623e-01 1.000000000000000000e+00 -4.941176474094390869e-01 4.941176474094390869e-01 4.941176474094390869e-01 1.000000000000000000e+00 -4.901960790157318115e-01 4.901960790157318115e-01 4.901960790157318115e-01 1.000000000000000000e+00 -4.862745106220245361e-01 4.862745106220245361e-01 4.862745106220245361e-01 1.000000000000000000e+00 -4.823529422283172607e-01 4.823529422283172607e-01 4.823529422283172607e-01 1.000000000000000000e+00 -4.784313738346099854e-01 4.784313738346099854e-01 4.784313738346099854e-01 1.000000000000000000e+00 -4.745098054409027100e-01 4.745098054409027100e-01 4.745098054409027100e-01 1.000000000000000000e+00 -4.705882370471954346e-01 4.705882370471954346e-01 4.705882370471954346e-01 1.000000000000000000e+00 -4.666666686534881592e-01 4.666666686534881592e-01 4.666666686534881592e-01 1.000000000000000000e+00 -4.627451002597808838e-01 4.627451002597808838e-01 4.627451002597808838e-01 1.000000000000000000e+00 -4.588235318660736084e-01 4.588235318660736084e-01 4.588235318660736084e-01 1.000000000000000000e+00 -4.549019634723663330e-01 4.549019634723663330e-01 4.549019634723663330e-01 1.000000000000000000e+00 -4.509803950786590576e-01 4.509803950786590576e-01 4.509803950786590576e-01 1.000000000000000000e+00 -4.470588266849517822e-01 4.470588266849517822e-01 4.470588266849517822e-01 1.000000000000000000e+00 -4.431372582912445068e-01 4.431372582912445068e-01 4.431372582912445068e-01 1.000000000000000000e+00 -4.392156898975372314e-01 4.392156898975372314e-01 4.392156898975372314e-01 1.000000000000000000e+00 -4.352941215038299561e-01 4.352941215038299561e-01 4.352941215038299561e-01 1.000000000000000000e+00 -4.313725531101226807e-01 4.313725531101226807e-01 4.313725531101226807e-01 1.000000000000000000e+00 -4.274509847164154053e-01 4.274509847164154053e-01 4.274509847164154053e-01 1.000000000000000000e+00 -4.235294163227081299e-01 4.235294163227081299e-01 4.235294163227081299e-01 1.000000000000000000e+00 -4.196078479290008545e-01 4.196078479290008545e-01 4.196078479290008545e-01 1.000000000000000000e+00 -4.156862795352935791e-01 4.156862795352935791e-01 4.156862795352935791e-01 1.000000000000000000e+00 -4.117647111415863037e-01 4.117647111415863037e-01 4.117647111415863037e-01 1.000000000000000000e+00 -4.078431427478790283e-01 4.078431427478790283e-01 4.078431427478790283e-01 1.000000000000000000e+00 -4.039215743541717529e-01 4.039215743541717529e-01 4.039215743541717529e-01 1.000000000000000000e+00 -4.000000059604644775e-01 4.000000059604644775e-01 4.000000059604644775e-01 1.000000000000000000e+00 -3.960784375667572021e-01 3.960784375667572021e-01 3.960784375667572021e-01 1.000000000000000000e+00 -3.921568691730499268e-01 3.921568691730499268e-01 3.921568691730499268e-01 1.000000000000000000e+00 -3.882353007793426514e-01 3.882353007793426514e-01 3.882353007793426514e-01 1.000000000000000000e+00 -3.843137323856353760e-01 3.843137323856353760e-01 3.843137323856353760e-01 1.000000000000000000e+00 -3.803921639919281006e-01 3.803921639919281006e-01 3.803921639919281006e-01 1.000000000000000000e+00 -3.764705955982208252e-01 3.764705955982208252e-01 3.764705955982208252e-01 1.000000000000000000e+00 -3.725490272045135498e-01 3.725490272045135498e-01 3.725490272045135498e-01 1.000000000000000000e+00 -3.686274588108062744e-01 3.686274588108062744e-01 3.686274588108062744e-01 1.000000000000000000e+00 -3.647058904170989990e-01 3.647058904170989990e-01 3.647058904170989990e-01 1.000000000000000000e+00 -3.607843220233917236e-01 3.607843220233917236e-01 3.607843220233917236e-01 1.000000000000000000e+00 -3.568627536296844482e-01 3.568627536296844482e-01 3.568627536296844482e-01 1.000000000000000000e+00 -3.529411852359771729e-01 3.529411852359771729e-01 3.529411852359771729e-01 1.000000000000000000e+00 -3.490196168422698975e-01 3.490196168422698975e-01 3.490196168422698975e-01 1.000000000000000000e+00 -3.450980484485626221e-01 3.450980484485626221e-01 3.450980484485626221e-01 1.000000000000000000e+00 -3.411764800548553467e-01 3.411764800548553467e-01 3.411764800548553467e-01 1.000000000000000000e+00 -3.372549116611480713e-01 3.372549116611480713e-01 3.372549116611480713e-01 1.000000000000000000e+00 -3.333333432674407959e-01 3.333333432674407959e-01 3.333333432674407959e-01 1.000000000000000000e+00 -3.294117748737335205e-01 3.294117748737335205e-01 3.294117748737335205e-01 1.000000000000000000e+00 -3.254902064800262451e-01 3.254902064800262451e-01 3.254902064800262451e-01 1.000000000000000000e+00 -3.215686380863189697e-01 3.215686380863189697e-01 3.215686380863189697e-01 1.000000000000000000e+00 -3.176470696926116943e-01 3.176470696926116943e-01 3.176470696926116943e-01 1.000000000000000000e+00 -3.137255012989044189e-01 3.137255012989044189e-01 3.137255012989044189e-01 1.000000000000000000e+00 -3.098039329051971436e-01 3.098039329051971436e-01 3.098039329051971436e-01 1.000000000000000000e+00 -3.058823645114898682e-01 3.058823645114898682e-01 3.058823645114898682e-01 1.000000000000000000e+00 -3.019607961177825928e-01 3.019607961177825928e-01 3.019607961177825928e-01 1.000000000000000000e+00 -2.980392277240753174e-01 2.980392277240753174e-01 2.980392277240753174e-01 1.000000000000000000e+00 -2.941176593303680420e-01 2.941176593303680420e-01 2.941176593303680420e-01 1.000000000000000000e+00 -2.901960909366607666e-01 2.901960909366607666e-01 2.901960909366607666e-01 1.000000000000000000e+00 -2.862745225429534912e-01 2.862745225429534912e-01 2.862745225429534912e-01 1.000000000000000000e+00 -2.823529541492462158e-01 2.823529541492462158e-01 2.823529541492462158e-01 1.000000000000000000e+00 -2.784313857555389404e-01 2.784313857555389404e-01 2.784313857555389404e-01 1.000000000000000000e+00 -2.745098173618316650e-01 2.745098173618316650e-01 2.745098173618316650e-01 1.000000000000000000e+00 -2.705882489681243896e-01 2.705882489681243896e-01 2.705882489681243896e-01 1.000000000000000000e+00 -2.666666805744171143e-01 2.666666805744171143e-01 2.666666805744171143e-01 1.000000000000000000e+00 -2.627451121807098389e-01 2.627451121807098389e-01 2.627451121807098389e-01 1.000000000000000000e+00 -2.588235437870025635e-01 2.588235437870025635e-01 2.588235437870025635e-01 1.000000000000000000e+00 -2.549019753932952881e-01 2.549019753932952881e-01 2.549019753932952881e-01 1.000000000000000000e+00 -2.509804069995880127e-01 2.509804069995880127e-01 2.509804069995880127e-01 1.000000000000000000e+00 -2.470588237047195435e-01 2.470588237047195435e-01 2.470588237047195435e-01 1.000000000000000000e+00 -2.431372553110122681e-01 2.431372553110122681e-01 2.431372553110122681e-01 1.000000000000000000e+00 -2.392156869173049927e-01 2.392156869173049927e-01 2.392156869173049927e-01 1.000000000000000000e+00 -2.352941185235977173e-01 2.352941185235977173e-01 2.352941185235977173e-01 1.000000000000000000e+00 -2.313725501298904419e-01 2.313725501298904419e-01 2.313725501298904419e-01 1.000000000000000000e+00 -2.274509817361831665e-01 2.274509817361831665e-01 2.274509817361831665e-01 1.000000000000000000e+00 -2.235294133424758911e-01 2.235294133424758911e-01 2.235294133424758911e-01 1.000000000000000000e+00 -2.196078449487686157e-01 2.196078449487686157e-01 2.196078449487686157e-01 1.000000000000000000e+00 -2.156862765550613403e-01 2.156862765550613403e-01 2.156862765550613403e-01 1.000000000000000000e+00 -2.117647081613540649e-01 2.117647081613540649e-01 2.117647081613540649e-01 1.000000000000000000e+00 -2.078431397676467896e-01 2.078431397676467896e-01 2.078431397676467896e-01 1.000000000000000000e+00 -2.039215713739395142e-01 2.039215713739395142e-01 2.039215713739395142e-01 1.000000000000000000e+00 -2.000000029802322388e-01 2.000000029802322388e-01 2.000000029802322388e-01 1.000000000000000000e+00 -1.960784345865249634e-01 1.960784345865249634e-01 1.960784345865249634e-01 1.000000000000000000e+00 -1.921568661928176880e-01 1.921568661928176880e-01 1.921568661928176880e-01 1.000000000000000000e+00 -1.882352977991104126e-01 1.882352977991104126e-01 1.882352977991104126e-01 1.000000000000000000e+00 -1.843137294054031372e-01 1.843137294054031372e-01 1.843137294054031372e-01 1.000000000000000000e+00 -1.803921610116958618e-01 1.803921610116958618e-01 1.803921610116958618e-01 1.000000000000000000e+00 -1.764705926179885864e-01 1.764705926179885864e-01 1.764705926179885864e-01 1.000000000000000000e+00 -1.725490242242813110e-01 1.725490242242813110e-01 1.725490242242813110e-01 1.000000000000000000e+00 -1.686274558305740356e-01 1.686274558305740356e-01 1.686274558305740356e-01 1.000000000000000000e+00 -1.647058874368667603e-01 1.647058874368667603e-01 1.647058874368667603e-01 1.000000000000000000e+00 -1.607843190431594849e-01 1.607843190431594849e-01 1.607843190431594849e-01 1.000000000000000000e+00 -1.568627506494522095e-01 1.568627506494522095e-01 1.568627506494522095e-01 1.000000000000000000e+00 -1.529411822557449341e-01 1.529411822557449341e-01 1.529411822557449341e-01 1.000000000000000000e+00 -1.490196138620376587e-01 1.490196138620376587e-01 1.490196138620376587e-01 1.000000000000000000e+00 -1.450980454683303833e-01 1.450980454683303833e-01 1.450980454683303833e-01 1.000000000000000000e+00 -1.411764770746231079e-01 1.411764770746231079e-01 1.411764770746231079e-01 1.000000000000000000e+00 -1.372549086809158325e-01 1.372549086809158325e-01 1.372549086809158325e-01 1.000000000000000000e+00 -1.333333402872085571e-01 1.333333402872085571e-01 1.333333402872085571e-01 1.000000000000000000e+00 -1.294117718935012817e-01 1.294117718935012817e-01 1.294117718935012817e-01 1.000000000000000000e+00 -1.254902034997940063e-01 1.254902034997940063e-01 1.254902034997940063e-01 1.000000000000000000e+00 -1.215686276555061340e-01 1.215686276555061340e-01 1.215686276555061340e-01 1.000000000000000000e+00 -1.176470592617988586e-01 1.176470592617988586e-01 1.176470592617988586e-01 1.000000000000000000e+00 -1.137254908680915833e-01 1.137254908680915833e-01 1.137254908680915833e-01 1.000000000000000000e+00 -1.098039224743843079e-01 1.098039224743843079e-01 1.098039224743843079e-01 1.000000000000000000e+00 -1.058823540806770325e-01 1.058823540806770325e-01 1.058823540806770325e-01 1.000000000000000000e+00 -1.019607856869697571e-01 1.019607856869697571e-01 1.019607856869697571e-01 1.000000000000000000e+00 -9.803921729326248169e-02 9.803921729326248169e-02 9.803921729326248169e-02 1.000000000000000000e+00 -9.411764889955520630e-02 9.411764889955520630e-02 9.411764889955520630e-02 1.000000000000000000e+00 -9.019608050584793091e-02 9.019608050584793091e-02 9.019608050584793091e-02 1.000000000000000000e+00 -8.627451211214065552e-02 8.627451211214065552e-02 8.627451211214065552e-02 1.000000000000000000e+00 -8.235294371843338013e-02 8.235294371843338013e-02 8.235294371843338013e-02 1.000000000000000000e+00 -7.843137532472610474e-02 7.843137532472610474e-02 7.843137532472610474e-02 1.000000000000000000e+00 -7.450980693101882935e-02 7.450980693101882935e-02 7.450980693101882935e-02 1.000000000000000000e+00 -7.058823853731155396e-02 7.058823853731155396e-02 7.058823853731155396e-02 1.000000000000000000e+00 -6.666667014360427856e-02 6.666667014360427856e-02 6.666667014360427856e-02 1.000000000000000000e+00 -6.274510174989700317e-02 6.274510174989700317e-02 6.274510174989700317e-02 1.000000000000000000e+00 -5.882352963089942932e-02 5.882352963089942932e-02 5.882352963089942932e-02 1.000000000000000000e+00 -5.490196123719215393e-02 5.490196123719215393e-02 5.490196123719215393e-02 1.000000000000000000e+00 -5.098039284348487854e-02 5.098039284348487854e-02 5.098039284348487854e-02 1.000000000000000000e+00 -4.705882444977760315e-02 4.705882444977760315e-02 4.705882444977760315e-02 1.000000000000000000e+00 -4.313725605607032776e-02 4.313725605607032776e-02 4.313725605607032776e-02 1.000000000000000000e+00 -3.921568766236305237e-02 3.921568766236305237e-02 3.921568766236305237e-02 1.000000000000000000e+00 -3.529411926865577698e-02 3.529411926865577698e-02 3.529411926865577698e-02 1.000000000000000000e+00 -3.137255087494850159e-02 3.137255087494850159e-02 3.137255087494850159e-02 1.000000000000000000e+00 -2.745098061859607697e-02 2.745098061859607697e-02 2.745098061859607697e-02 1.000000000000000000e+00 -2.352941222488880157e-02 2.352941222488880157e-02 2.352941222488880157e-02 1.000000000000000000e+00 -1.960784383118152618e-02 1.960784383118152618e-02 1.960784383118152618e-02 1.000000000000000000e+00 -1.568627543747425079e-02 1.568627543747425079e-02 1.568627543747425079e-02 1.000000000000000000e+00 -1.176470611244440079e-02 1.176470611244440079e-02 1.176470611244440079e-02 1.000000000000000000e+00 -7.843137718737125397e-03 7.843137718737125397e-03 7.843137718737125397e-03 1.000000000000000000e+00 -3.921568859368562698e-03 3.921568859368562698e-03 3.921568859368562698e-03 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/bone b/fastplotlib/utils/colormaps/bone deleted file mode 100644 index 4dea86b82..000000000 --- a/fastplotlib/utils/colormaps/bone +++ /dev/null @@ -1,256 +0,0 @@ -0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.431372577324509621e-03 3.431371180340647697e-03 4.774083383381366730e-03 1.000000000000000000e+00 -6.862745154649019241e-03 6.862742360681295395e-03 9.548166766762733459e-03 1.000000000000000000e+00 -1.029411796480417252e-02 1.029411330819129944e-02 1.432225108146667480e-02 1.000000000000000000e+00 -1.372549030929803848e-02 1.372548472136259079e-02 1.909633353352546692e-02 1.000000000000000000e+00 -1.715686358511447906e-02 1.715685613453388214e-02 2.387041784822940826e-02 1.000000000000000000e+00 -2.058823592960834503e-02 2.058822661638259888e-02 2.864450216293334961e-02 1.000000000000000000e+00 -2.401960827410221100e-02 2.401959896087646484e-02 3.341858461499214172e-02 1.000000000000000000e+00 -2.745098061859607697e-02 2.745096944272518158e-02 3.819266706705093384e-02 1.000000000000000000e+00 -3.088235296308994293e-02 3.088234178721904755e-02 4.296675324440002441e-02 1.000000000000000000e+00 -3.431372717022895813e-02 3.431371226906776428e-02 4.774083569645881653e-02 1.000000000000000000e+00 -3.774509951472282410e-02 3.774508461356163025e-02 5.251491814851760864e-02 1.000000000000000000e+00 -4.117647185921669006e-02 4.117645323276519775e-02 5.728900432586669922e-02 1.000000000000000000e+00 -4.460784420371055603e-02 4.460782557725906372e-02 6.206308677792549133e-02 1.000000000000000000e+00 -4.803921654820442200e-02 4.803919792175292969e-02 6.683716922998428345e-02 1.000000000000000000e+00 -5.147058889269828796e-02 5.147056654095649719e-02 7.161125540733337402e-02 1.000000000000000000e+00 -5.490196123719215393e-02 5.490193888545036316e-02 7.638533413410186768e-02 1.000000000000000000e+00 -5.833333358168601990e-02 5.833331122994422913e-02 8.115942031145095825e-02 1.000000000000000000e+00 -6.176470592617988586e-02 6.176468357443809509e-02 8.593350648880004883e-02 1.000000000000000000e+00 -6.519608199596405029e-02 6.519605219364166260e-02 9.070758521556854248e-02 1.000000000000000000e+00 -6.862745434045791626e-02 6.862742453813552856e-02 9.548167139291763306e-02 1.000000000000000000e+00 -7.205882668495178223e-02 7.205879688262939453e-02 1.002557575702667236e-01 1.000000000000000000e+00 -7.549019902944564819e-02 7.549016922712326050e-02 1.050298362970352173e-01 1.000000000000000000e+00 -7.892157137393951416e-02 7.892153412103652954e-02 1.098039224743843079e-01 1.000000000000000000e+00 -8.235294371843338013e-02 8.235290646553039551e-02 1.145780086517333984e-01 1.000000000000000000e+00 -8.578431606292724609e-02 8.578427881002426147e-02 1.193520873785018921e-01 1.000000000000000000e+00 -8.921568840742111206e-02 8.921565115451812744e-02 1.241261735558509827e-01 1.000000000000000000e+00 -9.264706075191497803e-02 9.264702349901199341e-02 1.289002597332000732e-01 1.000000000000000000e+00 -9.607843309640884399e-02 9.607839584350585938e-02 1.336743384599685669e-01 1.000000000000000000e+00 -9.950980544090270996e-02 9.950976818799972534e-02 1.384484171867370605e-01 1.000000000000000000e+00 -1.029411777853965759e-01 1.029411330819129944e-01 1.432225108146667480e-01 1.000000000000000000e+00 -1.063725501298904419e-01 1.063725054264068604e-01 1.479965895414352417e-01 1.000000000000000000e+00 -1.098039224743843079e-01 1.098038777709007263e-01 1.527706682682037354e-01 1.000000000000000000e+00 -1.132352948188781738e-01 1.132352501153945923e-01 1.575447618961334229e-01 1.000000000000000000e+00 -1.166666671633720398e-01 1.166666224598884583e-01 1.623188406229019165e-01 1.000000000000000000e+00 -1.200980395078659058e-01 1.200979948043823242e-01 1.670929193496704102e-01 1.000000000000000000e+00 -1.235294118523597717e-01 1.235293671488761902e-01 1.718670129776000977e-01 1.000000000000000000e+00 -1.269607841968536377e-01 1.269607394933700562e-01 1.766410917043685913e-01 1.000000000000000000e+00 -1.303921639919281006e-01 1.303921043872833252e-01 1.814151704311370850e-01 1.000000000000000000e+00 -1.338235288858413696e-01 1.338234841823577881e-01 1.861892640590667725e-01 1.000000000000000000e+00 -1.372549086809158325e-01 1.372548490762710571e-01 1.909633427858352661e-01 1.000000000000000000e+00 -1.406862735748291016e-01 1.406862139701843262e-01 1.957374215126037598e-01 1.000000000000000000e+00 -1.441176533699035645e-01 1.441175937652587891e-01 2.005115151405334473e-01 1.000000000000000000e+00 -1.475490182638168335e-01 1.475489586591720581e-01 2.052855938673019409e-01 1.000000000000000000e+00 -1.509803980588912964e-01 1.509803384542465210e-01 2.100596725940704346e-01 1.000000000000000000e+00 -1.544117629528045654e-01 1.544117033481597900e-01 2.148337662220001221e-01 1.000000000000000000e+00 -1.578431427478790283e-01 1.578430682420730591e-01 2.196078449487686157e-01 1.000000000000000000e+00 -1.612745076417922974e-01 1.612744480371475220e-01 2.243819236755371094e-01 1.000000000000000000e+00 -1.647058874368667603e-01 1.647058129310607910e-01 2.291560173034667969e-01 1.000000000000000000e+00 -1.681372523307800293e-01 1.681371927261352539e-01 2.339300960302352905e-01 1.000000000000000000e+00 -1.715686321258544922e-01 1.715685576200485229e-01 2.387041747570037842e-01 1.000000000000000000e+00 -1.749999970197677612e-01 1.749999374151229858e-01 2.434782534837722778e-01 1.000000000000000000e+00 -1.784313768148422241e-01 1.784313023090362549e-01 2.482523471117019653e-01 1.000000000000000000e+00 -1.818627417087554932e-01 1.818626672029495239e-01 2.530264258384704590e-01 1.000000000000000000e+00 -1.852941215038299561e-01 1.852940469980239868e-01 2.578005194664001465e-01 1.000000000000000000e+00 -1.887254863977432251e-01 1.887254118919372559e-01 2.625745832920074463e-01 1.000000000000000000e+00 -1.921568661928176880e-01 1.921567916870117188e-01 2.673486769199371338e-01 1.000000000000000000e+00 -1.955882310867309570e-01 1.955881565809249878e-01 2.721227705478668213e-01 1.000000000000000000e+00 -1.990196108818054199e-01 1.990195363759994507e-01 2.768968343734741211e-01 1.000000000000000000e+00 -2.024509757757186890e-01 2.024509012699127197e-01 2.816709280014038086e-01 1.000000000000000000e+00 -2.058823555707931519e-01 2.058822661638259888e-01 2.864450216293334961e-01 1.000000000000000000e+00 -2.093137204647064209e-01 2.093136459589004517e-01 2.912190854549407959e-01 1.000000000000000000e+00 -2.127451002597808838e-01 2.127450108528137207e-01 2.959931790828704834e-01 1.000000000000000000e+00 -2.161764651536941528e-01 2.161763906478881836e-01 3.007672727108001709e-01 1.000000000000000000e+00 -2.196078449487686157e-01 2.196077555418014526e-01 3.055413365364074707e-01 1.000000000000000000e+00 -2.230392098426818848e-01 2.230391353368759155e-01 3.103154301643371582e-01 1.000000000000000000e+00 -2.264705896377563477e-01 2.264705002307891846e-01 3.150895237922668457e-01 1.000000000000000000e+00 -2.299019545316696167e-01 2.299018651247024536e-01 3.198635876178741455e-01 1.000000000000000000e+00 -2.333333343267440796e-01 2.333332449197769165e-01 3.246376812458038330e-01 1.000000000000000000e+00 -2.367646992206573486e-01 2.367646098136901855e-01 3.294117748737335205e-01 1.000000000000000000e+00 -2.401960790157318115e-01 2.401959896087646484e-01 3.341858386993408203e-01 1.000000000000000000e+00 -2.436274439096450806e-01 2.436273545026779175e-01 3.389599323272705078e-01 1.000000000000000000e+00 -2.470588237047195435e-01 2.470587342977523804e-01 3.437340259552001953e-01 1.000000000000000000e+00 -2.504901885986328125e-01 2.504900991916656494e-01 3.485080897808074951e-01 1.000000000000000000e+00 -2.539215683937072754e-01 2.539214789867401123e-01 3.532821834087371826e-01 1.000000000000000000e+00 -2.573529481887817383e-01 2.573528289794921875e-01 3.580562770366668701e-01 1.000000000000000000e+00 -2.607843279838562012e-01 2.607842087745666504e-01 3.628303408622741699e-01 1.000000000000000000e+00 -2.642156779766082764e-01 2.642155885696411133e-01 3.676044344902038574e-01 1.000000000000000000e+00 -2.676470577716827393e-01 2.676469683647155762e-01 3.723785281181335449e-01 1.000000000000000000e+00 -2.710784375667572021e-01 2.710783183574676514e-01 3.771525919437408447e-01 1.000000000000000000e+00 -2.745098173618316650e-01 2.745096981525421143e-01 3.819266855716705322e-01 1.000000000000000000e+00 -2.779411673545837402e-01 2.779410779476165771e-01 3.867007791996002197e-01 1.000000000000000000e+00 -2.813725471496582031e-01 2.813724279403686523e-01 3.914748430252075195e-01 1.000000000000000000e+00 -2.848039269447326660e-01 2.848038077354431152e-01 3.962489366531372070e-01 1.000000000000000000e+00 -2.882353067398071289e-01 2.882351875305175781e-01 4.010230302810668945e-01 1.000000000000000000e+00 -2.916666567325592041e-01 2.916665673255920410e-01 4.057970941066741943e-01 1.000000000000000000e+00 -2.950980365276336670e-01 2.950979173183441162e-01 4.105711877346038818e-01 1.000000000000000000e+00 -2.985294163227081299e-01 2.985292971134185791e-01 4.153452813625335693e-01 1.000000000000000000e+00 -3.019607961177825928e-01 3.019606769084930420e-01 4.201193451881408691e-01 1.000000000000000000e+00 -3.053921461105346680e-01 3.053920269012451172e-01 4.248934388160705566e-01 1.000000000000000000e+00 -3.088235259056091309e-01 3.088234066963195801e-01 4.296675324440002441e-01 1.000000000000000000e+00 -3.122549057006835938e-01 3.122547864913940430e-01 4.344415962696075439e-01 1.000000000000000000e+00 -3.156862854957580566e-01 3.156861364841461182e-01 4.392156898975372314e-01 1.000000000000000000e+00 -3.191176354885101318e-01 3.191175162792205811e-01 4.439897835254669189e-01 1.000000000000000000e+00 -3.225490152835845947e-01 3.237132430076599121e-01 4.475488960742950439e-01 1.000000000000000000e+00 -3.259803950786590576e-01 3.284313678741455078e-01 4.509802758693695068e-01 1.000000000000000000e+00 -3.294117748737335205e-01 3.331494927406311035e-01 4.544116556644439697e-01 1.000000000000000000e+00 -3.328431248664855957e-01 3.378676474094390869e-01 4.578430056571960449e-01 1.000000000000000000e+00 -3.362745046615600586e-01 3.425857722759246826e-01 4.612743854522705078e-01 1.000000000000000000e+00 -3.397058844566345215e-01 3.473038971424102783e-01 4.647057652473449707e-01 1.000000000000000000e+00 -3.431372642517089844e-01 3.520220518112182617e-01 4.681371450424194336e-01 1.000000000000000000e+00 -3.465686142444610596e-01 3.567401766777038574e-01 4.715684950351715088e-01 1.000000000000000000e+00 -3.499999940395355225e-01 3.614583313465118408e-01 4.749998748302459717e-01 1.000000000000000000e+00 -3.534313738346099854e-01 3.661764562129974365e-01 4.784312546253204346e-01 1.000000000000000000e+00 -3.568627536296844482e-01 3.708945810794830322e-01 4.818626344203948975e-01 1.000000000000000000e+00 -3.602941036224365234e-01 3.756127357482910156e-01 4.852940142154693604e-01 1.000000000000000000e+00 -3.637254834175109863e-01 3.803308606147766113e-01 4.887253642082214355e-01 1.000000000000000000e+00 -3.671568632125854492e-01 3.850490152835845947e-01 4.921567440032958984e-01 1.000000000000000000e+00 -3.705882430076599121e-01 3.897671401500701904e-01 4.955881237983703613e-01 1.000000000000000000e+00 -3.740195930004119873e-01 3.944852650165557861e-01 4.990195035934448242e-01 1.000000000000000000e+00 -3.774509727954864502e-01 3.992034196853637695e-01 5.024508833885192871e-01 1.000000000000000000e+00 -3.808823525905609131e-01 4.039215445518493652e-01 5.058822631835937500e-01 1.000000000000000000e+00 -3.843137323856353760e-01 4.086396992206573486e-01 5.093136429786682129e-01 1.000000000000000000e+00 -3.877451121807098389e-01 4.133578240871429443e-01 5.127449631690979004e-01 1.000000000000000000e+00 -3.911764621734619141e-01 4.180759489536285400e-01 5.161763429641723633e-01 1.000000000000000000e+00 -3.946078419685363770e-01 4.227941036224365234e-01 5.196077227592468262e-01 1.000000000000000000e+00 -3.980392217636108398e-01 4.275122284889221191e-01 5.230391025543212891e-01 1.000000000000000000e+00 -4.014706015586853027e-01 4.322303533554077148e-01 5.264704823493957520e-01 1.000000000000000000e+00 -4.049019515514373779e-01 4.369485080242156982e-01 5.299018621444702148e-01 1.000000000000000000e+00 -4.083333313465118408e-01 4.416666328907012939e-01 5.333332419395446777e-01 1.000000000000000000e+00 -4.117647111415863037e-01 4.463847875595092773e-01 5.367646217346191406e-01 1.000000000000000000e+00 -4.151960909366607666e-01 4.511029124259948730e-01 5.401960015296936035e-01 1.000000000000000000e+00 -4.186274409294128418e-01 4.558210372924804688e-01 5.436273217201232910e-01 1.000000000000000000e+00 -4.220588207244873047e-01 4.605391919612884521e-01 5.470587015151977539e-01 1.000000000000000000e+00 -4.254902005195617676e-01 4.652573168277740479e-01 5.504900813102722168e-01 1.000000000000000000e+00 -4.289215803146362305e-01 4.699754714965820312e-01 5.539214611053466797e-01 1.000000000000000000e+00 -4.323529303073883057e-01 4.746935963630676270e-01 5.573528409004211426e-01 1.000000000000000000e+00 -4.357843101024627686e-01 4.794117212295532227e-01 5.607842206954956055e-01 1.000000000000000000e+00 -4.392156898975372314e-01 4.841298758983612061e-01 5.642156004905700684e-01 1.000000000000000000e+00 -4.426470696926116943e-01 4.888480007648468018e-01 5.676469802856445312e-01 1.000000000000000000e+00 -4.460784196853637695e-01 4.935661554336547852e-01 5.710783600807189941e-01 1.000000000000000000e+00 -4.495097994804382324e-01 4.982842803001403809e-01 5.745096802711486816e-01 1.000000000000000000e+00 -4.529411792755126953e-01 5.030024051666259766e-01 5.779410600662231445e-01 1.000000000000000000e+00 -4.563725590705871582e-01 5.077205300331115723e-01 5.813724398612976074e-01 1.000000000000000000e+00 -4.598039090633392334e-01 5.124387145042419434e-01 5.848038196563720703e-01 1.000000000000000000e+00 -4.632352888584136963e-01 5.171568393707275391e-01 5.882351994514465332e-01 1.000000000000000000e+00 -4.666666686534881592e-01 5.218749642372131348e-01 5.916665792465209961e-01 1.000000000000000000e+00 -4.700980484485626221e-01 5.265930891036987305e-01 5.950979590415954590e-01 1.000000000000000000e+00 -4.735293984413146973e-01 5.313112139701843262e-01 5.985293388366699219e-01 1.000000000000000000e+00 -4.769607782363891602e-01 5.360293984413146973e-01 6.019607186317443848e-01 1.000000000000000000e+00 -4.803921580314636230e-01 5.407475233078002930e-01 6.053920388221740723e-01 1.000000000000000000e+00 -4.838235378265380859e-01 5.454656481742858887e-01 6.088234186172485352e-01 1.000000000000000000e+00 -4.872548878192901611e-01 5.501837730407714844e-01 6.122547984123229980e-01 1.000000000000000000e+00 -4.906862676143646240e-01 5.549018979072570801e-01 6.156861782073974609e-01 1.000000000000000000e+00 -4.941176474094390869e-01 5.596200227737426758e-01 6.191175580024719238e-01 1.000000000000000000e+00 -4.975490272045135498e-01 5.643382072448730469e-01 6.225489377975463867e-01 1.000000000000000000e+00 -5.009803771972656250e-01 5.690563321113586426e-01 6.259803175926208496e-01 1.000000000000000000e+00 -5.044117569923400879e-01 5.737744569778442383e-01 6.294116973876953125e-01 1.000000000000000000e+00 -5.078431367874145508e-01 5.784925818443298340e-01 6.328430771827697754e-01 1.000000000000000000e+00 -5.112745165824890137e-01 5.832107067108154297e-01 6.362744569778442383e-01 1.000000000000000000e+00 -5.147058963775634766e-01 5.879288911819458008e-01 6.397057771682739258e-01 1.000000000000000000e+00 -5.181372761726379395e-01 5.926470160484313965e-01 6.431371569633483887e-01 1.000000000000000000e+00 -5.215686559677124023e-01 5.973651409149169922e-01 6.465685367584228516e-01 1.000000000000000000e+00 -5.249999761581420898e-01 6.020832657814025879e-01 6.499999165534973145e-01 1.000000000000000000e+00 -5.284313559532165527e-01 6.068013906478881836e-01 6.534312963485717773e-01 1.000000000000000000e+00 -5.318627357482910156e-01 6.115195751190185547e-01 6.568626761436462402e-01 1.000000000000000000e+00 -5.352941155433654785e-01 6.162376999855041504e-01 6.602940559387207031e-01 1.000000000000000000e+00 -5.387254953384399414e-01 6.209558248519897461e-01 6.637254357337951660e-01 1.000000000000000000e+00 -5.421568751335144043e-01 6.256739497184753418e-01 6.671568155288696289e-01 1.000000000000000000e+00 -5.455882549285888672e-01 6.303920745849609375e-01 6.705881357192993164e-01 1.000000000000000000e+00 -5.490196347236633301e-01 6.351102590560913086e-01 6.740195155143737793e-01 1.000000000000000000e+00 -5.524509549140930176e-01 6.398283839225769043e-01 6.774508953094482422e-01 1.000000000000000000e+00 -5.558823347091674805e-01 6.445465087890625000e-01 6.808822751045227051e-01 1.000000000000000000e+00 -5.593137145042419434e-01 6.492646336555480957e-01 6.843136548995971680e-01 1.000000000000000000e+00 -5.627450942993164062e-01 6.539827585220336914e-01 6.877450346946716309e-01 1.000000000000000000e+00 -5.661764740943908691e-01 6.587009429931640625e-01 6.911764144897460938e-01 1.000000000000000000e+00 -5.696078538894653320e-01 6.634190678596496582e-01 6.946077942848205566e-01 1.000000000000000000e+00 -5.730392336845397949e-01 6.681371927261352539e-01 6.980391740798950195e-01 1.000000000000000000e+00 -5.764706134796142578e-01 6.728553175926208496e-01 7.014704942703247070e-01 1.000000000000000000e+00 -5.799019336700439453e-01 6.775734424591064453e-01 7.049018740653991699e-01 1.000000000000000000e+00 -5.833333134651184082e-01 6.822916269302368164e-01 7.083332538604736328e-01 1.000000000000000000e+00 -5.867646932601928711e-01 6.870097517967224121e-01 7.117646336555480957e-01 1.000000000000000000e+00 -5.901960730552673340e-01 6.917278766632080078e-01 7.151960134506225586e-01 1.000000000000000000e+00 -5.936274528503417969e-01 6.964460015296936035e-01 7.186273932456970215e-01 1.000000000000000000e+00 -5.970588326454162598e-01 7.011641263961791992e-01 7.220587730407714844e-01 1.000000000000000000e+00 -6.004902124404907227e-01 7.058823108673095703e-01 7.254901528358459473e-01 1.000000000000000000e+00 -6.039215922355651855e-01 7.106004357337951660e-01 7.289215326309204102e-01 1.000000000000000000e+00 -6.073529124259948730e-01 7.153185606002807617e-01 7.323528528213500977e-01 1.000000000000000000e+00 -6.107842922210693359e-01 7.200366854667663574e-01 7.357842326164245605e-01 1.000000000000000000e+00 -6.142156720161437988e-01 7.247548103332519531e-01 7.392156124114990234e-01 1.000000000000000000e+00 -6.176470518112182617e-01 7.294729351997375488e-01 7.426469922065734863e-01 1.000000000000000000e+00 -6.210784316062927246e-01 7.341911196708679199e-01 7.460783720016479492e-01 1.000000000000000000e+00 -6.245098114013671875e-01 7.389092445373535156e-01 7.495097517967224121e-01 1.000000000000000000e+00 -6.279411911964416504e-01 7.436273694038391113e-01 7.529411315917968750e-01 1.000000000000000000e+00 -6.313725709915161133e-01 7.483454942703247070e-01 7.563725113868713379e-01 1.000000000000000000e+00 -6.348039507865905762e-01 7.530636191368103027e-01 7.598038911819458008e-01 1.000000000000000000e+00 -6.382352709770202637e-01 7.577818036079406738e-01 7.632352113723754883e-01 1.000000000000000000e+00 -6.416666507720947266e-01 7.624999284744262695e-01 7.666665911674499512e-01 1.000000000000000000e+00 -6.450980305671691895e-01 7.672180533409118652e-01 7.700979709625244141e-01 1.000000000000000000e+00 -6.485294103622436523e-01 7.719361782073974609e-01 7.735293507575988770e-01 1.000000000000000000e+00 -6.519607901573181152e-01 7.766543030738830566e-01 7.769607305526733398e-01 1.000000000000000000e+00 -6.568626165390014648e-01 7.803921699523925781e-01 7.803921103477478027e-01 1.000000000000000000e+00 -6.622241139411926270e-01 7.838235497474670410e-01 7.838234901428222656e-01 1.000000000000000000e+00 -6.675856709480285645e-01 7.872549295425415039e-01 7.872548699378967285e-01 1.000000000000000000e+00 -6.729471683502197266e-01 7.906862497329711914e-01 7.906862497329711914e-01 1.000000000000000000e+00 -6.783087253570556641e-01 7.941176295280456543e-01 7.941176295280456543e-01 1.000000000000000000e+00 -6.836702227592468262e-01 7.975490093231201172e-01 7.975489497184753418e-01 1.000000000000000000e+00 -6.890317797660827637e-01 8.009803891181945801e-01 8.009803295135498047e-01 1.000000000000000000e+00 -6.943932771682739258e-01 8.044117689132690430e-01 8.044117093086242676e-01 1.000000000000000000e+00 -6.997547745704650879e-01 8.078431487083435059e-01 8.078430891036987305e-01 1.000000000000000000e+00 -7.051163315773010254e-01 8.112745285034179688e-01 8.112744688987731934e-01 1.000000000000000000e+00 -7.104778289794921875e-01 8.147059082984924316e-01 8.147058486938476562e-01 1.000000000000000000e+00 -7.158393859863281250e-01 8.181372284889221191e-01 8.181372284889221191e-01 1.000000000000000000e+00 -7.212008833885192871e-01 8.215686082839965820e-01 8.215686082839965820e-01 1.000000000000000000e+00 -7.265623807907104492e-01 8.249999880790710449e-01 8.249999880790710449e-01 1.000000000000000000e+00 -7.319239377975463867e-01 8.284313678741455078e-01 8.284313082695007324e-01 1.000000000000000000e+00 -7.372854351997375488e-01 8.318627476692199707e-01 8.318626880645751953e-01 1.000000000000000000e+00 -7.426469922065734863e-01 8.352941274642944336e-01 8.352940678596496582e-01 1.000000000000000000e+00 -7.480084896087646484e-01 8.387255072593688965e-01 8.387254476547241211e-01 1.000000000000000000e+00 -7.533699870109558105e-01 8.421568870544433594e-01 8.421568274497985840e-01 1.000000000000000000e+00 -7.587315440177917480e-01 8.455882072448730469e-01 8.455882072448730469e-01 1.000000000000000000e+00 -7.640930414199829102e-01 8.490195870399475098e-01 8.490195870399475098e-01 1.000000000000000000e+00 -7.694545984268188477e-01 8.524509668350219727e-01 8.524509668350219727e-01 1.000000000000000000e+00 -7.748160958290100098e-01 8.558823466300964355e-01 8.558823466300964355e-01 1.000000000000000000e+00 -7.801775932312011719e-01 8.593137264251708984e-01 8.593136668205261230e-01 1.000000000000000000e+00 -7.855391502380371094e-01 8.627451062202453613e-01 8.627450466156005859e-01 1.000000000000000000e+00 -7.909006476402282715e-01 8.661764860153198242e-01 8.661764264106750488e-01 1.000000000000000000e+00 -7.962622046470642090e-01 8.696078658103942871e-01 8.696078062057495117e-01 1.000000000000000000e+00 -8.016237020492553711e-01 8.730391860008239746e-01 8.730391860008239746e-01 1.000000000000000000e+00 -8.069851994514465332e-01 8.764705657958984375e-01 8.764705657958984375e-01 1.000000000000000000e+00 -8.123467564582824707e-01 8.799019455909729004e-01 8.799019455909729004e-01 1.000000000000000000e+00 -8.177082538604736328e-01 8.833333253860473633e-01 8.833333253860473633e-01 1.000000000000000000e+00 -8.230698108673095703e-01 8.867647051811218262e-01 8.867647051811218262e-01 1.000000000000000000e+00 -8.284313082695007324e-01 8.901960849761962891e-01 8.901960253715515137e-01 1.000000000000000000e+00 -8.337928056716918945e-01 8.936274647712707520e-01 8.936274051666259766e-01 1.000000000000000000e+00 -8.391543626785278320e-01 8.970588445663452148e-01 8.970587849617004395e-01 1.000000000000000000e+00 -8.445158600807189941e-01 9.004902243614196777e-01 9.004901647567749023e-01 1.000000000000000000e+00 -8.498774170875549316e-01 9.039215445518493652e-01 9.039215445518493652e-01 1.000000000000000000e+00 -8.552389144897460938e-01 9.073529243469238281e-01 9.073529243469238281e-01 1.000000000000000000e+00 -8.606004118919372559e-01 9.107843041419982910e-01 9.107843041419982910e-01 1.000000000000000000e+00 -8.659619688987731934e-01 9.142156839370727539e-01 9.142156839370727539e-01 1.000000000000000000e+00 -8.713234663009643555e-01 9.176470637321472168e-01 9.176470637321472168e-01 1.000000000000000000e+00 -8.766850233078002930e-01 9.210784435272216797e-01 9.210783839225769043e-01 1.000000000000000000e+00 -8.820465207099914551e-01 9.245098233222961426e-01 9.245097637176513672e-01 1.000000000000000000e+00 -8.874080181121826172e-01 9.279412031173706055e-01 9.279411435127258301e-01 1.000000000000000000e+00 -8.927695751190185547e-01 9.313725233078002930e-01 9.313725233078002930e-01 1.000000000000000000e+00 -8.981310725212097168e-01 9.348039031028747559e-01 9.348039031028747559e-01 1.000000000000000000e+00 -9.034926295280456543e-01 9.382352828979492188e-01 9.382352828979492188e-01 1.000000000000000000e+00 -9.088541269302368164e-01 9.416666626930236816e-01 9.416666626930236816e-01 1.000000000000000000e+00 -9.142156839370727539e-01 9.450980424880981445e-01 9.450980424880981445e-01 1.000000000000000000e+00 -9.195771813392639160e-01 9.485294222831726074e-01 9.485294222831726074e-01 1.000000000000000000e+00 -9.249386787414550781e-01 9.519608020782470703e-01 9.519608020782470703e-01 1.000000000000000000e+00 -9.303002357482910156e-01 9.553921818733215332e-01 9.553921222686767578e-01 1.000000000000000000e+00 -9.356617331504821777e-01 9.588235020637512207e-01 9.588235020637512207e-01 1.000000000000000000e+00 -9.410232901573181152e-01 9.622548818588256836e-01 9.622548818588256836e-01 1.000000000000000000e+00 -9.463847875595092773e-01 9.656862616539001465e-01 9.656862616539001465e-01 1.000000000000000000e+00 -9.517462849617004395e-01 9.691176414489746094e-01 9.691176414489746094e-01 1.000000000000000000e+00 -9.571078419685363770e-01 9.725490212440490723e-01 9.725490212440490723e-01 1.000000000000000000e+00 -9.624693393707275391e-01 9.759804010391235352e-01 9.759804010391235352e-01 1.000000000000000000e+00 -9.678308963775634766e-01 9.794117808341979980e-01 9.794117808341979980e-01 1.000000000000000000e+00 -9.731923937797546387e-01 9.828431606292724609e-01 9.828431606292724609e-01 1.000000000000000000e+00 -9.785538911819458008e-01 9.862744808197021484e-01 9.862744808197021484e-01 1.000000000000000000e+00 -9.839154481887817383e-01 9.897058606147766113e-01 9.897058606147766113e-01 1.000000000000000000e+00 -9.892769455909729004e-01 9.931372404098510742e-01 9.931372404098510742e-01 1.000000000000000000e+00 -9.946385025978088379e-01 9.965686202049255371e-01 9.965686202049255371e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/brg b/fastplotlib/utils/colormaps/brg deleted file mode 100644 index 8ff668122..000000000 --- a/fastplotlib/utils/colormaps/brg +++ /dev/null @@ -1,256 +0,0 @@ -0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -7.843137718737125397e-03 0.000000000000000000e+00 9.921568632125854492e-01 1.000000000000000000e+00 -1.568627543747425079e-02 0.000000000000000000e+00 9.843137264251708984e-01 1.000000000000000000e+00 -2.352941222488880157e-02 0.000000000000000000e+00 9.764705896377563477e-01 1.000000000000000000e+00 -3.137255087494850159e-02 0.000000000000000000e+00 9.686274528503417969e-01 1.000000000000000000e+00 -3.921568766236305237e-02 0.000000000000000000e+00 9.607843160629272461e-01 1.000000000000000000e+00 -4.705882444977760315e-02 0.000000000000000000e+00 9.529411792755126953e-01 1.000000000000000000e+00 -5.490196123719215393e-02 0.000000000000000000e+00 9.450980424880981445e-01 1.000000000000000000e+00 -6.274510174989700317e-02 0.000000000000000000e+00 9.372549057006835938e-01 1.000000000000000000e+00 -7.058823853731155396e-02 0.000000000000000000e+00 9.294117689132690430e-01 1.000000000000000000e+00 -7.843137532472610474e-02 0.000000000000000000e+00 9.215686321258544922e-01 1.000000000000000000e+00 -8.627451211214065552e-02 0.000000000000000000e+00 9.137254953384399414e-01 1.000000000000000000e+00 -9.411764889955520630e-02 0.000000000000000000e+00 9.058823585510253906e-01 1.000000000000000000e+00 -1.019607856869697571e-01 0.000000000000000000e+00 8.980392217636108398e-01 1.000000000000000000e+00 -1.098039224743843079e-01 0.000000000000000000e+00 8.901960849761962891e-01 1.000000000000000000e+00 -1.176470592617988586e-01 0.000000000000000000e+00 8.823529481887817383e-01 1.000000000000000000e+00 -1.254902034997940063e-01 0.000000000000000000e+00 8.745098114013671875e-01 1.000000000000000000e+00 -1.333333402872085571e-01 0.000000000000000000e+00 8.666666746139526367e-01 1.000000000000000000e+00 -1.411764770746231079e-01 0.000000000000000000e+00 8.588235378265380859e-01 1.000000000000000000e+00 -1.490196138620376587e-01 0.000000000000000000e+00 8.509804010391235352e-01 1.000000000000000000e+00 -1.568627506494522095e-01 0.000000000000000000e+00 8.431372642517089844e-01 1.000000000000000000e+00 -1.647058874368667603e-01 0.000000000000000000e+00 8.352941274642944336e-01 1.000000000000000000e+00 -1.725490242242813110e-01 0.000000000000000000e+00 8.274509906768798828e-01 1.000000000000000000e+00 -1.803921610116958618e-01 0.000000000000000000e+00 8.196078538894653320e-01 1.000000000000000000e+00 -1.882352977991104126e-01 0.000000000000000000e+00 8.117647171020507812e-01 1.000000000000000000e+00 -1.960784345865249634e-01 0.000000000000000000e+00 8.039215803146362305e-01 1.000000000000000000e+00 -2.039215713739395142e-01 0.000000000000000000e+00 7.960784435272216797e-01 1.000000000000000000e+00 -2.117647081613540649e-01 0.000000000000000000e+00 7.882353067398071289e-01 1.000000000000000000e+00 -2.196078449487686157e-01 0.000000000000000000e+00 7.803921699523925781e-01 1.000000000000000000e+00 -2.274509817361831665e-01 0.000000000000000000e+00 7.725490331649780273e-01 1.000000000000000000e+00 -2.352941185235977173e-01 0.000000000000000000e+00 7.647058963775634766e-01 1.000000000000000000e+00 -2.431372553110122681e-01 0.000000000000000000e+00 7.568627595901489258e-01 1.000000000000000000e+00 -2.509804069995880127e-01 0.000000000000000000e+00 7.490196228027343750e-01 1.000000000000000000e+00 -2.588235437870025635e-01 0.000000000000000000e+00 7.411764860153198242e-01 1.000000000000000000e+00 -2.666666805744171143e-01 0.000000000000000000e+00 7.333333492279052734e-01 1.000000000000000000e+00 -2.745098173618316650e-01 0.000000000000000000e+00 7.254902124404907227e-01 1.000000000000000000e+00 -2.823529541492462158e-01 0.000000000000000000e+00 7.176470756530761719e-01 1.000000000000000000e+00 -2.901960909366607666e-01 0.000000000000000000e+00 7.098039388656616211e-01 1.000000000000000000e+00 -2.980392277240753174e-01 0.000000000000000000e+00 7.019608020782470703e-01 1.000000000000000000e+00 -3.058823645114898682e-01 0.000000000000000000e+00 6.941176652908325195e-01 1.000000000000000000e+00 -3.137255012989044189e-01 0.000000000000000000e+00 6.862745285034179688e-01 1.000000000000000000e+00 -3.215686380863189697e-01 0.000000000000000000e+00 6.784313917160034180e-01 1.000000000000000000e+00 -3.294117748737335205e-01 0.000000000000000000e+00 6.705882549285888672e-01 1.000000000000000000e+00 -3.372549116611480713e-01 0.000000000000000000e+00 6.627451181411743164e-01 1.000000000000000000e+00 -3.450980484485626221e-01 0.000000000000000000e+00 6.549019813537597656e-01 1.000000000000000000e+00 -3.529411852359771729e-01 0.000000000000000000e+00 6.470588445663452148e-01 1.000000000000000000e+00 -3.607843220233917236e-01 0.000000000000000000e+00 6.392157077789306641e-01 1.000000000000000000e+00 -3.686274588108062744e-01 0.000000000000000000e+00 6.313725709915161133e-01 1.000000000000000000e+00 -3.764705955982208252e-01 0.000000000000000000e+00 6.235294342041015625e-01 1.000000000000000000e+00 -3.843137323856353760e-01 0.000000000000000000e+00 6.156862974166870117e-01 1.000000000000000000e+00 -3.921568691730499268e-01 0.000000000000000000e+00 6.078431606292724609e-01 1.000000000000000000e+00 -4.000000059604644775e-01 0.000000000000000000e+00 6.000000238418579102e-01 1.000000000000000000e+00 -4.078431427478790283e-01 0.000000000000000000e+00 5.921568870544433594e-01 1.000000000000000000e+00 -4.156862795352935791e-01 0.000000000000000000e+00 5.843137502670288086e-01 1.000000000000000000e+00 -4.235294163227081299e-01 0.000000000000000000e+00 5.764706134796142578e-01 1.000000000000000000e+00 -4.313725531101226807e-01 0.000000000000000000e+00 5.686274766921997070e-01 1.000000000000000000e+00 -4.392156898975372314e-01 0.000000000000000000e+00 5.607843399047851562e-01 1.000000000000000000e+00 -4.470588266849517822e-01 0.000000000000000000e+00 5.529412031173706055e-01 1.000000000000000000e+00 -4.549019634723663330e-01 0.000000000000000000e+00 5.450980663299560547e-01 1.000000000000000000e+00 -4.627451002597808838e-01 0.000000000000000000e+00 5.372549295425415039e-01 1.000000000000000000e+00 -4.705882370471954346e-01 0.000000000000000000e+00 5.294117927551269531e-01 1.000000000000000000e+00 -4.784313738346099854e-01 0.000000000000000000e+00 5.215686559677124023e-01 1.000000000000000000e+00 -4.862745106220245361e-01 0.000000000000000000e+00 5.137255191802978516e-01 1.000000000000000000e+00 -4.941176474094390869e-01 0.000000000000000000e+00 5.058823823928833008e-01 1.000000000000000000e+00 -5.019608139991760254e-01 0.000000000000000000e+00 4.980392158031463623e-01 1.000000000000000000e+00 -5.098039507865905762e-01 0.000000000000000000e+00 4.901960790157318115e-01 1.000000000000000000e+00 -5.176470875740051270e-01 0.000000000000000000e+00 4.823529422283172607e-01 1.000000000000000000e+00 -5.254902243614196777e-01 0.000000000000000000e+00 4.745098054409027100e-01 1.000000000000000000e+00 -5.333333611488342285e-01 0.000000000000000000e+00 4.666666686534881592e-01 1.000000000000000000e+00 -5.411764979362487793e-01 0.000000000000000000e+00 4.588235318660736084e-01 1.000000000000000000e+00 -5.490196347236633301e-01 0.000000000000000000e+00 4.509803950786590576e-01 1.000000000000000000e+00 -5.568627715110778809e-01 0.000000000000000000e+00 4.431372582912445068e-01 1.000000000000000000e+00 -5.647059082984924316e-01 0.000000000000000000e+00 4.352941215038299561e-01 1.000000000000000000e+00 -5.725490450859069824e-01 0.000000000000000000e+00 4.274509847164154053e-01 1.000000000000000000e+00 -5.803921818733215332e-01 0.000000000000000000e+00 4.196078479290008545e-01 1.000000000000000000e+00 -5.882353186607360840e-01 0.000000000000000000e+00 4.117647111415863037e-01 1.000000000000000000e+00 -5.960784554481506348e-01 0.000000000000000000e+00 4.039215743541717529e-01 1.000000000000000000e+00 -6.039215922355651855e-01 0.000000000000000000e+00 3.960784375667572021e-01 1.000000000000000000e+00 -6.117647290229797363e-01 0.000000000000000000e+00 3.882353007793426514e-01 1.000000000000000000e+00 -6.196078658103942871e-01 0.000000000000000000e+00 3.803921639919281006e-01 1.000000000000000000e+00 -6.274510025978088379e-01 0.000000000000000000e+00 3.725490272045135498e-01 1.000000000000000000e+00 -6.352941393852233887e-01 0.000000000000000000e+00 3.647058904170989990e-01 1.000000000000000000e+00 -6.431372761726379395e-01 0.000000000000000000e+00 3.568627536296844482e-01 1.000000000000000000e+00 -6.509804129600524902e-01 0.000000000000000000e+00 3.490196168422698975e-01 1.000000000000000000e+00 -6.588235497474670410e-01 0.000000000000000000e+00 3.411764800548553467e-01 1.000000000000000000e+00 -6.666666865348815918e-01 0.000000000000000000e+00 3.333333432674407959e-01 1.000000000000000000e+00 -6.745098233222961426e-01 0.000000000000000000e+00 3.254902064800262451e-01 1.000000000000000000e+00 -6.823529601097106934e-01 0.000000000000000000e+00 3.176470696926116943e-01 1.000000000000000000e+00 -6.901960968971252441e-01 0.000000000000000000e+00 3.098039329051971436e-01 1.000000000000000000e+00 -6.980392336845397949e-01 0.000000000000000000e+00 3.019607961177825928e-01 1.000000000000000000e+00 -7.058823704719543457e-01 0.000000000000000000e+00 2.941176593303680420e-01 1.000000000000000000e+00 -7.137255072593688965e-01 0.000000000000000000e+00 2.862745225429534912e-01 1.000000000000000000e+00 -7.215686440467834473e-01 0.000000000000000000e+00 2.784313857555389404e-01 1.000000000000000000e+00 -7.294117808341979980e-01 0.000000000000000000e+00 2.705882489681243896e-01 1.000000000000000000e+00 -7.372549176216125488e-01 0.000000000000000000e+00 2.627451121807098389e-01 1.000000000000000000e+00 -7.450980544090270996e-01 0.000000000000000000e+00 2.549019753932952881e-01 1.000000000000000000e+00 -7.529411911964416504e-01 0.000000000000000000e+00 2.470588237047195435e-01 1.000000000000000000e+00 -7.607843279838562012e-01 0.000000000000000000e+00 2.392156869173049927e-01 1.000000000000000000e+00 -7.686274647712707520e-01 0.000000000000000000e+00 2.313725501298904419e-01 1.000000000000000000e+00 -7.764706015586853027e-01 0.000000000000000000e+00 2.235294133424758911e-01 1.000000000000000000e+00 -7.843137383460998535e-01 0.000000000000000000e+00 2.156862765550613403e-01 1.000000000000000000e+00 -7.921568751335144043e-01 0.000000000000000000e+00 2.078431397676467896e-01 1.000000000000000000e+00 -8.000000119209289551e-01 0.000000000000000000e+00 2.000000029802322388e-01 1.000000000000000000e+00 -8.078431487083435059e-01 0.000000000000000000e+00 1.921568661928176880e-01 1.000000000000000000e+00 -8.156862854957580566e-01 0.000000000000000000e+00 1.843137294054031372e-01 1.000000000000000000e+00 -8.235294222831726074e-01 0.000000000000000000e+00 1.764705926179885864e-01 1.000000000000000000e+00 -8.313725590705871582e-01 0.000000000000000000e+00 1.686274558305740356e-01 1.000000000000000000e+00 -8.392156958580017090e-01 0.000000000000000000e+00 1.607843190431594849e-01 1.000000000000000000e+00 -8.470588326454162598e-01 0.000000000000000000e+00 1.529411822557449341e-01 1.000000000000000000e+00 -8.549019694328308105e-01 0.000000000000000000e+00 1.450980454683303833e-01 1.000000000000000000e+00 -8.627451062202453613e-01 0.000000000000000000e+00 1.372549086809158325e-01 1.000000000000000000e+00 -8.705882430076599121e-01 0.000000000000000000e+00 1.294117718935012817e-01 1.000000000000000000e+00 -8.784313797950744629e-01 0.000000000000000000e+00 1.215686276555061340e-01 1.000000000000000000e+00 -8.862745165824890137e-01 0.000000000000000000e+00 1.137254908680915833e-01 1.000000000000000000e+00 -8.941176533699035645e-01 0.000000000000000000e+00 1.058823540806770325e-01 1.000000000000000000e+00 -9.019607901573181152e-01 0.000000000000000000e+00 9.803921729326248169e-02 1.000000000000000000e+00 -9.098039269447326660e-01 0.000000000000000000e+00 9.019608050584793091e-02 1.000000000000000000e+00 -9.176470637321472168e-01 0.000000000000000000e+00 8.235294371843338013e-02 1.000000000000000000e+00 -9.254902005195617676e-01 0.000000000000000000e+00 7.450980693101882935e-02 1.000000000000000000e+00 -9.333333373069763184e-01 0.000000000000000000e+00 6.666667014360427856e-02 1.000000000000000000e+00 -9.411764740943908691e-01 0.000000000000000000e+00 5.882352963089942932e-02 1.000000000000000000e+00 -9.490196108818054199e-01 0.000000000000000000e+00 5.098039284348487854e-02 1.000000000000000000e+00 -9.568627476692199707e-01 0.000000000000000000e+00 4.313725605607032776e-02 1.000000000000000000e+00 -9.647058844566345215e-01 0.000000000000000000e+00 3.529411926865577698e-02 1.000000000000000000e+00 -9.725490212440490723e-01 0.000000000000000000e+00 2.745098061859607697e-02 1.000000000000000000e+00 -9.803921580314636230e-01 0.000000000000000000e+00 1.960784383118152618e-02 1.000000000000000000e+00 -9.882352948188781738e-01 0.000000000000000000e+00 1.176470611244440079e-02 1.000000000000000000e+00 -9.960784316062927246e-01 0.000000000000000000e+00 3.921568859368562698e-03 1.000000000000000000e+00 -9.960784316062927246e-01 3.921568859368562698e-03 0.000000000000000000e+00 1.000000000000000000e+00 -9.882352948188781738e-01 1.176470611244440079e-02 0.000000000000000000e+00 1.000000000000000000e+00 -9.803921580314636230e-01 1.960784383118152618e-02 0.000000000000000000e+00 1.000000000000000000e+00 -9.725490212440490723e-01 2.745098061859607697e-02 0.000000000000000000e+00 1.000000000000000000e+00 -9.647058844566345215e-01 3.529411926865577698e-02 0.000000000000000000e+00 1.000000000000000000e+00 -9.568627476692199707e-01 4.313725605607032776e-02 0.000000000000000000e+00 1.000000000000000000e+00 -9.490196108818054199e-01 5.098039284348487854e-02 0.000000000000000000e+00 1.000000000000000000e+00 -9.411764740943908691e-01 5.882352963089942932e-02 0.000000000000000000e+00 1.000000000000000000e+00 -9.333333373069763184e-01 6.666667014360427856e-02 0.000000000000000000e+00 1.000000000000000000e+00 -9.254902005195617676e-01 7.450980693101882935e-02 0.000000000000000000e+00 1.000000000000000000e+00 -9.176470637321472168e-01 8.235294371843338013e-02 0.000000000000000000e+00 1.000000000000000000e+00 -9.098039269447326660e-01 9.019608050584793091e-02 0.000000000000000000e+00 1.000000000000000000e+00 -9.019607901573181152e-01 9.803921729326248169e-02 0.000000000000000000e+00 1.000000000000000000e+00 -8.941176533699035645e-01 1.058823540806770325e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.862745165824890137e-01 1.137254908680915833e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.784313797950744629e-01 1.215686276555061340e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.705882430076599121e-01 1.294117718935012817e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.627451062202453613e-01 1.372549086809158325e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.549019694328308105e-01 1.450980454683303833e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.470588326454162598e-01 1.529411822557449341e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.392156958580017090e-01 1.607843190431594849e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.313725590705871582e-01 1.686274558305740356e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.235294222831726074e-01 1.764705926179885864e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.156862854957580566e-01 1.843137294054031372e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.078431487083435059e-01 1.921568661928176880e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.000000119209289551e-01 2.000000029802322388e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.921568751335144043e-01 2.078431397676467896e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.843137383460998535e-01 2.156862765550613403e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.764706015586853027e-01 2.235294133424758911e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.686274647712707520e-01 2.313725501298904419e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.607843279838562012e-01 2.392156869173049927e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.529411911964416504e-01 2.470588237047195435e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.450980544090270996e-01 2.549019753932952881e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.372549176216125488e-01 2.627451121807098389e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.294117808341979980e-01 2.705882489681243896e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.215686440467834473e-01 2.784313857555389404e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.137255072593688965e-01 2.862745225429534912e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.058823704719543457e-01 2.941176593303680420e-01 0.000000000000000000e+00 1.000000000000000000e+00 -6.980392336845397949e-01 3.019607961177825928e-01 0.000000000000000000e+00 1.000000000000000000e+00 -6.901960968971252441e-01 3.098039329051971436e-01 0.000000000000000000e+00 1.000000000000000000e+00 -6.823529601097106934e-01 3.176470696926116943e-01 0.000000000000000000e+00 1.000000000000000000e+00 -6.745098233222961426e-01 3.254902064800262451e-01 0.000000000000000000e+00 1.000000000000000000e+00 -6.666666865348815918e-01 3.333333432674407959e-01 0.000000000000000000e+00 1.000000000000000000e+00 -6.588235497474670410e-01 3.411764800548553467e-01 0.000000000000000000e+00 1.000000000000000000e+00 -6.509804129600524902e-01 3.490196168422698975e-01 0.000000000000000000e+00 1.000000000000000000e+00 -6.431372761726379395e-01 3.568627536296844482e-01 0.000000000000000000e+00 1.000000000000000000e+00 -6.352941393852233887e-01 3.647058904170989990e-01 0.000000000000000000e+00 1.000000000000000000e+00 -6.274510025978088379e-01 3.725490272045135498e-01 0.000000000000000000e+00 1.000000000000000000e+00 -6.196078658103942871e-01 3.803921639919281006e-01 0.000000000000000000e+00 1.000000000000000000e+00 -6.117647290229797363e-01 3.882353007793426514e-01 0.000000000000000000e+00 1.000000000000000000e+00 -6.039215922355651855e-01 3.960784375667572021e-01 0.000000000000000000e+00 1.000000000000000000e+00 -5.960784554481506348e-01 4.039215743541717529e-01 0.000000000000000000e+00 1.000000000000000000e+00 -5.882353186607360840e-01 4.117647111415863037e-01 0.000000000000000000e+00 1.000000000000000000e+00 -5.803921818733215332e-01 4.196078479290008545e-01 0.000000000000000000e+00 1.000000000000000000e+00 -5.725490450859069824e-01 4.274509847164154053e-01 0.000000000000000000e+00 1.000000000000000000e+00 -5.647059082984924316e-01 4.352941215038299561e-01 0.000000000000000000e+00 1.000000000000000000e+00 -5.568627715110778809e-01 4.431372582912445068e-01 0.000000000000000000e+00 1.000000000000000000e+00 -5.490196347236633301e-01 4.509803950786590576e-01 0.000000000000000000e+00 1.000000000000000000e+00 -5.411764979362487793e-01 4.588235318660736084e-01 0.000000000000000000e+00 1.000000000000000000e+00 -5.333333611488342285e-01 4.666666686534881592e-01 0.000000000000000000e+00 1.000000000000000000e+00 -5.254902243614196777e-01 4.745098054409027100e-01 0.000000000000000000e+00 1.000000000000000000e+00 -5.176470875740051270e-01 4.823529422283172607e-01 0.000000000000000000e+00 1.000000000000000000e+00 -5.098039507865905762e-01 4.901960790157318115e-01 0.000000000000000000e+00 1.000000000000000000e+00 -5.019608139991760254e-01 4.980392158031463623e-01 0.000000000000000000e+00 1.000000000000000000e+00 -4.941176474094390869e-01 5.058823823928833008e-01 0.000000000000000000e+00 1.000000000000000000e+00 -4.862745106220245361e-01 5.137255191802978516e-01 0.000000000000000000e+00 1.000000000000000000e+00 -4.784313738346099854e-01 5.215686559677124023e-01 0.000000000000000000e+00 1.000000000000000000e+00 -4.705882370471954346e-01 5.294117927551269531e-01 0.000000000000000000e+00 1.000000000000000000e+00 -4.627451002597808838e-01 5.372549295425415039e-01 0.000000000000000000e+00 1.000000000000000000e+00 -4.549019634723663330e-01 5.450980663299560547e-01 0.000000000000000000e+00 1.000000000000000000e+00 -4.470588266849517822e-01 5.529412031173706055e-01 0.000000000000000000e+00 1.000000000000000000e+00 -4.392156898975372314e-01 5.607843399047851562e-01 0.000000000000000000e+00 1.000000000000000000e+00 -4.313725531101226807e-01 5.686274766921997070e-01 0.000000000000000000e+00 1.000000000000000000e+00 -4.235294163227081299e-01 5.764706134796142578e-01 0.000000000000000000e+00 1.000000000000000000e+00 -4.156862795352935791e-01 5.843137502670288086e-01 0.000000000000000000e+00 1.000000000000000000e+00 -4.078431427478790283e-01 5.921568870544433594e-01 0.000000000000000000e+00 1.000000000000000000e+00 -4.000000059604644775e-01 6.000000238418579102e-01 0.000000000000000000e+00 1.000000000000000000e+00 -3.921568691730499268e-01 6.078431606292724609e-01 0.000000000000000000e+00 1.000000000000000000e+00 -3.843137323856353760e-01 6.156862974166870117e-01 0.000000000000000000e+00 1.000000000000000000e+00 -3.764705955982208252e-01 6.235294342041015625e-01 0.000000000000000000e+00 1.000000000000000000e+00 -3.686274588108062744e-01 6.313725709915161133e-01 0.000000000000000000e+00 1.000000000000000000e+00 -3.607843220233917236e-01 6.392157077789306641e-01 0.000000000000000000e+00 1.000000000000000000e+00 -3.529411852359771729e-01 6.470588445663452148e-01 0.000000000000000000e+00 1.000000000000000000e+00 -3.450980484485626221e-01 6.549019813537597656e-01 0.000000000000000000e+00 1.000000000000000000e+00 -3.372549116611480713e-01 6.627451181411743164e-01 0.000000000000000000e+00 1.000000000000000000e+00 -3.294117748737335205e-01 6.705882549285888672e-01 0.000000000000000000e+00 1.000000000000000000e+00 -3.215686380863189697e-01 6.784313917160034180e-01 0.000000000000000000e+00 1.000000000000000000e+00 -3.137255012989044189e-01 6.862745285034179688e-01 0.000000000000000000e+00 1.000000000000000000e+00 -3.058823645114898682e-01 6.941176652908325195e-01 0.000000000000000000e+00 1.000000000000000000e+00 -2.980392277240753174e-01 7.019608020782470703e-01 0.000000000000000000e+00 1.000000000000000000e+00 -2.901960909366607666e-01 7.098039388656616211e-01 0.000000000000000000e+00 1.000000000000000000e+00 -2.823529541492462158e-01 7.176470756530761719e-01 0.000000000000000000e+00 1.000000000000000000e+00 -2.745098173618316650e-01 7.254902124404907227e-01 0.000000000000000000e+00 1.000000000000000000e+00 -2.666666805744171143e-01 7.333333492279052734e-01 0.000000000000000000e+00 1.000000000000000000e+00 -2.588235437870025635e-01 7.411764860153198242e-01 0.000000000000000000e+00 1.000000000000000000e+00 -2.509804069995880127e-01 7.490196228027343750e-01 0.000000000000000000e+00 1.000000000000000000e+00 -2.431372553110122681e-01 7.568627595901489258e-01 0.000000000000000000e+00 1.000000000000000000e+00 -2.352941185235977173e-01 7.647058963775634766e-01 0.000000000000000000e+00 1.000000000000000000e+00 -2.274509817361831665e-01 7.725490331649780273e-01 0.000000000000000000e+00 1.000000000000000000e+00 -2.196078449487686157e-01 7.803921699523925781e-01 0.000000000000000000e+00 1.000000000000000000e+00 -2.117647081613540649e-01 7.882353067398071289e-01 0.000000000000000000e+00 1.000000000000000000e+00 -2.039215713739395142e-01 7.960784435272216797e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.960784345865249634e-01 8.039215803146362305e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.882352977991104126e-01 8.117647171020507812e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.803921610116958618e-01 8.196078538894653320e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.725490242242813110e-01 8.274509906768798828e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.647058874368667603e-01 8.352941274642944336e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.568627506494522095e-01 8.431372642517089844e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.490196138620376587e-01 8.509804010391235352e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.411764770746231079e-01 8.588235378265380859e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.333333402872085571e-01 8.666666746139526367e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.254902034997940063e-01 8.745098114013671875e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.176470592617988586e-01 8.823529481887817383e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.098039224743843079e-01 8.901960849761962891e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.019607856869697571e-01 8.980392217636108398e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.411764889955520630e-02 9.058823585510253906e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.627451211214065552e-02 9.137254953384399414e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.843137532472610474e-02 9.215686321258544922e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.058823853731155396e-02 9.294117689132690430e-01 0.000000000000000000e+00 1.000000000000000000e+00 -6.274510174989700317e-02 9.372549057006835938e-01 0.000000000000000000e+00 1.000000000000000000e+00 -5.490196123719215393e-02 9.450980424880981445e-01 0.000000000000000000e+00 1.000000000000000000e+00 -4.705882444977760315e-02 9.529411792755126953e-01 0.000000000000000000e+00 1.000000000000000000e+00 -3.921568766236305237e-02 9.607843160629272461e-01 0.000000000000000000e+00 1.000000000000000000e+00 -3.137255087494850159e-02 9.686274528503417969e-01 0.000000000000000000e+00 1.000000000000000000e+00 -2.352941222488880157e-02 9.764705896377563477e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.568627543747425079e-02 9.843137264251708984e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.843137718737125397e-03 9.921568632125854492e-01 0.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/bwr b/fastplotlib/utils/colormaps/bwr deleted file mode 100644 index 5cfe14afb..000000000 --- a/fastplotlib/utils/colormaps/bwr +++ /dev/null @@ -1,256 +0,0 @@ -0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -7.843137718737125397e-03 7.843137718737125397e-03 1.000000000000000000e+00 1.000000000000000000e+00 -1.568627543747425079e-02 1.568627543747425079e-02 1.000000000000000000e+00 1.000000000000000000e+00 -2.352941222488880157e-02 2.352941222488880157e-02 1.000000000000000000e+00 1.000000000000000000e+00 -3.137255087494850159e-02 3.137255087494850159e-02 1.000000000000000000e+00 1.000000000000000000e+00 -3.921568766236305237e-02 3.921568766236305237e-02 1.000000000000000000e+00 1.000000000000000000e+00 -4.705882444977760315e-02 4.705882444977760315e-02 1.000000000000000000e+00 1.000000000000000000e+00 -5.490196123719215393e-02 5.490196123719215393e-02 1.000000000000000000e+00 1.000000000000000000e+00 -6.274510174989700317e-02 6.274510174989700317e-02 1.000000000000000000e+00 1.000000000000000000e+00 -7.058823853731155396e-02 7.058823853731155396e-02 1.000000000000000000e+00 1.000000000000000000e+00 -7.843137532472610474e-02 7.843137532472610474e-02 1.000000000000000000e+00 1.000000000000000000e+00 -8.627451211214065552e-02 8.627451211214065552e-02 1.000000000000000000e+00 1.000000000000000000e+00 -9.411764889955520630e-02 9.411764889955520630e-02 1.000000000000000000e+00 1.000000000000000000e+00 -1.019607856869697571e-01 1.019607856869697571e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.098039224743843079e-01 1.098039224743843079e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.176470592617988586e-01 1.176470592617988586e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.254902034997940063e-01 1.254902034997940063e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.333333402872085571e-01 1.333333402872085571e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.411764770746231079e-01 1.411764770746231079e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.490196138620376587e-01 1.490196138620376587e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.568627506494522095e-01 1.568627506494522095e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.647058874368667603e-01 1.647058874368667603e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.725490242242813110e-01 1.725490242242813110e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.803921610116958618e-01 1.803921610116958618e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.882352977991104126e-01 1.882352977991104126e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.960784345865249634e-01 1.960784345865249634e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.039215713739395142e-01 2.039215713739395142e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.117647081613540649e-01 2.117647081613540649e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.196078449487686157e-01 2.196078449487686157e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.274509817361831665e-01 2.274509817361831665e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.352941185235977173e-01 2.352941185235977173e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.431372553110122681e-01 2.431372553110122681e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.509804069995880127e-01 2.509804069995880127e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.588235437870025635e-01 2.588235437870025635e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.666666805744171143e-01 2.666666805744171143e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.745098173618316650e-01 2.745098173618316650e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.823529541492462158e-01 2.823529541492462158e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.901960909366607666e-01 2.901960909366607666e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.980392277240753174e-01 2.980392277240753174e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.058823645114898682e-01 3.058823645114898682e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.137255012989044189e-01 3.137255012989044189e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.215686380863189697e-01 3.215686380863189697e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.294117748737335205e-01 3.294117748737335205e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.372549116611480713e-01 3.372549116611480713e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.450980484485626221e-01 3.450980484485626221e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.529411852359771729e-01 3.529411852359771729e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.607843220233917236e-01 3.607843220233917236e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.686274588108062744e-01 3.686274588108062744e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.764705955982208252e-01 3.764705955982208252e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.843137323856353760e-01 3.843137323856353760e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.921568691730499268e-01 3.921568691730499268e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.000000059604644775e-01 4.000000059604644775e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.078431427478790283e-01 4.078431427478790283e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.156862795352935791e-01 4.156862795352935791e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.235294163227081299e-01 4.235294163227081299e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.313725531101226807e-01 4.313725531101226807e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.392156898975372314e-01 4.392156898975372314e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.470588266849517822e-01 4.470588266849517822e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.549019634723663330e-01 4.549019634723663330e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.627451002597808838e-01 4.627451002597808838e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.705882370471954346e-01 4.705882370471954346e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.784313738346099854e-01 4.784313738346099854e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.862745106220245361e-01 4.862745106220245361e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.941176474094390869e-01 4.941176474094390869e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.019608139991760254e-01 5.019608139991760254e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.098039507865905762e-01 5.098039507865905762e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.176470875740051270e-01 5.176470875740051270e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.254902243614196777e-01 5.254902243614196777e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.333333611488342285e-01 5.333333611488342285e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.411764979362487793e-01 5.411764979362487793e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.490196347236633301e-01 5.490196347236633301e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.568627715110778809e-01 5.568627715110778809e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.647059082984924316e-01 5.647059082984924316e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.725490450859069824e-01 5.725490450859069824e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.803921818733215332e-01 5.803921818733215332e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.882353186607360840e-01 5.882353186607360840e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.960784554481506348e-01 5.960784554481506348e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.039215922355651855e-01 6.039215922355651855e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.117647290229797363e-01 6.117647290229797363e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.196078658103942871e-01 6.196078658103942871e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.274510025978088379e-01 6.274510025978088379e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.352941393852233887e-01 6.352941393852233887e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.431372761726379395e-01 6.431372761726379395e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.509804129600524902e-01 6.509804129600524902e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.588235497474670410e-01 6.588235497474670410e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.666666865348815918e-01 6.666666865348815918e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.745098233222961426e-01 6.745098233222961426e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.823529601097106934e-01 6.823529601097106934e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.901960968971252441e-01 6.901960968971252441e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.980392336845397949e-01 6.980392336845397949e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.058823704719543457e-01 7.058823704719543457e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.137255072593688965e-01 7.137255072593688965e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.215686440467834473e-01 7.215686440467834473e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.294117808341979980e-01 7.294117808341979980e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.372549176216125488e-01 7.372549176216125488e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.450980544090270996e-01 7.450980544090270996e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.529411911964416504e-01 7.529411911964416504e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.607843279838562012e-01 7.607843279838562012e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.686274647712707520e-01 7.686274647712707520e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.764706015586853027e-01 7.764706015586853027e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.843137383460998535e-01 7.843137383460998535e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.921568751335144043e-01 7.921568751335144043e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.000000119209289551e-01 8.000000119209289551e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.078431487083435059e-01 8.078431487083435059e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.156862854957580566e-01 8.156862854957580566e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.235294222831726074e-01 8.235294222831726074e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.313725590705871582e-01 8.313725590705871582e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.392156958580017090e-01 8.392156958580017090e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.470588326454162598e-01 8.470588326454162598e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.549019694328308105e-01 8.549019694328308105e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.627451062202453613e-01 8.627451062202453613e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.705882430076599121e-01 8.705882430076599121e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.784313797950744629e-01 8.784313797950744629e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.862745165824890137e-01 8.862745165824890137e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.941176533699035645e-01 8.941176533699035645e-01 1.000000000000000000e+00 1.000000000000000000e+00 -9.019607901573181152e-01 9.019607901573181152e-01 1.000000000000000000e+00 1.000000000000000000e+00 -9.098039269447326660e-01 9.098039269447326660e-01 1.000000000000000000e+00 1.000000000000000000e+00 -9.176470637321472168e-01 9.176470637321472168e-01 1.000000000000000000e+00 1.000000000000000000e+00 -9.254902005195617676e-01 9.254902005195617676e-01 1.000000000000000000e+00 1.000000000000000000e+00 -9.333333373069763184e-01 9.333333373069763184e-01 1.000000000000000000e+00 1.000000000000000000e+00 -9.411764740943908691e-01 9.411764740943908691e-01 1.000000000000000000e+00 1.000000000000000000e+00 -9.490196108818054199e-01 9.490196108818054199e-01 1.000000000000000000e+00 1.000000000000000000e+00 -9.568627476692199707e-01 9.568627476692199707e-01 1.000000000000000000e+00 1.000000000000000000e+00 -9.647058844566345215e-01 9.647058844566345215e-01 1.000000000000000000e+00 1.000000000000000000e+00 -9.725490212440490723e-01 9.725490212440490723e-01 1.000000000000000000e+00 1.000000000000000000e+00 -9.803921580314636230e-01 9.803921580314636230e-01 1.000000000000000000e+00 1.000000000000000000e+00 -9.882352948188781738e-01 9.882352948188781738e-01 1.000000000000000000e+00 1.000000000000000000e+00 -9.960784316062927246e-01 9.960784316062927246e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.960784316062927246e-01 9.960784316062927246e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.882352948188781738e-01 9.882352948188781738e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.803921580314636230e-01 9.803921580314636230e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.725490212440490723e-01 9.725490212440490723e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.647058844566345215e-01 9.647058844566345215e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.568627476692199707e-01 9.568627476692199707e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.490196108818054199e-01 9.490196108818054199e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.411764740943908691e-01 9.411764740943908691e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.333333373069763184e-01 9.333333373069763184e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.254902005195617676e-01 9.254902005195617676e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.176470637321472168e-01 9.176470637321472168e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.098039269447326660e-01 9.098039269447326660e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.019607901573181152e-01 9.019607901573181152e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.941176533699035645e-01 8.941176533699035645e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.862745165824890137e-01 8.862745165824890137e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.784313797950744629e-01 8.784313797950744629e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.705882430076599121e-01 8.705882430076599121e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.627451062202453613e-01 8.627451062202453613e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.549019694328308105e-01 8.549019694328308105e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.470588326454162598e-01 8.470588326454162598e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.392156958580017090e-01 8.392156958580017090e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.313725590705871582e-01 8.313725590705871582e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.235294222831726074e-01 8.235294222831726074e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.156862854957580566e-01 8.156862854957580566e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.078431487083435059e-01 8.078431487083435059e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.000000119209289551e-01 8.000000119209289551e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.921568751335144043e-01 7.921568751335144043e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.843137383460998535e-01 7.843137383460998535e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.764706015586853027e-01 7.764706015586853027e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.686274647712707520e-01 7.686274647712707520e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.607843279838562012e-01 7.607843279838562012e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.529411911964416504e-01 7.529411911964416504e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.450980544090270996e-01 7.450980544090270996e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.372549176216125488e-01 7.372549176216125488e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.294117808341979980e-01 7.294117808341979980e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.215686440467834473e-01 7.215686440467834473e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.137255072593688965e-01 7.137255072593688965e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.058823704719543457e-01 7.058823704719543457e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.980392336845397949e-01 6.980392336845397949e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.901960968971252441e-01 6.901960968971252441e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.823529601097106934e-01 6.823529601097106934e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.745098233222961426e-01 6.745098233222961426e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.666666865348815918e-01 6.666666865348815918e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.588235497474670410e-01 6.588235497474670410e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.509804129600524902e-01 6.509804129600524902e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.431372761726379395e-01 6.431372761726379395e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.352941393852233887e-01 6.352941393852233887e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.274510025978088379e-01 6.274510025978088379e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.196078658103942871e-01 6.196078658103942871e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.117647290229797363e-01 6.117647290229797363e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.039215922355651855e-01 6.039215922355651855e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.960784554481506348e-01 5.960784554481506348e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.882353186607360840e-01 5.882353186607360840e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.803921818733215332e-01 5.803921818733215332e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.725490450859069824e-01 5.725490450859069824e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.647059082984924316e-01 5.647059082984924316e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.568627715110778809e-01 5.568627715110778809e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.490196347236633301e-01 5.490196347236633301e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.411764979362487793e-01 5.411764979362487793e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.333333611488342285e-01 5.333333611488342285e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.254902243614196777e-01 5.254902243614196777e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.176470875740051270e-01 5.176470875740051270e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.098039507865905762e-01 5.098039507865905762e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.019608139991760254e-01 5.019608139991760254e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.941176474094390869e-01 4.941176474094390869e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.862745106220245361e-01 4.862745106220245361e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.784313738346099854e-01 4.784313738346099854e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.705882370471954346e-01 4.705882370471954346e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.627451002597808838e-01 4.627451002597808838e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.549019634723663330e-01 4.549019634723663330e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.470588266849517822e-01 4.470588266849517822e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.392156898975372314e-01 4.392156898975372314e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.313725531101226807e-01 4.313725531101226807e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.235294163227081299e-01 4.235294163227081299e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.156862795352935791e-01 4.156862795352935791e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.078431427478790283e-01 4.078431427478790283e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.000000059604644775e-01 4.000000059604644775e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.921568691730499268e-01 3.921568691730499268e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.843137323856353760e-01 3.843137323856353760e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.764705955982208252e-01 3.764705955982208252e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.686274588108062744e-01 3.686274588108062744e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.607843220233917236e-01 3.607843220233917236e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.529411852359771729e-01 3.529411852359771729e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.450980484485626221e-01 3.450980484485626221e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.372549116611480713e-01 3.372549116611480713e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.294117748737335205e-01 3.294117748737335205e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.215686380863189697e-01 3.215686380863189697e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.137255012989044189e-01 3.137255012989044189e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.058823645114898682e-01 3.058823645114898682e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.980392277240753174e-01 2.980392277240753174e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.901960909366607666e-01 2.901960909366607666e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.823529541492462158e-01 2.823529541492462158e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.745098173618316650e-01 2.745098173618316650e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.666666805744171143e-01 2.666666805744171143e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.588235437870025635e-01 2.588235437870025635e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.509804069995880127e-01 2.509804069995880127e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.431372553110122681e-01 2.431372553110122681e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.352941185235977173e-01 2.352941185235977173e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.274509817361831665e-01 2.274509817361831665e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.196078449487686157e-01 2.196078449487686157e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.117647081613540649e-01 2.117647081613540649e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.039215713739395142e-01 2.039215713739395142e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.960784345865249634e-01 1.960784345865249634e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.882352977991104126e-01 1.882352977991104126e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.803921610116958618e-01 1.803921610116958618e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.725490242242813110e-01 1.725490242242813110e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.647058874368667603e-01 1.647058874368667603e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.568627506494522095e-01 1.568627506494522095e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.490196138620376587e-01 1.490196138620376587e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.411764770746231079e-01 1.411764770746231079e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.333333402872085571e-01 1.333333402872085571e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.254902034997940063e-01 1.254902034997940063e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.176470592617988586e-01 1.176470592617988586e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.098039224743843079e-01 1.098039224743843079e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.019607856869697571e-01 1.019607856869697571e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.411764889955520630e-02 9.411764889955520630e-02 1.000000000000000000e+00 -1.000000000000000000e+00 8.627451211214065552e-02 8.627451211214065552e-02 1.000000000000000000e+00 -1.000000000000000000e+00 7.843137532472610474e-02 7.843137532472610474e-02 1.000000000000000000e+00 -1.000000000000000000e+00 7.058823853731155396e-02 7.058823853731155396e-02 1.000000000000000000e+00 -1.000000000000000000e+00 6.274510174989700317e-02 6.274510174989700317e-02 1.000000000000000000e+00 -1.000000000000000000e+00 5.490196123719215393e-02 5.490196123719215393e-02 1.000000000000000000e+00 -1.000000000000000000e+00 4.705882444977760315e-02 4.705882444977760315e-02 1.000000000000000000e+00 -1.000000000000000000e+00 3.921568766236305237e-02 3.921568766236305237e-02 1.000000000000000000e+00 -1.000000000000000000e+00 3.137255087494850159e-02 3.137255087494850159e-02 1.000000000000000000e+00 -1.000000000000000000e+00 2.352941222488880157e-02 2.352941222488880157e-02 1.000000000000000000e+00 -1.000000000000000000e+00 1.568627543747425079e-02 1.568627543747425079e-02 1.000000000000000000e+00 -1.000000000000000000e+00 7.843137718737125397e-03 7.843137718737125397e-03 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/cividis b/fastplotlib/utils/colormaps/cividis deleted file mode 100644 index 30005ec22..000000000 --- a/fastplotlib/utils/colormaps/cividis +++ /dev/null @@ -1,256 +0,0 @@ -0.000000000000000000e+00 1.351120024919509888e-01 3.047510087490081787e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.380680054426193237e-01 3.111050128936767578e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.410129964351654053e-01 3.175790011882781982e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.439509987831115723e-01 3.239820003509521484e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.468770056962966919e-01 3.304789960384368896e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.497910022735595703e-01 3.370650112628936768e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.526730060577392578e-01 3.437039852142333984e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.553770005702972412e-01 3.504999876022338867e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.579319983720779419e-01 3.575209975242614746e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.604949980974197388e-01 3.645339906215667725e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.630579978227615356e-01 3.716079890727996826e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.656209975481033325e-01 3.787690103054046631e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.682039946317672729e-01 3.859019875526428223e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.708000004291534424e-01 3.930999934673309326e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.734199970960617065e-01 4.003530144691467285e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.760820001363754272e-01 4.075770080089569092e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.788019984960556030e-01 4.147639870643615723e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.816100031137466431e-01 4.218589961528778076e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.845500022172927856e-01 4.288020133972167969e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.869149953126907349e-01 4.355320036411285400e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.887689977884292603e-01 4.395630061626434326e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.909500062465667725e-01 4.410850107669830322e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.933660060167312622e-01 4.415610134601593018e-01 1.000000000000000000e+00 -3.601999953389167786e-03 1.959110051393508911e-01 4.415639936923980713e-01 1.000000000000000000e+00 -1.785200089216232300e-02 1.985280066728591919e-01 4.412479996681213379e-01 1.000000000000000000e+00 -3.210999816656112671e-02 2.011989951133728027e-01 4.407849907875061035e-01 1.000000000000000000e+00 -4.620499908924102783e-02 2.039030045270919800e-01 4.401960074901580811e-01 1.000000000000000000e+00 -5.837799981236457825e-02 2.066289931535720825e-01 4.395309984683990479e-01 1.000000000000000000e+00 -6.896799802780151367e-02 2.093719989061355591e-01 4.388630092144012451e-01 1.000000000000000000e+00 -7.862400263547897339e-02 2.121219933032989502e-01 4.381049871444702148e-01 1.000000000000000000e+00 -8.746500313282012939e-02 2.148790061473846436e-01 4.373419880867004395e-01 1.000000000000000000e+00 -9.564500302076339722e-02 2.176429927349090576e-01 4.365929961204528809e-01 1.000000000000000000e+00 -1.034009978175163269e-01 2.204059958457946777e-01 4.357900023460388184e-01 1.000000000000000000e+00 -1.106579974293708801e-01 2.231699973344802856e-01 4.350669980049133301e-01 1.000000000000000000e+00 -1.176119968295097351e-01 2.259349972009658813e-01 4.343079924583435059e-01 1.000000000000000000e+00 -1.242910027503967285e-01 2.286970019340515137e-01 4.335469901561737061e-01 1.000000000000000000e+00 -1.306689977645874023e-01 2.314579933881759644e-01 4.328399896621704102e-01 1.000000000000000000e+00 -1.368300020694732666e-01 2.342160046100616455e-01 4.321480095386505127e-01 1.000000000000000000e+00 -1.428519934415817261e-01 2.369720041751861572e-01 4.314039945602416992e-01 1.000000000000000000e+00 -1.486379951238632202e-01 2.397239953279495239e-01 4.307520091533660889e-01 1.000000000000000000e+00 -1.542609930038452148e-01 2.424750030040740967e-01 4.301199913024902344e-01 1.000000000000000000e+00 -1.597329974174499512e-01 2.452210038900375366e-01 4.295279979705810547e-01 1.000000000000000000e+00 -1.651130020618438721e-01 2.479649931192398071e-01 4.289079904556274414e-01 1.000000000000000000e+00 -1.703619956970214844e-01 2.507070004940032959e-01 4.283249974250793457e-01 1.000000000000000000e+00 -1.754900068044662476e-01 2.534439861774444580e-01 4.277899861335754395e-01 1.000000000000000000e+00 -1.805029958486557007e-01 2.561799883842468262e-01 4.272989928722381592e-01 1.000000000000000000e+00 -1.854529976844787598e-01 2.589139938354492188e-01 4.267880022525787354e-01 1.000000000000000000e+00 -1.903029978275299072e-01 2.616440057754516602e-01 4.263289868831634521e-01 1.000000000000000000e+00 -1.950570046901702881e-01 2.643719911575317383e-01 4.259240031242370605e-01 1.000000000000000000e+00 -1.997639983892440796e-01 2.670989930629730225e-01 4.254969954490661621e-01 1.000000000000000000e+00 -2.043849974870681763e-01 2.698230147361755371e-01 4.251259863376617432e-01 1.000000000000000000e+00 -2.089260071516036987e-01 2.725459933280944824e-01 4.248090088367462158e-01 1.000000000000000000e+00 -2.134310007095336914e-01 2.752659916877746582e-01 4.244799911975860596e-01 1.000000000000000000e+00 -2.178629934787750244e-01 2.779850065708160400e-01 4.242059886455535889e-01 1.000000000000000000e+00 -2.222640067338943481e-01 2.807019948959350586e-01 4.239139854907989502e-01 1.000000000000000000e+00 -2.265979945659637451e-01 2.834190130233764648e-01 4.236780107021331787e-01 1.000000000000000000e+00 -2.308710068464279175e-01 2.861340045928955078e-01 4.234980046749114990e-01 1.000000000000000000e+00 -2.351199984550476074e-01 2.888480126857757568e-01 4.233039915561676025e-01 1.000000000000000000e+00 -2.393119931221008301e-01 2.915619909763336182e-01 4.231669902801513672e-01 1.000000000000000000e+00 -2.434850037097930908e-01 2.942740023136138916e-01 4.230139851570129395e-01 1.000000000000000000e+00 -2.476049959659576416e-01 2.969860136508941650e-01 4.229170083999633789e-01 1.000000000000000000e+00 -2.516750097274780273e-01 2.996979951858520508e-01 4.228729903697967529e-01 1.000000000000000000e+00 -2.557309865951538086e-01 3.024089932441711426e-01 4.228140115737915039e-01 1.000000000000000000e+00 -2.597399950027465820e-01 3.051199913024902344e-01 4.228099882602691650e-01 1.000000000000000000e+00 -2.637380063533782959e-01 3.078309893608093262e-01 4.227890074253082275e-01 1.000000000000000000e+00 -2.676930129528045654e-01 3.105419874191284180e-01 4.228209853172302246e-01 1.000000000000000000e+00 -2.716389894485473633e-01 3.132529854774475098e-01 4.228369891643524170e-01 1.000000000000000000e+00 -2.755129933357238770e-01 3.159649968147277832e-01 4.229789972305297852e-01 1.000000000000000000e+00 -2.794109880924224854e-01 3.186770081520080566e-01 4.230310022830963135e-01 1.000000000000000000e+00 -2.832399904727935791e-01 3.213900029659271240e-01 4.232110083103179932e-01 1.000000000000000000e+00 -2.870649993419647217e-01 3.241029977798461914e-01 4.233730137348175049e-01 1.000000000000000000e+00 -2.908839881420135498e-01 3.268159925937652588e-01 4.235169887542724609e-01 1.000000000000000000e+00 -2.946690022945404053e-01 3.295310139656066895e-01 4.237160086631774902e-01 1.000000000000000000e+00 -2.984209954738616943e-01 3.322469890117645264e-01 4.239729940891265869e-01 1.000000000000000000e+00 -3.021689951419830322e-01 3.349629938602447510e-01 4.242129921913146973e-01 1.000000000000000000e+00 -3.058860003948211670e-01 3.376809954643249512e-01 4.245119988918304443e-01 1.000000000000000000e+00 -3.096010088920593262e-01 3.403989970684051514e-01 4.247899949550628662e-01 1.000000000000000000e+00 -3.132869899272918701e-01 3.431200087070465088e-01 4.251199960708618164e-01 1.000000000000000000e+00 -3.169409930706024170e-01 3.458420038223266602e-01 4.255119860172271729e-01 1.000000000000000000e+00 -3.205949962139129639e-01 3.485650122165679932e-01 4.258889853954315186e-01 1.000000000000000000e+00 -3.242500126361846924e-01 3.512890040874481201e-01 4.262500107288360596e-01 1.000000000000000000e+00 -3.278749883174896240e-01 3.540160059928894043e-01 4.266700148582458496e-01 1.000000000000000000e+00 -3.314740061759948730e-01 3.567439913749694824e-01 4.271439909934997559e-01 1.000000000000000000e+00 -3.350729942321777344e-01 3.594740033149719238e-01 4.276050031185150146e-01 1.000000000000000000e+00 -3.386729955673217773e-01 3.622060120105743408e-01 4.280529916286468506e-01 1.000000000000000000e+00 -3.422459959983825684e-01 3.649390041828155518e-01 4.285590052604675293e-01 1.000000000000000000e+00 -3.457930088043212891e-01 3.676759898662567139e-01 4.291270077228546143e-01 1.000000000000000000e+00 -3.493410050868988037e-01 3.704139888286590576e-01 4.296849966049194336e-01 1.000000000000000000e+00 -3.528920114040374756e-01 3.731530010700225830e-01 4.302259981632232666e-01 1.000000000000000000e+00 -3.564180135726928711e-01 3.758960068225860596e-01 4.308229982852935791e-01 1.000000000000000000e+00 -3.599160015583038330e-01 3.786410093307495117e-01 4.315010011196136475e-01 1.000000000000000000e+00 -3.634459972381591797e-01 3.813880085945129395e-01 4.320749938488006592e-01 1.000000000000000000e+00 -3.669230043888092041e-01 3.841390013694763184e-01 4.327960014343261719e-01 1.000000000000000000e+00 -3.704299926757812500e-01 3.868899941444396973e-01 4.334279894828796387e-01 1.000000000000000000e+00 -3.738839924335479736e-01 3.896459937095642090e-01 4.342089891433715820e-01 1.000000000000000000e+00 -3.773710131645202637e-01 3.924039900302886963e-01 4.348900020122528076e-01 1.000000000000000000e+00 -3.808299899101257324e-01 3.951640129089355469e-01 4.356530010700225830e-01 1.000000000000000000e+00 -3.842679858207702637e-01 3.979279994964599609e-01 4.364750087261199951e-01 1.000000000000000000e+00 -3.877049982547760010e-01 4.006940126419067383e-01 4.373050034046173096e-01 1.000000000000000000e+00 -3.911510109901428223e-01 4.034639894962310791e-01 4.380959868431091309e-01 1.000000000000000000e+00 -3.945679962635040283e-01 4.062359929084777832e-01 4.389860033988952637e-01 1.000000000000000000e+00 -3.979910016059875488e-01 4.090110063552856445e-01 4.398480057716369629e-01 1.000000000000000000e+00 -4.014180004596710205e-01 4.117900133132934570e-01 4.407080113887786865e-01 1.000000000000000000e+00 -4.048199951648712158e-01 4.145720005035400391e-01 4.416419863700866699e-01 1.000000000000000000e+00 -4.082260131835937500e-01 4.173569977283477783e-01 4.425700008869171143e-01 1.000000000000000000e+00 -4.116069972515106201e-01 4.201450049877166748e-01 4.435769915580749512e-01 1.000000000000000000e+00 -4.149920046329498291e-01 4.229370057582855225e-01 4.445779919624328613e-01 1.000000000000000000e+00 -4.183830022811889648e-01 4.257330000400543213e-01 4.455600082874298096e-01 1.000000000000000000e+00 -4.217480123043060303e-01 4.285309910774230957e-01 4.466400146484375000e-01 1.000000000000000000e+00 -4.251199960708618164e-01 4.313339889049530029e-01 4.476920068264007568e-01 1.000000000000000000e+00 -4.284619987010955811e-01 4.341399967670440674e-01 4.488640129566192627e-01 1.000000000000000000e+00 -4.318169951438903809e-01 4.369499981403350830e-01 4.499819874763488770e-01 1.000000000000000000e+00 -4.351679980754852295e-01 4.397630095481872559e-01 4.511339962482452393e-01 1.000000000000000000e+00 -4.385040104389190674e-01 4.425800144672393799e-01 4.523409903049468994e-01 1.000000000000000000e+00 -4.418100118637084961e-01 4.454019963741302490e-01 4.536589980125427246e-01 1.000000000000000000e+00 -4.451479911804199219e-01 4.482260048389434814e-01 4.548850059509277344e-01 1.000000000000000000e+00 -4.484469890594482422e-01 4.510529935359954834e-01 4.562639892101287842e-01 1.000000000000000000e+00 -4.517590105533599854e-01 4.538869857788085938e-01 4.575819969177246094e-01 1.000000000000000000e+00 -4.550719857215881348e-01 4.567179977893829346e-01 4.589760005474090576e-01 1.000000000000000000e+00 -4.583660066127777100e-01 4.595519900321960449e-01 4.604569971561431885e-01 1.000000000000000000e+00 -4.616160094738006592e-01 4.624049961566925049e-01 4.619689881801605225e-01 1.000000000000000000e+00 -4.649469852447509766e-01 4.652409851551055908e-01 4.633949995040893555e-01 1.000000000000000000e+00 -4.682539999485015869e-01 4.680829942226409912e-01 4.649080038070678711e-01 1.000000000000000000e+00 -4.715009927749633789e-01 4.709599912166595459e-01 4.663569927215576172e-01 1.000000000000000000e+00 -4.748120009899139404e-01 4.738320112228393555e-01 4.676809906959533691e-01 1.000000000000000000e+00 -4.781860113143920898e-01 4.766989946365356445e-01 4.688450098037719727e-01 1.000000000000000000e+00 -4.816220104694366455e-01 4.795730113983154297e-01 4.697670042514801025e-01 1.000000000000000000e+00 -4.851410090923309326e-01 4.824509918689727783e-01 4.703840017318725586e-01 1.000000000000000000e+00 -4.886969923973083496e-01 4.853180050849914551e-01 4.710080027580261230e-01 1.000000000000000000e+00 -4.922780096530914307e-01 4.881980121135711670e-01 4.714530110359191895e-01 1.000000000000000000e+00 -4.959129989147186279e-01 4.910759925842285156e-01 4.717510044574737549e-01 1.000000000000000000e+00 -4.995520114898681641e-01 4.939599931240081787e-01 4.720320105552673340e-01 1.000000000000000000e+00 -5.031849741935729980e-01 4.968509972095489502e-01 4.723049998283386230e-01 1.000000000000000000e+00 -5.068659782409667969e-01 4.997430145740509033e-01 4.724319875240325928e-01 1.000000000000000000e+00 -5.105400085449218750e-01 5.026429891586303711e-01 4.725500047206878662e-01 1.000000000000000000e+00 -5.142260193824768066e-01 5.055459737777709961e-01 4.726400077342987061e-01 1.000000000000000000e+00 -5.179200172424316406e-01 5.084540247917175293e-01 4.727070033550262451e-01 1.000000000000000000e+00 -5.216429829597473145e-01 5.113670229911804199e-01 4.726389944553375244e-01 1.000000000000000000e+00 -5.253480076789855957e-01 5.142850279808044434e-01 4.726600050926208496e-01 1.000000000000000000e+00 -5.290859937667846680e-01 5.172070264816284180e-01 4.725430011749267578e-01 1.000000000000000000e+00 -5.328289866447448730e-01 5.201349854469299316e-01 4.724009931087493896e-01 1.000000000000000000e+00 -5.365530252456665039e-01 5.230669975280761719e-01 4.723519980907440186e-01 1.000000000000000000e+00 -5.403069853782653809e-01 5.260050296783447266e-01 4.721629917621612549e-01 1.000000000000000000e+00 -5.440689921379089355e-01 5.289480090141296387e-01 4.719470143318176270e-01 1.000000000000000000e+00 -5.478399991989135742e-01 5.318949818611145020e-01 4.717040061950683594e-01 1.000000000000000000e+00 -5.516120195388793945e-01 5.348489880561828613e-01 4.714390039443969727e-01 1.000000000000000000e+00 -5.553929805755615234e-01 5.378069877624511719e-01 4.711470007896423340e-01 1.000000000000000000e+00 -5.591809749603271484e-01 5.407710075378417969e-01 4.708290100097656250e-01 1.000000000000000000e+00 -5.629720091819763184e-01 5.437409877777099609e-01 4.704880118370056152e-01 1.000000000000000000e+00 -5.668020248413085938e-01 5.467150211334228516e-01 4.699879884719848633e-01 1.000000000000000000e+00 -5.706070065498352051e-01 5.496950149536132812e-01 4.695929884910583496e-01 1.000000000000000000e+00 -5.744169950485229492e-01 5.526819825172424316e-01 4.691720008850097656e-01 1.000000000000000000e+00 -5.782359838485717773e-01 5.556730031967163086e-01 4.687240123748779297e-01 1.000000000000000000e+00 -5.820869803428649902e-01 5.586699843406677246e-01 4.681180119514465332e-01 1.000000000000000000e+00 -5.859159827232360840e-01 5.616739988327026367e-01 4.676179885864257812e-01 1.000000000000000000e+00 -5.897529721260070801e-01 5.646820068359375000e-01 4.670900106430053711e-01 1.000000000000000000e+00 -5.936220288276672363e-01 5.676969885826110840e-01 4.664010107517242432e-01 1.000000000000000000e+00 -5.974689722061157227e-01 5.707179903984069824e-01 4.658209979534149170e-01 1.000000000000000000e+00 -6.013540029525756836e-01 5.737429857254028320e-01 4.650740027427673340e-01 1.000000000000000000e+00 -6.052110195159912109e-01 5.767769813537597656e-01 4.644410014152526855e-01 1.000000000000000000e+00 -6.091049909591674805e-01 5.798159837722778320e-01 4.636380076408386230e-01 1.000000000000000000e+00 -6.129770278930664062e-01 5.828610062599182129e-01 4.629499912261962891e-01 1.000000000000000000e+00 -6.168519854545593262e-01 5.859130024909973145e-01 4.622370004653930664e-01 1.000000000000000000e+00 -6.207649707794189453e-01 5.889700055122375488e-01 4.613510072231292725e-01 1.000000000000000000e+00 -6.246539950370788574e-01 5.920339822769165039e-01 4.605830013751983643e-01 1.000000000000000000e+00 -6.285759806632995605e-01 5.951039791107177734e-01 4.596410095691680908e-01 1.000000000000000000e+00 -6.325060129165649414e-01 5.981799960136413574e-01 4.586679935455322266e-01 1.000000000000000000e+00 -6.364120244979858398e-01 6.012639999389648438e-01 4.578180015087127686e-01 1.000000000000000000e+00 -6.403520107269287109e-01 6.043540239334106445e-01 4.567910134792327881e-01 1.000000000000000000e+00 -6.442700028419494629e-01 6.074500083923339844e-01 4.558860063552856445e-01 1.000000000000000000e+00 -6.482220292091369629e-01 6.105530261993408203e-01 4.548009932041168213e-01 1.000000000000000000e+00 -6.521779894828796387e-01 6.136639714241027832e-01 4.536890089511871338e-01 1.000000000000000000e+00 -6.561139822006225586e-01 6.167799830436706543e-01 4.527019858360290527e-01 1.000000000000000000e+00 -6.600819826126098633e-01 6.199039816856384277e-01 4.515340030193328857e-01 1.000000000000000000e+00 -6.640549898147583008e-01 6.230340003967285156e-01 4.503380060195922852e-01 1.000000000000000000e+00 -6.680080294609069824e-01 6.261709928512573242e-01 4.492700099945068359e-01 1.000000000000000000e+00 -6.719909906387329102e-01 6.293159723281860352e-01 4.480180144309997559e-01 1.000000000000000000e+00 -6.759809851646423340e-01 6.324679851531982422e-01 4.467360079288482666e-01 1.000000000000000000e+00 -6.799790263175964355e-01 6.356260180473327637e-01 4.454239904880523682e-01 1.000000000000000000e+00 -6.839500069618225098e-01 6.387929916381835938e-01 4.442510008811950684e-01 1.000000000000000000e+00 -6.879569888114929199e-01 6.419659852981567383e-01 4.428859949111938477e-01 1.000000000000000000e+00 -6.919710040092468262e-01 6.451449990272521973e-01 4.414910078048706055e-01 1.000000000000000000e+00 -6.959850192070007324e-01 6.483340263366699219e-01 4.400720000267028809e-01 1.000000000000000000e+00 -7.000079751014709473e-01 6.515290141105651855e-01 4.386239945888519287e-01 1.000000000000000000e+00 -7.040370106697082520e-01 6.547309756278991699e-01 4.371469914913177490e-01 1.000000000000000000e+00 -7.080669999122619629e-01 6.579419970512390137e-01 4.356470108032226562e-01 1.000000000000000000e+00 -7.121049761772155762e-01 6.611599922180175781e-01 4.341169893741607666e-01 1.000000000000000000e+00 -7.161769866943359375e-01 6.643840074539184570e-01 4.323860108852386475e-01 1.000000000000000000e+00 -7.202219963073730469e-01 6.676179766654968262e-01 4.308049976825714111e-01 1.000000000000000000e+00 -7.242739796638488770e-01 6.708589792251586914e-01 4.291940033435821533e-01 1.000000000000000000e+00 -7.283340096473693848e-01 6.741070151329040527e-01 4.275540113449096680e-01 1.000000000000000000e+00 -7.324219942092895508e-01 6.773639917373657227e-01 4.257169961929321289e-01 1.000000000000000000e+00 -7.364879846572875977e-01 6.806290149688720703e-01 4.240280091762542725e-01 1.000000000000000000e+00 -7.405890226364135742e-01 6.838999986648559570e-01 4.221310019493103027e-01 1.000000000000000000e+00 -7.446640133857727051e-01 6.871809959411621094e-01 4.203929901123046875e-01 1.000000000000000000e+00 -7.487720251083374023e-01 6.904699802398681641e-01 4.184480011463165283e-01 1.000000000000000000e+00 -7.528859972953796387e-01 6.937659978866577148e-01 4.164719879627227783e-01 1.000000000000000000e+00 -7.569749951362609863e-01 6.970710158348083496e-01 4.146589934825897217e-01 1.000000000000000000e+00 -7.610960006713867188e-01 7.003840208053588867e-01 4.126380085945129395e-01 1.000000000000000000e+00 -7.652230262756347656e-01 7.037050127983093262e-01 4.105870127677917480e-01 1.000000000000000000e+00 -7.693529725074768066e-01 7.070350050926208496e-01 4.085159897804260254e-01 1.000000000000000000e+00 -7.734860181808471680e-01 7.103729844093322754e-01 4.064219892024993896e-01 1.000000000000000000e+00 -7.776510119438171387e-01 7.137190103530883789e-01 4.041120111942291260e-01 1.000000000000000000e+00 -7.817950248718261719e-01 7.170739769935607910e-01 4.019660055637359619e-01 1.000000000000000000e+00 -7.859650254249572754e-01 7.204380035400390625e-01 3.996129930019378662e-01 1.000000000000000000e+00 -7.901160120964050293e-01 7.238100171089172363e-01 3.974229991436004639e-01 1.000000000000000000e+00 -7.942979931831359863e-01 7.271900177001953125e-01 3.950160145759582520e-01 1.000000000000000000e+00 -7.984799742698669434e-01 7.305799722671508789e-01 3.925969898700714111e-01 1.000000000000000000e+00 -8.026670217514038086e-01 7.339779734611511230e-01 3.901529908180236816e-01 1.000000000000000000e+00 -8.068590164184570312e-01 7.373849749565124512e-01 3.876839876174926758e-01 1.000000000000000000e+00 -8.110539913177490234e-01 7.408009767532348633e-01 3.851979970932006836e-01 1.000000000000000000e+00 -8.152740001678466797e-01 7.442259788513183594e-01 3.825039863586425781e-01 1.000000000000000000e+00 -8.194990158081054688e-01 7.476590275764465332e-01 3.797850012779235840e-01 1.000000000000000000e+00 -8.237289786338806152e-01 7.511010169982910156e-01 3.770430088043212891e-01 1.000000000000000000e+00 -8.279590010643005371e-01 7.545530200004577637e-01 3.742919862270355225e-01 1.000000000000000000e+00 -8.321920037269592285e-01 7.580140233039855957e-01 3.715290129184722900e-01 1.000000000000000000e+00 -8.364289999008178711e-01 7.614830136299133301e-01 3.687469959259033203e-01 1.000000000000000000e+00 -8.406929969787597656e-01 7.649620175361633301e-01 3.657459914684295654e-01 1.000000000000000000e+00 -8.449569940567016602e-01 7.684500217437744141e-01 3.627409934997558594e-01 1.000000000000000000e+00 -8.492230176925659180e-01 7.719470262527465820e-01 3.597289919853210449e-01 1.000000000000000000e+00 -8.535150289535522461e-01 7.754539847373962402e-01 3.564999997615814209e-01 1.000000000000000000e+00 -8.578090071678161621e-01 7.789689898490905762e-01 3.532589972019195557e-01 1.000000000000000000e+00 -8.621050119400024414e-01 7.824940085411071777e-01 3.500109910964965820e-01 1.000000000000000000e+00 -8.664209842681884766e-01 7.860280275344848633e-01 3.465709984302520752e-01 1.000000000000000000e+00 -8.707169890403747559e-01 7.895720005035400391e-01 3.433330059051513672e-01 1.000000000000000000e+00 -8.750569820404052734e-01 7.931249737739562988e-01 3.396849930286407471e-01 1.000000000000000000e+00 -8.793780207633972168e-01 7.966870069503784180e-01 3.362410068511962891e-01 1.000000000000000000e+00 -8.837199807167053223e-01 8.002579808235168457e-01 3.325990140438079834e-01 1.000000000000000000e+00 -8.880810141563415527e-01 8.038390278816223145e-01 3.287700116634368896e-01 1.000000000000000000e+00 -8.924400210380554199e-01 8.074300289154052734e-01 3.249680101871490479e-01 1.000000000000000000e+00 -8.968179821968078613e-01 8.110299706459045410e-01 3.209820091724395752e-01 1.000000000000000000e+00 -9.011949896812438965e-01 8.146389722824096680e-01 3.170210123062133789e-01 1.000000000000000000e+00 -9.055889844894409180e-01 8.182569742202758789e-01 3.128890097141265869e-01 1.000000000000000000e+00 -9.100000262260437012e-01 8.218849897384643555e-01 3.085939884185791016e-01 1.000000000000000000e+00 -9.144070148468017578e-01 8.255220055580139160e-01 3.043479919433593750e-01 1.000000000000000000e+00 -9.188280105590820312e-01 8.291680216789245605e-01 2.999599874019622803e-01 1.000000000000000000e+00 -9.232789874076843262e-01 8.328220248222351074e-01 2.952440083026885986e-01 1.000000000000000000e+00 -9.277240037918090820e-01 8.364859819412231445e-01 2.906109988689422607e-01 1.000000000000000000e+00 -9.321799874305725098e-01 8.401589989662170410e-01 2.858799993991851807e-01 1.000000000000000000e+00 -9.366599917411804199e-01 8.438410162925720215e-01 2.808760106563568115e-01 1.000000000000000000e+00 -9.411470293998718262e-01 8.475300073623657227e-01 2.758150100708007812e-01 1.000000000000000000e+00 -9.456539750099182129e-01 8.512279987335205078e-01 2.705320119857788086e-01 1.000000000000000000e+00 -9.501780271530151367e-01 8.549330234527587891e-01 2.650850117206573486e-01 1.000000000000000000e+00 -9.547250270843505859e-01 8.586459755897521973e-01 2.593649923801422119e-01 1.000000000000000000e+00 -9.592840075492858887e-01 8.623650074005126953e-01 2.535629868507385254e-01 1.000000000000000000e+00 -9.638720154762268066e-01 8.660889863967895508e-01 2.474450021982192993e-01 1.000000000000000000e+00 -9.684690237045288086e-01 8.698189854621887207e-01 2.413100004196166992e-01 1.000000000000000000e+00 -9.731140136718750000e-01 8.735499978065490723e-01 2.346770018339157104e-01 1.000000000000000000e+00 -9.777799844741821289e-01 8.772810101509094238e-01 2.279540002346038818e-01 1.000000000000000000e+00 -9.824969768524169922e-01 8.810080289840698242e-01 2.208780050277709961e-01 1.000000000000000000e+00 -9.872930049896240234e-01 8.847180008888244629e-01 2.133360058069229126e-01 1.000000000000000000e+00 -9.922180175781250000e-01 8.883849978446960449e-01 2.054679989814758301e-01 1.000000000000000000e+00 -9.948469996452331543e-01 8.929539918899536133e-01 2.034450024366378784e-01 1.000000000000000000e+00 -9.952489733695983887e-01 8.983839750289916992e-01 2.075610011816024780e-01 1.000000000000000000e+00 -9.955030083656311035e-01 9.038659930229187012e-01 2.123699933290481567e-01 1.000000000000000000e+00 -9.957370162010192871e-01 9.093440175056457520e-01 2.177720069885253906e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/cool b/fastplotlib/utils/colormaps/cool deleted file mode 100644 index 4af027533..000000000 --- a/fastplotlib/utils/colormaps/cool +++ /dev/null @@ -1,256 +0,0 @@ -0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -3.921568859368562698e-03 9.960784316062927246e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.843137718737125397e-03 9.921568632125854492e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.176470611244440079e-02 9.882352948188781738e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.568627543747425079e-02 9.843137264251708984e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.960784383118152618e-02 9.803921580314636230e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.352941222488880157e-02 9.764705896377563477e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.745098061859607697e-02 9.725490212440490723e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.137255087494850159e-02 9.686274528503417969e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.529411926865577698e-02 9.647058844566345215e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.921568766236305237e-02 9.607843160629272461e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.313725605607032776e-02 9.568627476692199707e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.705882444977760315e-02 9.529411792755126953e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.098039284348487854e-02 9.490196108818054199e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.490196123719215393e-02 9.450980424880981445e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.882352963089942932e-02 9.411764740943908691e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.274510174989700317e-02 9.372549057006835938e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.666667014360427856e-02 9.333333373069763184e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.058823853731155396e-02 9.294117689132690430e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.450980693101882935e-02 9.254902005195617676e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.843137532472610474e-02 9.215686321258544922e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.235294371843338013e-02 9.176470637321472168e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.627451211214065552e-02 9.137254953384399414e-01 1.000000000000000000e+00 1.000000000000000000e+00 -9.019608050584793091e-02 9.098039269447326660e-01 1.000000000000000000e+00 1.000000000000000000e+00 -9.411764889955520630e-02 9.058823585510253906e-01 1.000000000000000000e+00 1.000000000000000000e+00 -9.803921729326248169e-02 9.019607901573181152e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.019607856869697571e-01 8.980392217636108398e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.058823540806770325e-01 8.941176533699035645e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.098039224743843079e-01 8.901960849761962891e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.137254908680915833e-01 8.862745165824890137e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.176470592617988586e-01 8.823529481887817383e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.215686276555061340e-01 8.784313797950744629e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.254902034997940063e-01 8.745098114013671875e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.294117718935012817e-01 8.705882430076599121e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.333333402872085571e-01 8.666666746139526367e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.372549086809158325e-01 8.627451062202453613e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.411764770746231079e-01 8.588235378265380859e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.450980454683303833e-01 8.549019694328308105e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.490196138620376587e-01 8.509804010391235352e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.529411822557449341e-01 8.470588326454162598e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.568627506494522095e-01 8.431372642517089844e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.607843190431594849e-01 8.392156958580017090e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.647058874368667603e-01 8.352941274642944336e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.686274558305740356e-01 8.313725590705871582e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.725490242242813110e-01 8.274509906768798828e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.764705926179885864e-01 8.235294222831726074e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.803921610116958618e-01 8.196078538894653320e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.843137294054031372e-01 8.156862854957580566e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.882352977991104126e-01 8.117647171020507812e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.921568661928176880e-01 8.078431487083435059e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.960784345865249634e-01 8.039215803146362305e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.000000029802322388e-01 8.000000119209289551e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.039215713739395142e-01 7.960784435272216797e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.078431397676467896e-01 7.921568751335144043e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.117647081613540649e-01 7.882353067398071289e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.156862765550613403e-01 7.843137383460998535e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.196078449487686157e-01 7.803921699523925781e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.235294133424758911e-01 7.764706015586853027e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.274509817361831665e-01 7.725490331649780273e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.313725501298904419e-01 7.686274647712707520e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.352941185235977173e-01 7.647058963775634766e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.392156869173049927e-01 7.607843279838562012e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.431372553110122681e-01 7.568627595901489258e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.470588237047195435e-01 7.529411911964416504e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.509804069995880127e-01 7.490196228027343750e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.549019753932952881e-01 7.450980544090270996e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.588235437870025635e-01 7.411764860153198242e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.627451121807098389e-01 7.372549176216125488e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.666666805744171143e-01 7.333333492279052734e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.705882489681243896e-01 7.294117808341979980e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.745098173618316650e-01 7.254902124404907227e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.784313857555389404e-01 7.215686440467834473e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.823529541492462158e-01 7.176470756530761719e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.862745225429534912e-01 7.137255072593688965e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.901960909366607666e-01 7.098039388656616211e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.941176593303680420e-01 7.058823704719543457e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.980392277240753174e-01 7.019608020782470703e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.019607961177825928e-01 6.980392336845397949e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.058823645114898682e-01 6.941176652908325195e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.098039329051971436e-01 6.901960968971252441e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.137255012989044189e-01 6.862745285034179688e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.176470696926116943e-01 6.823529601097106934e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.215686380863189697e-01 6.784313917160034180e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.254902064800262451e-01 6.745098233222961426e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.294117748737335205e-01 6.705882549285888672e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.333333432674407959e-01 6.666666865348815918e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.372549116611480713e-01 6.627451181411743164e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.411764800548553467e-01 6.588235497474670410e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.450980484485626221e-01 6.549019813537597656e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.490196168422698975e-01 6.509804129600524902e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.529411852359771729e-01 6.470588445663452148e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.568627536296844482e-01 6.431372761726379395e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.607843220233917236e-01 6.392157077789306641e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.647058904170989990e-01 6.352941393852233887e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.686274588108062744e-01 6.313725709915161133e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.725490272045135498e-01 6.274510025978088379e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.764705955982208252e-01 6.235294342041015625e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.803921639919281006e-01 6.196078658103942871e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.843137323856353760e-01 6.156862974166870117e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.882353007793426514e-01 6.117647290229797363e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.921568691730499268e-01 6.078431606292724609e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.960784375667572021e-01 6.039215922355651855e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.000000059604644775e-01 6.000000238418579102e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.039215743541717529e-01 5.960784554481506348e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.078431427478790283e-01 5.921568870544433594e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.117647111415863037e-01 5.882353186607360840e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.156862795352935791e-01 5.843137502670288086e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.196078479290008545e-01 5.803921818733215332e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.235294163227081299e-01 5.764706134796142578e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.274509847164154053e-01 5.725490450859069824e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.313725531101226807e-01 5.686274766921997070e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.352941215038299561e-01 5.647059082984924316e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.392156898975372314e-01 5.607843399047851562e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.431372582912445068e-01 5.568627715110778809e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.470588266849517822e-01 5.529412031173706055e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.509803950786590576e-01 5.490196347236633301e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.549019634723663330e-01 5.450980663299560547e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.588235318660736084e-01 5.411764979362487793e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.627451002597808838e-01 5.372549295425415039e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.666666686534881592e-01 5.333333611488342285e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.705882370471954346e-01 5.294117927551269531e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.745098054409027100e-01 5.254902243614196777e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.784313738346099854e-01 5.215686559677124023e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.823529422283172607e-01 5.176470875740051270e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.862745106220245361e-01 5.137255191802978516e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.901960790157318115e-01 5.098039507865905762e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.941176474094390869e-01 5.058823823928833008e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.980392158031463623e-01 5.019608139991760254e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.019608139991760254e-01 4.980392158031463623e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.058823823928833008e-01 4.941176474094390869e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.098039507865905762e-01 4.901960790157318115e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.137255191802978516e-01 4.862745106220245361e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.176470875740051270e-01 4.823529422283172607e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.215686559677124023e-01 4.784313738346099854e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.254902243614196777e-01 4.745098054409027100e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.294117927551269531e-01 4.705882370471954346e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.333333611488342285e-01 4.666666686534881592e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.372549295425415039e-01 4.627451002597808838e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.411764979362487793e-01 4.588235318660736084e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.450980663299560547e-01 4.549019634723663330e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.490196347236633301e-01 4.509803950786590576e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.529412031173706055e-01 4.470588266849517822e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.568627715110778809e-01 4.431372582912445068e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.607843399047851562e-01 4.392156898975372314e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.647059082984924316e-01 4.352941215038299561e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.686274766921997070e-01 4.313725531101226807e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.725490450859069824e-01 4.274509847164154053e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.764706134796142578e-01 4.235294163227081299e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.803921818733215332e-01 4.196078479290008545e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.843137502670288086e-01 4.156862795352935791e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.882353186607360840e-01 4.117647111415863037e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.921568870544433594e-01 4.078431427478790283e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.960784554481506348e-01 4.039215743541717529e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.000000238418579102e-01 4.000000059604644775e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.039215922355651855e-01 3.960784375667572021e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.078431606292724609e-01 3.921568691730499268e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.117647290229797363e-01 3.882353007793426514e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.156862974166870117e-01 3.843137323856353760e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.196078658103942871e-01 3.803921639919281006e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.235294342041015625e-01 3.764705955982208252e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.274510025978088379e-01 3.725490272045135498e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.313725709915161133e-01 3.686274588108062744e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.352941393852233887e-01 3.647058904170989990e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.392157077789306641e-01 3.607843220233917236e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.431372761726379395e-01 3.568627536296844482e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.470588445663452148e-01 3.529411852359771729e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.509804129600524902e-01 3.490196168422698975e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.549019813537597656e-01 3.450980484485626221e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.588235497474670410e-01 3.411764800548553467e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.627451181411743164e-01 3.372549116611480713e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.666666865348815918e-01 3.333333432674407959e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.705882549285888672e-01 3.294117748737335205e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.745098233222961426e-01 3.254902064800262451e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.784313917160034180e-01 3.215686380863189697e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.823529601097106934e-01 3.176470696926116943e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.862745285034179688e-01 3.137255012989044189e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.901960968971252441e-01 3.098039329051971436e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.941176652908325195e-01 3.058823645114898682e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.980392336845397949e-01 3.019607961177825928e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.019608020782470703e-01 2.980392277240753174e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.058823704719543457e-01 2.941176593303680420e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.098039388656616211e-01 2.901960909366607666e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.137255072593688965e-01 2.862745225429534912e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.176470756530761719e-01 2.823529541492462158e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.215686440467834473e-01 2.784313857555389404e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.254902124404907227e-01 2.745098173618316650e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.294117808341979980e-01 2.705882489681243896e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.333333492279052734e-01 2.666666805744171143e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.372549176216125488e-01 2.627451121807098389e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.411764860153198242e-01 2.588235437870025635e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.450980544090270996e-01 2.549019753932952881e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.490196228027343750e-01 2.509804069995880127e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.529411911964416504e-01 2.470588237047195435e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.568627595901489258e-01 2.431372553110122681e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.607843279838562012e-01 2.392156869173049927e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.647058963775634766e-01 2.352941185235977173e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.686274647712707520e-01 2.313725501298904419e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.725490331649780273e-01 2.274509817361831665e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.764706015586853027e-01 2.235294133424758911e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.803921699523925781e-01 2.196078449487686157e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.843137383460998535e-01 2.156862765550613403e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.882353067398071289e-01 2.117647081613540649e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.921568751335144043e-01 2.078431397676467896e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.960784435272216797e-01 2.039215713739395142e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.000000119209289551e-01 2.000000029802322388e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.039215803146362305e-01 1.960784345865249634e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.078431487083435059e-01 1.921568661928176880e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.117647171020507812e-01 1.882352977991104126e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.156862854957580566e-01 1.843137294054031372e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.196078538894653320e-01 1.803921610116958618e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.235294222831726074e-01 1.764705926179885864e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.274509906768798828e-01 1.725490242242813110e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.313725590705871582e-01 1.686274558305740356e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.352941274642944336e-01 1.647058874368667603e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.392156958580017090e-01 1.607843190431594849e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.431372642517089844e-01 1.568627506494522095e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.470588326454162598e-01 1.529411822557449341e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.509804010391235352e-01 1.490196138620376587e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.549019694328308105e-01 1.450980454683303833e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.588235378265380859e-01 1.411764770746231079e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.627451062202453613e-01 1.372549086809158325e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.666666746139526367e-01 1.333333402872085571e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.705882430076599121e-01 1.294117718935012817e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.745098114013671875e-01 1.254902034997940063e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.784313797950744629e-01 1.215686276555061340e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.823529481887817383e-01 1.176470592617988586e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.862745165824890137e-01 1.137254908680915833e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.901960849761962891e-01 1.098039224743843079e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.941176533699035645e-01 1.058823540806770325e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.980392217636108398e-01 1.019607856869697571e-01 1.000000000000000000e+00 1.000000000000000000e+00 -9.019607901573181152e-01 9.803921729326248169e-02 1.000000000000000000e+00 1.000000000000000000e+00 -9.058823585510253906e-01 9.411764889955520630e-02 1.000000000000000000e+00 1.000000000000000000e+00 -9.098039269447326660e-01 9.019608050584793091e-02 1.000000000000000000e+00 1.000000000000000000e+00 -9.137254953384399414e-01 8.627451211214065552e-02 1.000000000000000000e+00 1.000000000000000000e+00 -9.176470637321472168e-01 8.235294371843338013e-02 1.000000000000000000e+00 1.000000000000000000e+00 -9.215686321258544922e-01 7.843137532472610474e-02 1.000000000000000000e+00 1.000000000000000000e+00 -9.254902005195617676e-01 7.450980693101882935e-02 1.000000000000000000e+00 1.000000000000000000e+00 -9.294117689132690430e-01 7.058823853731155396e-02 1.000000000000000000e+00 1.000000000000000000e+00 -9.333333373069763184e-01 6.666667014360427856e-02 1.000000000000000000e+00 1.000000000000000000e+00 -9.372549057006835938e-01 6.274510174989700317e-02 1.000000000000000000e+00 1.000000000000000000e+00 -9.411764740943908691e-01 5.882352963089942932e-02 1.000000000000000000e+00 1.000000000000000000e+00 -9.450980424880981445e-01 5.490196123719215393e-02 1.000000000000000000e+00 1.000000000000000000e+00 -9.490196108818054199e-01 5.098039284348487854e-02 1.000000000000000000e+00 1.000000000000000000e+00 -9.529411792755126953e-01 4.705882444977760315e-02 1.000000000000000000e+00 1.000000000000000000e+00 -9.568627476692199707e-01 4.313725605607032776e-02 1.000000000000000000e+00 1.000000000000000000e+00 -9.607843160629272461e-01 3.921568766236305237e-02 1.000000000000000000e+00 1.000000000000000000e+00 -9.647058844566345215e-01 3.529411926865577698e-02 1.000000000000000000e+00 1.000000000000000000e+00 -9.686274528503417969e-01 3.137255087494850159e-02 1.000000000000000000e+00 1.000000000000000000e+00 -9.725490212440490723e-01 2.745098061859607697e-02 1.000000000000000000e+00 1.000000000000000000e+00 -9.764705896377563477e-01 2.352941222488880157e-02 1.000000000000000000e+00 1.000000000000000000e+00 -9.803921580314636230e-01 1.960784383118152618e-02 1.000000000000000000e+00 1.000000000000000000e+00 -9.843137264251708984e-01 1.568627543747425079e-02 1.000000000000000000e+00 1.000000000000000000e+00 -9.882352948188781738e-01 1.176470611244440079e-02 1.000000000000000000e+00 1.000000000000000000e+00 -9.921568632125854492e-01 7.843137718737125397e-03 1.000000000000000000e+00 1.000000000000000000e+00 -9.960784316062927246e-01 3.921568859368562698e-03 1.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/coolwarm b/fastplotlib/utils/colormaps/coolwarm deleted file mode 100644 index fc1d170e9..000000000 --- a/fastplotlib/utils/colormaps/coolwarm +++ /dev/null @@ -1,256 +0,0 @@ -2.298056930303573608e-01 2.987179756164550781e-01 7.536831498146057129e-01 1.000000000000000000e+00 -2.343770861625671387e-01 3.055417239665985107e-01 7.596795558929443359e-01 1.000000000000000000e+00 -2.389484643936157227e-01 3.123655021190643311e-01 7.656759023666381836e-01 1.000000000000000000e+00 -2.435198426246643066e-01 3.191892504692077637e-01 7.716722488403320312e-01 1.000000000000000000e+00 -2.480912208557128906e-01 3.260130286216735840e-01 7.776686549186706543e-01 1.000000000000000000e+00 -2.526625990867614746e-01 3.328367769718170166e-01 7.836650013923645020e-01 1.000000000000000000e+00 -2.572339773178100586e-01 3.396605551242828369e-01 7.896614074707031250e-01 1.000000000000000000e+00 -2.618053555488586426e-01 3.464843034744262695e-01 7.956577539443969727e-01 1.000000000000000000e+00 -2.663814723491668701e-01 3.533044159412384033e-01 8.016372919082641602e-01 1.000000000000000000e+00 -2.711043059825897217e-01 3.600106537342071533e-01 8.070951104164123535e-01 1.000000000000000000e+00 -2.758271098136901855e-01 3.667169213294982910e-01 8.125529289245605469e-01 1.000000000000000000e+00 -2.805499434471130371e-01 3.734231591224670410e-01 8.180107474327087402e-01 1.000000000000000000e+00 -2.852727770805358887e-01 3.801294267177581787e-01 8.234685659408569336e-01 1.000000000000000000e+00 -2.899956107139587402e-01 3.868356645107269287e-01 8.289263844490051270e-01 1.000000000000000000e+00 -2.947184443473815918e-01 3.935419321060180664e-01 8.343841433525085449e-01 1.000000000000000000e+00 -2.994412481784820557e-01 4.002481698989868164e-01 8.398419618606567383e-01 1.000000000000000000e+00 -3.041742742061614990e-01 4.069448709487915039e-01 8.452627062797546387e-01 1.000000000000000000e+00 -3.090603053569793701e-01 4.134982824325561523e-01 8.501276373863220215e-01 1.000000000000000000e+00 -3.139463365077972412e-01 4.200516641139984131e-01 8.549925684928894043e-01 1.000000000000000000e+00 -3.188323974609375000e-01 4.266050457954406738e-01 8.598574399948120117e-01 1.000000000000000000e+00 -3.237184286117553711e-01 4.331584274768829346e-01 8.647223711013793945e-01 1.000000000000000000e+00 -3.286044597625732422e-01 4.397118389606475830e-01 8.695872426033020020e-01 1.000000000000000000e+00 -3.334904909133911133e-01 4.462652206420898438e-01 8.744521737098693848e-01 1.000000000000000000e+00 -3.383765220642089844e-01 4.528186023235321045e-01 8.793171048164367676e-01 1.000000000000000000e+00 -3.432775139808654785e-01 4.593536257743835449e-01 8.841218948364257812e-01 1.000000000000000000e+00 -3.483233451843261719e-01 4.657111465930938721e-01 8.883461356163024902e-01 1.000000000000000000e+00 -3.533691465854644775e-01 4.720686674118041992e-01 8.925703763961791992e-01 1.000000000000000000e+00 -3.584149777889251709e-01 4.784261584281921387e-01 8.967946171760559082e-01 1.000000000000000000e+00 -3.634608089923858643e-01 4.847836792469024658e-01 9.010188579559326172e-01 1.000000000000000000e+00 -3.685066103935241699e-01 4.911412000656127930e-01 9.052430987358093262e-01 1.000000000000000000e+00 -3.735524415969848633e-01 4.974986910820007324e-01 9.094673991203308105e-01 1.000000000000000000e+00 -3.785982429981231689e-01 5.038562417030334473e-01 9.136916399002075195e-01 1.000000000000000000e+00 -3.836620748043060303e-01 5.101833939552307129e-01 9.178306460380554199e-01 1.000000000000000000e+00 -3.888518810272216797e-01 5.162984132766723633e-01 9.213734865188598633e-01 1.000000000000000000e+00 -3.940416872501373291e-01 5.224134325981140137e-01 9.249162673950195312e-01 1.000000000000000000e+00 -3.992314934730529785e-01 5.285284519195556641e-01 9.284591078758239746e-01 1.000000000000000000e+00 -4.044212996959686279e-01 5.346434712409973145e-01 9.320018887519836426e-01 1.000000000000000000e+00 -4.096111059188842773e-01 5.407584905624389648e-01 9.355447292327880859e-01 1.000000000000000000e+00 -4.148009121417999268e-01 5.468735098838806152e-01 9.390875101089477539e-01 1.000000000000000000e+00 -4.199907183647155762e-01 5.529885292053222656e-01 9.426303505897521973e-01 1.000000000000000000e+00 -4.251989722251892090e-01 5.590581893920898438e-01 9.460614323616027832e-01 1.000000000000000000e+00 -4.305068850517272949e-01 5.648827552795410156e-01 9.488894343376159668e-01 1.000000000000000000e+00 -4.358147978782653809e-01 5.707073211669921875e-01 9.517173767089843750e-01 1.000000000000000000e+00 -4.411227107048034668e-01 5.765318870544433594e-01 9.545453190803527832e-01 1.000000000000000000e+00 -4.464306533336639404e-01 5.823564529418945312e-01 9.573733210563659668e-01 1.000000000000000000e+00 -4.517385661602020264e-01 5.881809592247009277e-01 9.602012634277343750e-01 1.000000000000000000e+00 -4.570464789867401123e-01 5.940055251121520996e-01 9.630292057991027832e-01 1.000000000000000000e+00 -4.623543918132781982e-01 5.998300909996032715e-01 9.658572077751159668e-01 1.000000000000000000e+00 -4.676780998706817627e-01 6.055912375450134277e-01 9.685462713241577148e-01 1.000000000000000000e+00 -4.730701744556427002e-01 6.110774278640747070e-01 9.706335663795471191e-01 1.000000000000000000e+00 -4.784622490406036377e-01 6.165636181831359863e-01 9.727209210395812988e-01 1.000000000000000000e+00 -4.838543236255645752e-01 6.220498681068420410e-01 9.748082160949707031e-01 1.000000000000000000e+00 -4.892463982105255127e-01 6.275360584259033203e-01 9.768955111503601074e-01 1.000000000000000000e+00 -4.946384727954864502e-01 6.330222487449645996e-01 9.789828062057495117e-01 1.000000000000000000e+00 -5.000305771827697754e-01 6.385084390640258789e-01 9.810701012611389160e-01 1.000000000000000000e+00 -5.054226517677307129e-01 6.439946889877319336e-01 9.831574559211730957e-01 1.000000000000000000e+00 -5.108243227005004883e-01 6.493965983390808105e-01 9.850787520408630371e-01 1.000000000000000000e+00 -5.162603259086608887e-01 6.544976234436035156e-01 9.864073991775512695e-01 1.000000000000000000e+00 -5.216962695121765137e-01 6.595985889434814453e-01 9.877360463142395020e-01 1.000000000000000000e+00 -5.271322727203369141e-01 6.646996140480041504e-01 9.890646338462829590e-01 1.000000000000000000e+00 -5.325682163238525391e-01 6.698005795478820801e-01 9.903932809829711914e-01 1.000000000000000000e+00 -5.380042195320129395e-01 6.749016046524047852e-01 9.917218685150146484e-01 1.000000000000000000e+00 -5.434402227401733398e-01 6.800025701522827148e-01 9.930505156517028809e-01 1.000000000000000000e+00 -5.488761663436889648e-01 6.851035952568054199e-01 9.943791627883911133e-01 1.000000000000000000e+00 -5.543118715286254883e-01 6.900970339775085449e-01 9.955155253410339355e-01 1.000000000000000000e+00 -5.597467422485351562e-01 6.947677135467529297e-01 9.960753321647644043e-01 1.000000000000000000e+00 -5.651815533638000488e-01 6.994384527206420898e-01 9.966350793838500977e-01 1.000000000000000000e+00 -5.706164240837097168e-01 7.041091322898864746e-01 9.971948266029357910e-01 1.000000000000000000e+00 -5.760512948036193848e-01 7.087798714637756348e-01 9.977545738220214844e-01 1.000000000000000000e+00 -5.814861655235290527e-01 7.134506106376647949e-01 9.983143806457519531e-01 1.000000000000000000e+00 -5.869209766387939453e-01 7.181212902069091797e-01 9.988741278648376465e-01 1.000000000000000000e+00 -5.923558473587036133e-01 7.227920293807983398e-01 9.994338750839233398e-01 1.000000000000000000e+00 -5.977767705917358398e-01 7.273297309875488281e-01 9.997767210006713867e-01 1.000000000000000000e+00 -6.031620502471923828e-01 7.315274477005004883e-01 9.995653033256530762e-01 1.000000000000000000e+00 -6.085473895072937012e-01 7.357252240180969238e-01 9.993538260459899902e-01 1.000000000000000000e+00 -6.139326691627502441e-01 7.399230003356933594e-01 9.991423487663269043e-01 1.000000000000000000e+00 -6.193179488182067871e-01 7.441207170486450195e-01 9.989309310913085938e-01 1.000000000000000000e+00 -6.247032284736633301e-01 7.483184933662414551e-01 9.987194538116455078e-01 1.000000000000000000e+00 -6.300885081291198730e-01 7.525162100791931152e-01 9.985080361366271973e-01 1.000000000000000000e+00 -6.354738473892211914e-01 7.567139863967895508e-01 9.982965588569641113e-01 1.000000000000000000e+00 -6.408277750015258789e-01 7.607514858245849609e-01 9.978457689285278320e-01 1.000000000000000000e+00 -6.461127996444702148e-01 7.644364833831787109e-01 9.968684911727905273e-01 1.000000000000000000e+00 -6.513978242874145508e-01 7.681214809417724609e-01 9.958911538124084473e-01 1.000000000000000000e+00 -6.566828489303588867e-01 7.718064785003662109e-01 9.949138164520263672e-01 1.000000000000000000e+00 -6.619678735733032227e-01 7.754914760589599609e-01 9.939365386962890625e-01 1.000000000000000000e+00 -6.672528982162475586e-01 7.791764736175537109e-01 9.929592013359069824e-01 1.000000000000000000e+00 -6.725379824638366699e-01 7.828614711761474609e-01 9.919819235801696777e-01 1.000000000000000000e+00 -6.778230071067810059e-01 7.865464091300964355e-01 9.910045862197875977e-01 1.000000000000000000e+00 -6.830556988716125488e-01 7.900426387786865234e-01 9.897684454917907715e-01 1.000000000000000000e+00 -6.881884932518005371e-01 7.931783795356750488e-01 9.880381226539611816e-01 1.000000000000000000e+00 -6.933212876319885254e-01 7.963141202926635742e-01 9.863077998161315918e-01 1.000000000000000000e+00 -6.984540820121765137e-01 7.994498610496520996e-01 9.845774769783020020e-01 1.000000000000000000e+00 -7.035868763923645020e-01 8.025856614112854004e-01 9.828471541404724121e-01 1.000000000000000000e+00 -7.087196707725524902e-01 8.057214021682739258e-01 9.811168313026428223e-01 1.000000000000000000e+00 -7.138524651527404785e-01 8.088571429252624512e-01 9.793865084648132324e-01 1.000000000000000000e+00 -7.189853191375732422e-01 8.119928836822509766e-01 9.776561856269836426e-01 1.000000000000000000e+00 -7.240413427352905273e-01 8.149104118347167969e-01 9.756509661674499512e-01 1.000000000000000000e+00 -7.289695739746093750e-01 8.174641132354736328e-01 9.731876850128173828e-01 1.000000000000000000e+00 -7.338978052139282227e-01 8.200178742408752441e-01 9.707243442535400391e-01 1.000000000000000000e+00 -7.388259768486022949e-01 8.225716352462768555e-01 9.682610630989074707e-01 1.000000000000000000e+00 -7.437542080879211426e-01 8.251253366470336914e-01 9.657977819442749023e-01 1.000000000000000000e+00 -7.486824393272399902e-01 8.276790976524353027e-01 9.633344411849975586e-01 1.000000000000000000e+00 -7.536106109619140625e-01 8.302328586578369141e-01 9.608711600303649902e-01 1.000000000000000000e+00 -7.585388422012329102e-01 8.327866196632385254e-01 9.584078788757324219e-01 1.000000000000000000e+00 -7.633627653121948242e-01 8.350922465324401855e-01 9.556576609611511230e-01 1.000000000000000000e+00 -7.680343389511108398e-01 8.370352387428283691e-01 9.524882435798645020e-01 1.000000000000000000e+00 -7.727059721946716309e-01 8.389782309532165527e-01 9.493187665939331055e-01 1.000000000000000000e+00 -7.773775458335876465e-01 8.409212231636047363e-01 9.461492896080017090e-01 1.000000000000000000e+00 -7.820491194725036621e-01 8.428642153739929199e-01 9.429798722267150879e-01 1.000000000000000000e+00 -7.867206931114196777e-01 8.448072075843811035e-01 9.398103952407836914e-01 1.000000000000000000e+00 -7.913922667503356934e-01 8.467501997947692871e-01 9.366409182548522949e-01 1.000000000000000000e+00 -7.960638403892517090e-01 8.486931920051574707e-01 9.334714412689208984e-01 1.000000000000000000e+00 -8.006008267402648926e-01 8.503583073616027832e-01 9.300075769424438477e-01 1.000000000000000000e+00 -8.049647808074951172e-01 8.516661524772644043e-01 9.261651039123535156e-01 1.000000000000000000e+00 -8.093286752700805664e-01 8.529739975929260254e-01 9.223225712776184082e-01 1.000000000000000000e+00 -8.136925697326660156e-01 8.542818427085876465e-01 9.184800982475280762e-01 1.000000000000000000e+00 -8.180564641952514648e-01 8.555896878242492676e-01 9.146376252174377441e-01 1.000000000000000000e+00 -8.224204182624816895e-01 8.568975329399108887e-01 9.107951521873474121e-01 1.000000000000000000e+00 -8.267843127250671387e-01 8.582053780555725098e-01 9.069526195526123047e-01 1.000000000000000000e+00 -8.311482071876525879e-01 8.595132231712341309e-01 9.031101465225219727e-01 1.000000000000000000e+00 -8.353447318077087402e-01 8.605139851570129395e-01 8.989704251289367676e-01 1.000000000000000000e+00 -8.393514156341552734e-01 8.611668348312377930e-01 8.944937586784362793e-01 1.000000000000000000e+00 -8.433581590652465820e-01 8.618196249008178711e-01 8.900170922279357910e-01 1.000000000000000000e+00 -8.473649024963378906e-01 8.624724745750427246e-01 8.855404853820800781e-01 1.000000000000000000e+00 -8.513716459274291992e-01 8.631253242492675781e-01 8.810638189315795898e-01 1.000000000000000000e+00 -8.553783893585205078e-01 8.637781143188476562e-01 8.765871524810791016e-01 1.000000000000000000e+00 -8.593850731849670410e-01 8.644309639930725098e-01 8.721105456352233887e-01 1.000000000000000000e+00 -8.633918166160583496e-01 8.650838136672973633e-01 8.676338791847229004e-01 1.000000000000000000e+00 -8.674276471138000488e-01 8.643766045570373535e-01 8.626024723052978516e-01 1.000000000000000000e+00 -8.714925050735473633e-01 8.623093962669372559e-01 8.570162653923034668e-01 1.000000000000000000e+00 -8.755573630332946777e-01 8.602421879768371582e-01 8.514300584793090820e-01 1.000000000000000000e+00 -8.796222805976867676e-01 8.581749200820922852e-01 8.458438515663146973e-01 1.000000000000000000e+00 -8.836871385574340820e-01 8.561077117919921875e-01 8.402576446533203125e-01 1.000000000000000000e+00 -8.877519965171813965e-01 8.540405035018920898e-01 8.346714973449707031e-01 1.000000000000000000e+00 -8.918169140815734863e-01 8.519732952117919922e-01 8.290852904319763184e-01 1.000000000000000000e+00 -8.958817720413208008e-01 8.499060273170471191e-01 8.234990835189819336e-01 1.000000000000000000e+00 -8.995432257652282715e-01 8.475002646446228027e-01 8.177890777587890625e-01 1.000000000000000000e+00 -9.028486609458923340e-01 8.447956442832946777e-01 8.119698166847229004e-01 1.000000000000000000e+00 -9.061541557312011719e-01 8.420910835266113281e-01 8.061506152153015137e-01 1.000000000000000000e+00 -9.094595909118652344e-01 8.393864631652832031e-01 8.003313541412353516e-01 1.000000000000000000e+00 -9.127650856971740723e-01 8.366819024085998535e-01 7.945120930671691895e-01 1.000000000000000000e+00 -9.160705208778381348e-01 8.339772820472717285e-01 7.886928915977478027e-01 1.000000000000000000e+00 -9.193760156631469727e-01 8.312727212905883789e-01 7.828736305236816406e-01 1.000000000000000000e+00 -9.226814508438110352e-01 8.285681605339050293e-01 7.770543694496154785e-01 1.000000000000000000e+00 -9.255633950233459473e-01 8.255172967910766602e-01 7.711362838745117188e-01 1.000000000000000000e+00 -9.281160235404968262e-01 8.221971392631530762e-01 7.651413679122924805e-01 1.000000000000000000e+00 -9.306685924530029297e-01 8.188769817352294922e-01 7.591463923454284668e-01 1.000000000000000000e+00 -9.332211613655090332e-01 8.155568242073059082e-01 7.531514167785644531e-01 1.000000000000000000e+00 -9.357737898826599121e-01 8.122367262840270996e-01 7.471565008163452148e-01 1.000000000000000000e+00 -9.383263587951660156e-01 8.089165687561035156e-01 7.411615252494812012e-01 1.000000000000000000e+00 -9.408789277076721191e-01 8.055964112281799316e-01 7.351665496826171875e-01 1.000000000000000000e+00 -9.434315562248229980e-01 8.022762537002563477e-01 7.291715741157531738e-01 1.000000000000000000e+00 -9.455403089523315430e-01 7.986057400703430176e-01 7.231054306030273438e-01 1.000000000000000000e+00 -9.473453760147094727e-01 7.946954965591430664e-01 7.169905304908752441e-01 1.000000000000000000e+00 -9.491505026817321777e-01 7.907852530479431152e-01 7.108755707740783691e-01 1.000000000000000000e+00 -9.509556293487548828e-01 7.868750095367431641e-01 7.047606706619262695e-01 1.000000000000000000e+00 -9.527606964111328125e-01 7.829648256301879883e-01 6.986457705497741699e-01 1.000000000000000000e+00 -9.545658230781555176e-01 7.790545821189880371e-01 6.925308704376220703e-01 1.000000000000000000e+00 -9.563709497451782227e-01 7.751443386077880859e-01 6.864159703254699707e-01 1.000000000000000000e+00 -9.581760168075561523e-01 7.712340950965881348e-01 6.803010106086730957e-01 1.000000000000000000e+00 -9.595176577568054199e-01 7.669728398323059082e-01 6.741446852684020996e-01 1.000000000000000000e+00 -9.605811834335327148e-01 7.625010013580322266e-01 6.679635643959045410e-01 1.000000000000000000e+00 -9.616447091102600098e-01 7.580291628837585449e-01 6.617823839187622070e-01 1.000000000000000000e+00 -9.627082943916320801e-01 7.535573244094848633e-01 6.556012034416198730e-01 1.000000000000000000e+00 -9.637718200683593750e-01 7.490854859352111816e-01 6.494200229644775391e-01 1.000000000000000000e+00 -9.648353457450866699e-01 7.446136474609375000e-01 6.432389020919799805e-01 1.000000000000000000e+00 -9.658988714218139648e-01 7.401418089866638184e-01 6.370577216148376465e-01 1.000000000000000000e+00 -9.669624567031860352e-01 7.356700301170349121e-01 6.308765411376953125e-01 1.000000000000000000e+00 -9.675443172454833984e-01 7.308497428894042969e-01 6.246854662895202637e-01 1.000000000000000000e+00 -9.678738713264465332e-01 7.258468866348266602e-01 6.184892058372497559e-01 1.000000000000000000e+00 -9.682034254074096680e-01 7.208440899848937988e-01 6.122930049896240234e-01 1.000000000000000000e+00 -9.685329198837280273e-01 7.158412933349609375e-01 6.060967445373535156e-01 1.000000000000000000e+00 -9.688624739646911621e-01 7.108384966850280762e-01 5.999004840850830078e-01 1.000000000000000000e+00 -9.691920280456542969e-01 7.058357000350952148e-01 5.937042832374572754e-01 1.000000000000000000e+00 -9.695215821266174316e-01 7.008328437805175781e-01 5.875080227851867676e-01 1.000000000000000000e+00 -9.698511362075805664e-01 6.958300471305847168e-01 5.813117623329162598e-01 1.000000000000000000e+00 -9.696829915046691895e-01 6.904839277267456055e-01 5.751383900642395020e-01 1.000000000000000000e+00 -9.692885875701904297e-01 6.849817633628845215e-01 5.689753293991088867e-01 1.000000000000000000e+00 -9.688941836357116699e-01 6.794795393943786621e-01 5.628122687339782715e-01 1.000000000000000000e+00 -9.684997200965881348e-01 6.739773750305175781e-01 5.566492676734924316e-01 1.000000000000000000e+00 -9.681053161621093750e-01 6.684752106666564941e-01 5.504862070083618164e-01 1.000000000000000000e+00 -9.677109122276306152e-01 6.629729866981506348e-01 5.443232059478759766e-01 1.000000000000000000e+00 -9.673165082931518555e-01 6.574708223342895508e-01 5.381601452827453613e-01 1.000000000000000000e+00 -9.669221043586730957e-01 6.519686579704284668e-01 5.319971442222595215e-01 1.000000000000000000e+00 -9.660167098045349121e-01 6.461297273635864258e-01 5.258903503417968750e-01 1.000000000000000000e+00 -9.649114012718200684e-01 6.401590704917907715e-01 5.198056101799011230e-01 1.000000000000000000e+00 -9.638060331344604492e-01 6.341884136199951172e-01 5.137208700180053711e-01 1.000000000000000000e+00 -9.627007246017456055e-01 6.282177567481994629e-01 5.076360702514648438e-01 1.000000000000000000e+00 -9.615954160690307617e-01 6.222470998764038086e-01 5.015513300895690918e-01 1.000000000000000000e+00 -9.604900479316711426e-01 6.162764430046081543e-01 4.954665899276733398e-01 1.000000000000000000e+00 -9.593847393989562988e-01 6.103057861328125000e-01 4.893818497657775879e-01 1.000000000000000000e+00 -9.582793712615966797e-01 6.043350696563720703e-01 4.832971096038818359e-01 1.000000000000000000e+00 -9.566532373428344727e-01 5.980338454246520996e-01 4.773022830486297607e-01 1.000000000000000000e+00 -9.548534154891967773e-01 5.916223526000976562e-01 4.713374674320220947e-01 1.000000000000000000e+00 -9.530535936355590820e-01 5.852108597755432129e-01 4.653726220130920410e-01 1.000000000000000000e+00 -9.512537717819213867e-01 5.787993669509887695e-01 4.594078063964843750e-01 1.000000000000000000e+00 -9.494540095329284668e-01 5.723879337310791016e-01 4.534429907798767090e-01 1.000000000000000000e+00 -9.476541876792907715e-01 5.659764409065246582e-01 4.474781453609466553e-01 1.000000000000000000e+00 -9.458543658256530762e-01 5.595649480819702148e-01 4.415133297443389893e-01 1.000000000000000000e+00 -9.440545439720153809e-01 5.531534552574157715e-01 4.355484843254089355e-01 1.000000000000000000e+00 -9.417279362678527832e-01 5.464134812355041504e-01 4.297070801258087158e-01 1.000000000000000000e+00 -9.392537474632263184e-01 5.395814776420593262e-01 4.239002168178558350e-01 1.000000000000000000e+00 -9.367796182632446289e-01 5.327494740486145020e-01 4.180933535099029541e-01 1.000000000000000000e+00 -9.343054294586181641e-01 5.259175300598144531e-01 4.122864603996276855e-01 1.000000000000000000e+00 -9.318313002586364746e-01 5.190855264663696289e-01 4.064795970916748047e-01 1.000000000000000000e+00 -9.293571114540100098e-01 5.122535228729248047e-01 4.006727337837219238e-01 1.000000000000000000e+00 -9.268829822540283203e-01 5.054215192794799805e-01 3.948658704757690430e-01 1.000000000000000000e+00 -9.244087934494018555e-01 4.985895454883575439e-01 3.890590071678161621e-01 1.000000000000000000e+00 -9.214062094688415527e-01 4.914204180240631104e-01 3.834084272384643555e-01 1.000000000000000000e+00 -9.182816743850708008e-01 4.841734766960144043e-01 3.777939379215240479e-01 1.000000000000000000e+00 -9.151571393013000488e-01 4.769265353679656982e-01 3.721794188022613525e-01 1.000000000000000000e+00 -9.120326042175292969e-01 4.696795940399169922e-01 3.665648996829986572e-01 1.000000000000000000e+00 -9.089080095291137695e-01 4.624326229095458984e-01 3.609503805637359619e-01 1.000000000000000000e+00 -9.057834744453430176e-01 4.551856815814971924e-01 3.553358912467956543e-01 1.000000000000000000e+00 -9.026589393615722656e-01 4.479387402534484863e-01 3.497213721275329590e-01 1.000000000000000000e+00 -8.995344042778015137e-01 4.406917989253997803e-01 3.441068530082702637e-01 1.000000000000000000e+00 -8.958845734596252441e-01 4.330745637416839600e-01 3.386806249618530273e-01 1.000000000000000000e+00 -8.921375274658203125e-01 4.253887236118316650e-01 3.332892656326293945e-01 1.000000000000000000e+00 -8.883904814720153809e-01 4.177029132843017578e-01 3.278979063034057617e-01 1.000000000000000000e+00 -8.846434354782104492e-01 4.100171029567718506e-01 3.225065469741821289e-01 1.000000000000000000e+00 -8.808963894844055176e-01 4.023312926292419434e-01 3.171151876449584961e-01 1.000000000000000000e+00 -8.771493434906005859e-01 3.946454524993896484e-01 3.117238283157348633e-01 1.000000000000000000e+00 -8.734022974967956543e-01 3.869596421718597412e-01 3.063324689865112305e-01 1.000000000000000000e+00 -8.696552515029907227e-01 3.792738318443298340e-01 3.009411096572875977e-01 1.000000000000000000e+00 -8.653913140296936035e-01 3.711276650428771973e-01 2.957689464092254639e-01 1.000000000000000000e+00 -8.610535860061645508e-01 3.629157543182373047e-01 2.906281352043151855e-01 1.000000000000000000e+00 -8.567158579826354980e-01 3.547038435935974121e-01 2.854872941970825195e-01 1.000000000000000000e+00 -8.523781299591064453e-01 3.464919328689575195e-01 2.803464829921722412e-01 1.000000000000000000e+00 -8.480404019355773926e-01 3.382800519466400146e-01 2.752056419849395752e-01 1.000000000000000000e+00 -8.437026739120483398e-01 3.300681412220001221e-01 2.700648009777069092e-01 1.000000000000000000e+00 -8.393649458885192871e-01 3.218562304973602295e-01 2.649239897727966309e-01 1.000000000000000000e+00 -8.350272178649902344e-01 3.136443197727203369e-01 2.597831487655639648e-01 1.000000000000000000e+00 -8.301865458488464355e-01 3.047327697277069092e-01 2.548914253711700439e-01 1.000000000000000000e+00 -8.252938389778137207e-01 2.957488298416137695e-01 2.500254809856414795e-01 1.000000000000000000e+00 -8.204010725021362305e-01 2.867649197578430176e-01 2.451595216989517212e-01 1.000000000000000000e+00 -8.155083656311035156e-01 2.777809798717498779e-01 2.402935624122619629e-01 1.000000000000000000e+00 -8.106156587600708008e-01 2.687970697879791260e-01 2.354276180267333984e-01 1.000000000000000000e+00 -8.057229518890380859e-01 2.598131299018859863e-01 2.305616587400436401e-01 1.000000000000000000e+00 -8.008302450180053711e-01 2.508292198181152344e-01 2.256956994533538818e-01 1.000000000000000000e+00 -7.959375381469726562e-01 2.418452799320220947e-01 2.208297550678253174e-01 1.000000000000000000e+00 -7.905615568161010742e-01 2.313970029354095459e-01 2.162420451641082764e-01 1.000000000000000000e+00 -7.851533293724060059e-01 2.208510935306549072e-01 2.116728723049163818e-01 1.000000000000000000e+00 -7.797451019287109375e-01 2.103051841259002686e-01 2.071037143468856812e-01 1.000000000000000000e+00 -7.743368744850158691e-01 1.997592747211456299e-01 2.025345563888549805e-01 1.000000000000000000e+00 -7.689286470413208008e-01 1.892133504152297974e-01 1.979653984308242798e-01 1.000000000000000000e+00 -7.635204195976257324e-01 1.786674410104751587e-01 1.933962255716323853e-01 1.000000000000000000e+00 -7.581121921539306641e-01 1.681215316057205200e-01 1.888270676136016846e-01 1.000000000000000000e+00 -7.527039647102355957e-01 1.575756222009658813e-01 1.842579096555709839e-01 1.000000000000000000e+00 -7.468380331993103027e-01 1.400210261344909668e-01 1.799961030483245850e-01 1.000000000000000000e+00 -7.409573197364807129e-01 1.222403272986412048e-01 1.757442057132720947e-01 1.000000000000000000e+00 -7.350766062736511230e-01 1.044596284627914429e-01 1.714923083782196045e-01 1.000000000000000000e+00 -7.291959524154663086e-01 8.667893707752227783e-02 1.672403961420059204e-01 1.000000000000000000e+00 -7.233152389526367188e-01 6.889824569225311279e-02 1.629884988069534302e-01 1.000000000000000000e+00 -7.174345254898071289e-01 5.111754685640335083e-02 1.587366014719009399e-01 1.000000000000000000e+00 -7.115538716316223145e-01 3.333685547113418579e-02 1.544847041368484497e-01 1.000000000000000000e+00 -7.056731581687927246e-01 1.555616036057472229e-02 1.502328068017959595e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/copper b/fastplotlib/utils/colormaps/copper deleted file mode 100644 index aad93521a..000000000 --- a/fastplotlib/utils/colormaps/copper +++ /dev/null @@ -1,256 +0,0 @@ -0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.844289738684892654e-03 3.063529497012495995e-03 1.950980396941304207e-03 1.000000000000000000e+00 -9.688579477369785309e-03 6.127058994024991989e-03 3.901960793882608414e-03 1.000000000000000000e+00 -1.453286875039339066e-02 9.190588258206844330e-03 5.852940957993268967e-03 1.000000000000000000e+00 -1.937715895473957062e-02 1.225411798804998398e-02 7.803921587765216827e-03 1.000000000000000000e+00 -2.422144822776317596e-02 1.531764678657054901e-02 9.754901751875877380e-03 1.000000000000000000e+00 -2.906573750078678131e-02 1.838117651641368866e-02 1.170588191598653793e-02 1.000000000000000000e+00 -3.391002491116523743e-02 2.144470624625682831e-02 1.365686301141977310e-02 1.000000000000000000e+00 -3.875431790947914124e-02 2.450823597609996796e-02 1.560784317553043365e-02 1.000000000000000000e+00 -4.359860718250274658e-02 2.757176384329795837e-02 1.755882427096366882e-02 1.000000000000000000e+00 -4.844289645552635193e-02 3.063529357314109802e-02 1.950980350375175476e-02 1.000000000000000000e+00 -5.328718572854995728e-02 3.369882330298423767e-02 2.146078459918498993e-02 1.000000000000000000e+00 -5.813147500157356262e-02 3.676235303282737732e-02 2.341176383197307587e-02 1.000000000000000000e+00 -6.297576427459716797e-02 3.982588276267051697e-02 2.536274492740631104e-02 1.000000000000000000e+00 -6.782004982233047485e-02 4.288941249251365662e-02 2.731372602283954620e-02 1.000000000000000000e+00 -7.266434282064437866e-02 4.595294222235679626e-02 2.926470525562763214e-02 1.000000000000000000e+00 -7.750863581895828247e-02 4.901647195219993591e-02 3.121568635106086731e-02 1.000000000000000000e+00 -8.235292136669158936e-02 5.208000168204307556e-02 3.316666558384895325e-02 1.000000000000000000e+00 -8.719721436500549316e-02 5.514352768659591675e-02 3.511764854192733765e-02 1.000000000000000000e+00 -9.204149991273880005e-02 5.820705741643905640e-02 3.706862777471542358e-02 1.000000000000000000e+00 -9.688579291105270386e-02 6.127058714628219604e-02 3.901960700750350952e-02 1.000000000000000000e+00 -1.017300784587860107e-01 6.433411687612533569e-02 4.097058996558189392e-02 1.000000000000000000e+00 -1.065743714570999146e-01 6.739764660596847534e-02 4.292156919836997986e-02 1.000000000000000000e+00 -1.114186570048332214e-01 7.046117633581161499e-02 4.487254843115806580e-02 1.000000000000000000e+00 -1.162629500031471252e-01 7.352470606565475464e-02 4.682352766394615173e-02 1.000000000000000000e+00 -1.211072355508804321e-01 7.658823579549789429e-02 4.877451062202453613e-02 1.000000000000000000e+00 -1.259515285491943359e-01 7.965176552534103394e-02 5.072548985481262207e-02 1.000000000000000000e+00 -1.307958215475082397e-01 8.271529525518417358e-02 5.267646908760070801e-02 1.000000000000000000e+00 -1.356400996446609497e-01 8.577882498502731323e-02 5.462745204567909241e-02 1.000000000000000000e+00 -1.404843926429748535e-01 8.884235471487045288e-02 5.657843127846717834e-02 1.000000000000000000e+00 -1.453286856412887573e-01 9.190588444471359253e-02 5.852941051125526428e-02 1.000000000000000000e+00 -1.501729786396026611e-01 9.496941417455673218e-02 6.048039346933364868e-02 1.000000000000000000e+00 -1.550172716379165649e-01 9.803294390439987183e-02 6.243137270212173462e-02 1.000000000000000000e+00 -1.598615497350692749e-01 1.010964736342430115e-01 6.438235193490982056e-02 1.000000000000000000e+00 -1.647058427333831787e-01 1.041600033640861511e-01 6.633333116769790649e-02 1.000000000000000000e+00 -1.695501357316970825e-01 1.072235330939292908e-01 6.828431040048599243e-02 1.000000000000000000e+00 -1.743944287300109863e-01 1.102870553731918335e-01 7.023529708385467529e-02 1.000000000000000000e+00 -1.792387068271636963e-01 1.133505851030349731e-01 7.218627631664276123e-02 1.000000000000000000e+00 -1.840829998254776001e-01 1.164141148328781128e-01 7.413725554943084717e-02 1.000000000000000000e+00 -1.889272928237915039e-01 1.194776445627212524e-01 7.608823478221893311e-02 1.000000000000000000e+00 -1.937715858221054077e-01 1.225411742925643921e-01 7.803921401500701904e-02 1.000000000000000000e+00 -1.986158639192581177e-01 1.256047040224075317e-01 7.999019324779510498e-02 1.000000000000000000e+00 -2.034601569175720215e-01 1.286682337522506714e-01 8.194117993116378784e-02 1.000000000000000000e+00 -2.083044499158859253e-01 1.317317634820938110e-01 8.389215916395187378e-02 1.000000000000000000e+00 -2.131487429141998291e-01 1.347952932119369507e-01 8.584313839673995972e-02 1.000000000000000000e+00 -2.179930210113525391e-01 1.378588229417800903e-01 8.779411762952804565e-02 1.000000000000000000e+00 -2.228373140096664429e-01 1.409223526716232300e-01 8.974509686231613159e-02 1.000000000000000000e+00 -2.276816070079803467e-01 1.439858824014663696e-01 9.169607609510421753e-02 1.000000000000000000e+00 -2.325259000062942505e-01 1.470494121313095093e-01 9.364705532789230347e-02 1.000000000000000000e+00 -2.373701930046081543e-01 1.501129418611526489e-01 9.559804201126098633e-02 1.000000000000000000e+00 -2.422144711017608643e-01 1.531764715909957886e-01 9.754902124404907227e-02 1.000000000000000000e+00 -2.470587641000747681e-01 1.562400013208389282e-01 9.950000047683715820e-02 1.000000000000000000e+00 -2.519030570983886719e-01 1.593035310506820679e-01 1.014509797096252441e-01 1.000000000000000000e+00 -2.567473351955413818e-01 1.623670607805252075e-01 1.034019589424133301e-01 1.000000000000000000e+00 -2.615916430950164795e-01 1.654305905103683472e-01 1.053529381752014160e-01 1.000000000000000000e+00 -2.664359211921691895e-01 1.684941202402114868e-01 1.073039248585700989e-01 1.000000000000000000e+00 -2.712801992893218994e-01 1.715576499700546265e-01 1.092549040913581848e-01 1.000000000000000000e+00 -2.761245071887969971e-01 1.746211796998977661e-01 1.112058833241462708e-01 1.000000000000000000e+00 -2.809687852859497070e-01 1.776847094297409058e-01 1.131568625569343567e-01 1.000000000000000000e+00 -2.858130931854248047e-01 1.807482391595840454e-01 1.151078417897224426e-01 1.000000000000000000e+00 -2.906573712825775146e-01 1.838117688894271851e-01 1.170588210225105286e-01 1.000000000000000000e+00 -2.955016493797302246e-01 1.868752986192703247e-01 1.190098002552986145e-01 1.000000000000000000e+00 -3.003459572792053223e-01 1.899388283491134644e-01 1.209607869386672974e-01 1.000000000000000000e+00 -3.051902353763580322e-01 1.930023580789566040e-01 1.229117661714553833e-01 1.000000000000000000e+00 -3.100345432758331299e-01 1.960658878087997437e-01 1.248627454042434692e-01 1.000000000000000000e+00 -3.148788213729858398e-01 1.991294175386428833e-01 1.268137246370315552e-01 1.000000000000000000e+00 -3.197230994701385498e-01 2.021929472684860229e-01 1.287647038698196411e-01 1.000000000000000000e+00 -3.245674073696136475e-01 2.052564769983291626e-01 1.307156831026077271e-01 1.000000000000000000e+00 -3.294116854667663574e-01 2.083200067281723022e-01 1.326666623353958130e-01 1.000000000000000000e+00 -3.342559635639190674e-01 2.113835364580154419e-01 1.346176415681838989e-01 1.000000000000000000e+00 -3.391002714633941650e-01 2.144470661878585815e-01 1.365686208009719849e-01 1.000000000000000000e+00 -3.439445495605468750e-01 2.175105810165405273e-01 1.385196149349212646e-01 1.000000000000000000e+00 -3.487888574600219727e-01 2.205741107463836670e-01 1.404705941677093506e-01 1.000000000000000000e+00 -3.536331355571746826e-01 2.236376404762268066e-01 1.424215734004974365e-01 1.000000000000000000e+00 -3.584774136543273926e-01 2.267011702060699463e-01 1.443725526332855225e-01 1.000000000000000000e+00 -3.633217215538024902e-01 2.297646999359130859e-01 1.463235318660736084e-01 1.000000000000000000e+00 -3.681659996509552002e-01 2.328282296657562256e-01 1.482745110988616943e-01 1.000000000000000000e+00 -3.730103075504302979e-01 2.358917593955993652e-01 1.502254903316497803e-01 1.000000000000000000e+00 -3.778545856475830078e-01 2.389552891254425049e-01 1.521764695644378662e-01 1.000000000000000000e+00 -3.826988637447357178e-01 2.420188188552856445e-01 1.541274487972259521e-01 1.000000000000000000e+00 -3.875431716442108154e-01 2.450823485851287842e-01 1.560784280300140381e-01 1.000000000000000000e+00 -3.923874497413635254e-01 2.481458783149719238e-01 1.580294072628021240e-01 1.000000000000000000e+00 -3.972317278385162354e-01 2.512094080448150635e-01 1.599803864955902100e-01 1.000000000000000000e+00 -4.020760357379913330e-01 2.542729377746582031e-01 1.619313657283782959e-01 1.000000000000000000e+00 -4.069203138351440430e-01 2.573364675045013428e-01 1.638823598623275757e-01 1.000000000000000000e+00 -4.117646217346191406e-01 2.603999972343444824e-01 1.658333390951156616e-01 1.000000000000000000e+00 -4.166088998317718506e-01 2.634635269641876221e-01 1.677843183279037476e-01 1.000000000000000000e+00 -4.214531779289245605e-01 2.665270566940307617e-01 1.697352975606918335e-01 1.000000000000000000e+00 -4.262974858283996582e-01 2.695905864238739014e-01 1.716862767934799194e-01 1.000000000000000000e+00 -4.311417639255523682e-01 2.726541161537170410e-01 1.736372560262680054e-01 1.000000000000000000e+00 -4.359860420227050781e-01 2.757176458835601807e-01 1.755882352590560913e-01 1.000000000000000000e+00 -4.408303499221801758e-01 2.787811756134033203e-01 1.775392144918441772e-01 1.000000000000000000e+00 -4.456746280193328857e-01 2.818447053432464600e-01 1.794901937246322632e-01 1.000000000000000000e+00 -4.505189359188079834e-01 2.849082350730895996e-01 1.814411729574203491e-01 1.000000000000000000e+00 -4.553632140159606934e-01 2.879717648029327393e-01 1.833921521902084351e-01 1.000000000000000000e+00 -4.602074921131134033e-01 2.910352945327758789e-01 1.853431314229965210e-01 1.000000000000000000e+00 -4.650518000125885010e-01 2.940988242626190186e-01 1.872941106557846069e-01 1.000000000000000000e+00 -4.698960781097412109e-01 2.971623539924621582e-01 1.892451047897338867e-01 1.000000000000000000e+00 -4.747403860092163086e-01 3.002258837223052979e-01 1.911960840225219727e-01 1.000000000000000000e+00 -4.795846641063690186e-01 3.032894134521484375e-01 1.931470632553100586e-01 1.000000000000000000e+00 -4.844289422035217285e-01 3.063529431819915771e-01 1.950980424880981445e-01 1.000000000000000000e+00 -4.892732501029968262e-01 3.094164729118347168e-01 1.970490217208862305e-01 1.000000000000000000e+00 -4.941175282001495361e-01 3.124800026416778564e-01 1.990000009536743164e-01 1.000000000000000000e+00 -4.989618062973022461e-01 3.155435323715209961e-01 2.009509801864624023e-01 1.000000000000000000e+00 -5.038061141967773438e-01 3.186070621013641357e-01 2.029019594192504883e-01 1.000000000000000000e+00 -5.086504220962524414e-01 3.216705918312072754e-01 2.048529386520385742e-01 1.000000000000000000e+00 -5.134946703910827637e-01 3.247341215610504150e-01 2.068039178848266602e-01 1.000000000000000000e+00 -5.183389782905578613e-01 3.277976512908935547e-01 2.087548971176147461e-01 1.000000000000000000e+00 -5.231832861900329590e-01 3.308611810207366943e-01 2.107058763504028320e-01 1.000000000000000000e+00 -5.280275344848632812e-01 3.339247107505798340e-01 2.126568555831909180e-01 1.000000000000000000e+00 -5.328718423843383789e-01 3.369882404804229736e-01 2.146078497171401978e-01 1.000000000000000000e+00 -5.377161502838134766e-01 3.400517702102661133e-01 2.165588289499282837e-01 1.000000000000000000e+00 -5.425603985786437988e-01 3.431152999401092529e-01 2.185098081827163696e-01 1.000000000000000000e+00 -5.474047064781188965e-01 3.461788296699523926e-01 2.204607874155044556e-01 1.000000000000000000e+00 -5.522490143775939941e-01 3.492423593997955322e-01 2.224117666482925415e-01 1.000000000000000000e+00 -5.570933222770690918e-01 3.523058891296386719e-01 2.243627458810806274e-01 1.000000000000000000e+00 -5.619375705718994141e-01 3.553694188594818115e-01 2.263137251138687134e-01 1.000000000000000000e+00 -5.667818784713745117e-01 3.584329485893249512e-01 2.282647043466567993e-01 1.000000000000000000e+00 -5.716261863708496094e-01 3.614964783191680908e-01 2.302156835794448853e-01 1.000000000000000000e+00 -5.764704346656799316e-01 3.645600080490112305e-01 2.321666628122329712e-01 1.000000000000000000e+00 -5.813147425651550293e-01 3.676235377788543701e-01 2.341176420450210571e-01 1.000000000000000000e+00 -5.861590504646301270e-01 3.706870675086975098e-01 2.360686212778091431e-01 1.000000000000000000e+00 -5.910032987594604492e-01 3.737505972385406494e-01 2.380196005105972290e-01 1.000000000000000000e+00 -5.958476066589355469e-01 3.768141269683837891e-01 2.399705946445465088e-01 1.000000000000000000e+00 -6.006919145584106445e-01 3.798776566982269287e-01 2.419215738773345947e-01 1.000000000000000000e+00 -6.055361628532409668e-01 3.829411864280700684e-01 2.438725531101226807e-01 1.000000000000000000e+00 -6.103804707527160645e-01 3.860047161579132080e-01 2.458235323429107666e-01 1.000000000000000000e+00 -6.152247786521911621e-01 3.890682458877563477e-01 2.477745115756988525e-01 1.000000000000000000e+00 -6.200690865516662598e-01 3.921317756175994873e-01 2.497254908084869385e-01 1.000000000000000000e+00 -6.249133348464965820e-01 3.951953053474426270e-01 2.516764700412750244e-01 1.000000000000000000e+00 -6.297576427459716797e-01 3.982588350772857666e-01 2.536274492740631104e-01 1.000000000000000000e+00 -6.346019506454467773e-01 4.013223648071289062e-01 2.555784285068511963e-01 1.000000000000000000e+00 -6.394461989402770996e-01 4.043858945369720459e-01 2.575294077396392822e-01 1.000000000000000000e+00 -6.442905068397521973e-01 4.074494242668151855e-01 2.594803869724273682e-01 1.000000000000000000e+00 -6.491348147392272949e-01 4.105129539966583252e-01 2.614313662052154541e-01 1.000000000000000000e+00 -6.539790630340576172e-01 4.135764837265014648e-01 2.633823454380035400e-01 1.000000000000000000e+00 -6.588233709335327148e-01 4.166400134563446045e-01 2.653333246707916260e-01 1.000000000000000000e+00 -6.636676788330078125e-01 4.197035431861877441e-01 2.672843039035797119e-01 1.000000000000000000e+00 -6.685119271278381348e-01 4.227670729160308838e-01 2.692352831363677979e-01 1.000000000000000000e+00 -6.733562350273132324e-01 4.258306026458740234e-01 2.711862623691558838e-01 1.000000000000000000e+00 -6.782005429267883301e-01 4.288941323757171631e-01 2.731372416019439697e-01 1.000000000000000000e+00 -6.830448508262634277e-01 4.319576323032379150e-01 2.750882208347320557e-01 1.000000000000000000e+00 -6.878890991210937500e-01 4.350211620330810547e-01 2.770392298698425293e-01 1.000000000000000000e+00 -6.927334070205688477e-01 4.380846917629241943e-01 2.789902091026306152e-01 1.000000000000000000e+00 -6.975777149200439453e-01 4.411482214927673340e-01 2.809411883354187012e-01 1.000000000000000000e+00 -7.024219632148742676e-01 4.442117512226104736e-01 2.828921675682067871e-01 1.000000000000000000e+00 -7.072662711143493652e-01 4.472752809524536133e-01 2.848431468009948730e-01 1.000000000000000000e+00 -7.121105790138244629e-01 4.503388106822967529e-01 2.867941260337829590e-01 1.000000000000000000e+00 -7.169548273086547852e-01 4.534023404121398926e-01 2.887451052665710449e-01 1.000000000000000000e+00 -7.217991352081298828e-01 4.564658701419830322e-01 2.906960844993591309e-01 1.000000000000000000e+00 -7.266434431076049805e-01 4.595293998718261719e-01 2.926470637321472168e-01 1.000000000000000000e+00 -7.314876914024353027e-01 4.625929296016693115e-01 2.945980429649353027e-01 1.000000000000000000e+00 -7.363319993019104004e-01 4.656564593315124512e-01 2.965490221977233887e-01 1.000000000000000000e+00 -7.411763072013854980e-01 4.687199890613555908e-01 2.985000014305114746e-01 1.000000000000000000e+00 -7.460206151008605957e-01 4.717835187911987305e-01 3.004509806632995605e-01 1.000000000000000000e+00 -7.508648633956909180e-01 4.748470485210418701e-01 3.024019598960876465e-01 1.000000000000000000e+00 -7.557091712951660156e-01 4.779105782508850098e-01 3.043529391288757324e-01 1.000000000000000000e+00 -7.605534791946411133e-01 4.809741079807281494e-01 3.063039183616638184e-01 1.000000000000000000e+00 -7.653977274894714355e-01 4.840376377105712891e-01 3.082548975944519043e-01 1.000000000000000000e+00 -7.702420353889465332e-01 4.871011674404144287e-01 3.102058768272399902e-01 1.000000000000000000e+00 -7.750863432884216309e-01 4.901646971702575684e-01 3.121568560600280762e-01 1.000000000000000000e+00 -7.799305915832519531e-01 4.932282269001007080e-01 3.141078352928161621e-01 1.000000000000000000e+00 -7.847748994827270508e-01 4.962917566299438477e-01 3.160588145256042480e-01 1.000000000000000000e+00 -7.896192073822021484e-01 4.993552863597869873e-01 3.180097937583923340e-01 1.000000000000000000e+00 -7.944634556770324707e-01 5.024188160896301270e-01 3.199607729911804199e-01 1.000000000000000000e+00 -7.993077635765075684e-01 5.054823756217956543e-01 3.219117522239685059e-01 1.000000000000000000e+00 -8.041520714759826660e-01 5.085458755493164062e-01 3.238627314567565918e-01 1.000000000000000000e+00 -8.089963197708129883e-01 5.116094350814819336e-01 3.258137106895446777e-01 1.000000000000000000e+00 -8.138406276702880859e-01 5.146729350090026855e-01 3.277647197246551514e-01 1.000000000000000000e+00 -8.186849355697631836e-01 5.177364945411682129e-01 3.297156989574432373e-01 1.000000000000000000e+00 -8.235292434692382812e-01 5.207999944686889648e-01 3.316666781902313232e-01 1.000000000000000000e+00 -8.283734917640686035e-01 5.238635540008544922e-01 3.336176574230194092e-01 1.000000000000000000e+00 -8.332177996635437012e-01 5.269270539283752441e-01 3.355686366558074951e-01 1.000000000000000000e+00 -8.380621075630187988e-01 5.299906134605407715e-01 3.375196158885955811e-01 1.000000000000000000e+00 -8.429063558578491211e-01 5.330541133880615234e-01 3.394705951213836670e-01 1.000000000000000000e+00 -8.477506637573242188e-01 5.361176729202270508e-01 3.414215743541717529e-01 1.000000000000000000e+00 -8.525949716567993164e-01 5.391811728477478027e-01 3.433725535869598389e-01 1.000000000000000000e+00 -8.574392199516296387e-01 5.422447323799133301e-01 3.453235328197479248e-01 1.000000000000000000e+00 -8.622835278511047363e-01 5.453082323074340820e-01 3.472745120525360107e-01 1.000000000000000000e+00 -8.671278357505798340e-01 5.483717918395996094e-01 3.492254912853240967e-01 1.000000000000000000e+00 -8.719720840454101562e-01 5.514352917671203613e-01 3.511764705181121826e-01 1.000000000000000000e+00 -8.768163919448852539e-01 5.544988512992858887e-01 3.531274497509002686e-01 1.000000000000000000e+00 -8.816606998443603516e-01 5.575623512268066406e-01 3.550784289836883545e-01 1.000000000000000000e+00 -8.865050077438354492e-01 5.606259107589721680e-01 3.570294082164764404e-01 1.000000000000000000e+00 -8.913492560386657715e-01 5.636894106864929199e-01 3.589803874492645264e-01 1.000000000000000000e+00 -8.961935639381408691e-01 5.667529702186584473e-01 3.609313666820526123e-01 1.000000000000000000e+00 -9.010378718376159668e-01 5.698164701461791992e-01 3.628823459148406982e-01 1.000000000000000000e+00 -9.058821201324462891e-01 5.728800296783447266e-01 3.648333251476287842e-01 1.000000000000000000e+00 -9.107264280319213867e-01 5.759435296058654785e-01 3.667843043804168701e-01 1.000000000000000000e+00 -9.155707359313964844e-01 5.790070295333862305e-01 3.687352836132049561e-01 1.000000000000000000e+00 -9.204149842262268066e-01 5.820705890655517578e-01 3.706862628459930420e-01 1.000000000000000000e+00 -9.252592921257019043e-01 5.851340889930725098e-01 3.726372420787811279e-01 1.000000000000000000e+00 -9.301036000251770020e-01 5.881976485252380371e-01 3.745882213115692139e-01 1.000000000000000000e+00 -9.349478483200073242e-01 5.912611484527587891e-01 3.765392303466796875e-01 1.000000000000000000e+00 -9.397921562194824219e-01 5.943247079849243164e-01 3.784902095794677734e-01 1.000000000000000000e+00 -9.446364641189575195e-01 5.973882079124450684e-01 3.804411888122558594e-01 1.000000000000000000e+00 -9.494807720184326172e-01 6.004517674446105957e-01 3.823921680450439453e-01 1.000000000000000000e+00 -9.543250203132629395e-01 6.035152673721313477e-01 3.843431472778320312e-01 1.000000000000000000e+00 -9.591693282127380371e-01 6.065788269042968750e-01 3.862941265106201172e-01 1.000000000000000000e+00 -9.640136361122131348e-01 6.096423268318176270e-01 3.882451057434082031e-01 1.000000000000000000e+00 -9.688578844070434570e-01 6.127058863639831543e-01 3.901960849761962891e-01 1.000000000000000000e+00 -9.737021923065185547e-01 6.157693862915039062e-01 3.921470642089843750e-01 1.000000000000000000e+00 -9.785465002059936523e-01 6.188329458236694336e-01 3.940980434417724609e-01 1.000000000000000000e+00 -9.833907485008239746e-01 6.218964457511901855e-01 3.960490226745605469e-01 1.000000000000000000e+00 -9.882350564002990723e-01 6.249600052833557129e-01 3.980000019073486328e-01 1.000000000000000000e+00 -9.930793642997741699e-01 6.280235052108764648e-01 3.999509811401367188e-01 1.000000000000000000e+00 -9.979236125946044922e-01 6.310870647430419922e-01 4.019019603729248047e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.341505646705627441e-01 4.038529396057128906e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.372141242027282715e-01 4.058039188385009766e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.402776241302490234e-01 4.077548980712890625e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.433411836624145508e-01 4.097058773040771484e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.464046835899353027e-01 4.116568565368652344e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.494682431221008301e-01 4.136078357696533203e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.525317430496215820e-01 4.155588150024414062e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.555953025817871094e-01 4.175097942352294922e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.586588025093078613e-01 4.194607734680175781e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.617223620414733887e-01 4.214117527008056641e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.647858619689941406e-01 4.233627319335937500e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.678494215011596680e-01 4.253137111663818359e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.709129214286804199e-01 4.272647202014923096e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.739764809608459473e-01 4.292156994342803955e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.770399808883666992e-01 4.311666786670684814e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.801035404205322266e-01 4.331176578998565674e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.831670403480529785e-01 4.350686371326446533e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.862305998802185059e-01 4.370196163654327393e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.892940998077392578e-01 4.389705955982208252e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.923576593399047852e-01 4.409215748310089111e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.954211592674255371e-01 4.428725540637969971e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.984847187995910645e-01 4.448235332965850830e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.015482187271118164e-01 4.467745125293731689e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.046117782592773438e-01 4.487254917621612549e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.076752781867980957e-01 4.506764709949493408e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.107388377189636230e-01 4.526274502277374268e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.138023376464843750e-01 4.545784294605255127e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.168658971786499023e-01 4.565294086933135986e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.199293971061706543e-01 4.584803879261016846e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.229929566383361816e-01 4.604313671588897705e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.260564565658569336e-01 4.623823463916778564e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.291200160980224609e-01 4.643333256244659424e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.321835160255432129e-01 4.662843048572540283e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.352470755577087402e-01 4.682352840900421143e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.383105754852294922e-01 4.701862633228302002e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.413741350173950195e-01 4.721372425556182861e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.444376349449157715e-01 4.740882217884063721e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.475011944770812988e-01 4.760392010211944580e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.505646944046020508e-01 4.779902100563049316e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.536282539367675781e-01 4.799411892890930176e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.566917538642883301e-01 4.818921685218811035e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.597553133964538574e-01 4.838431477546691895e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.628188133239746094e-01 4.857941269874572754e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.658823728561401367e-01 4.877451062202453613e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.689458727836608887e-01 4.896960854530334473e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.720094323158264160e-01 4.916470646858215332e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.750729322433471680e-01 4.935980439186096191e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.781364917755126953e-01 4.955490231513977051e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.811999917030334473e-01 4.975000023841857910e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/cubehelix b/fastplotlib/utils/colormaps/cubehelix deleted file mode 100644 index 58103ea52..000000000 --- a/fastplotlib/utils/colormaps/cubehelix +++ /dev/null @@ -1,256 +0,0 @@ -0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.716294679790735245e-03 2.118574455380439758e-03 5.970232654362916946e-03 1.000000000000000000e+00 -1.325241569429636002e-02 4.287499003112316132e-03 1.216178853064775467e-02 1.000000000000000000e+00 -1.959919743239879608e-02 6.513601168990135193e-03 1.856303960084915161e-02 1.000000000000000000e+00 -2.574810385704040527e-02 8.803482167422771454e-03 2.516186796128749847e-02 1.000000000000000000e+00 -3.169123455882072449e-02 1.116350293159484863e-02 3.194570168852806091e-02 1.000000000000000000e+00 -3.742133826017379761e-02 1.359977014362812042e-02 3.890155255794525146e-02 1.000000000000000000e+00 -4.293183609843254089e-02 1.611812412738800049e-02 4.601604491472244263e-02 1.000000000000000000e+00 -4.821681976318359375e-02 1.872412860393524170e-02 5.327545478940010071e-02 1.000000000000000000e+00 -5.327106639742851257e-02 2.142305485904216766e-02 6.066573783755302429e-02 1.000000000000000000e+00 -5.809004604816436768e-02 2.421987615525722504e-02 6.817258149385452271e-02 1.000000000000000000e+00 -6.266992539167404175e-02 2.711925655603408813e-02 7.578142732381820679e-02 1.000000000000000000e+00 -6.700757890939712524e-02 3.012553974986076355e-02 8.347751200199127197e-02 1.000000000000000000e+00 -7.110057771205902100e-02 3.324273973703384399e-02 9.124591946601867676e-02 1.000000000000000000e+00 -7.494720071554183960e-02 3.647453710436820984e-02 9.907157719135284424e-02 1.000000000000000000e+00 -7.854642719030380249e-02 3.982427716255187988e-02 1.069393530488014221e-01 1.000000000000000000e+00 -8.189795911312103271e-02 4.329494759440422058e-02 1.148340404033660889e-01 1.000000000000000000e+00 -8.500218391418457031e-02 4.688918963074684143e-02 1.227404102683067322e-01 1.000000000000000000e+00 -8.786017447710037231e-02 5.060928687453269958e-02 1.306432783603668213e-01 1.000000000000000000e+00 -9.047371149063110352e-02 5.445716157555580139e-02 1.385274827480316162e-01 1.000000000000000000e+00 -9.284523874521255493e-02 5.843437463045120239e-02 1.463779956102371216e-01 1.000000000000000000e+00 -9.497788548469543457e-02 6.254211813211441040e-02 1.541798710823059082e-01 1.000000000000000000e+00 -9.687542170286178589e-02 6.678122282028198242e-02 1.619183719158172607e-01 1.000000000000000000e+00 -9.854228049516677856e-02 7.115215808153152466e-02 1.695789247751235962e-01 1.000000000000000000e+00 -9.998352825641632080e-02 7.565501332283020020e-02 1.771471947431564331e-01 1.000000000000000000e+00 -1.012048274278640747e-01 8.028952032327651978e-02 1.846091449260711670e-01 1.000000000000000000e+00 -1.022124737501144409e-01 8.505506813526153564e-02 1.919510066509246826e-01 1.000000000000000000e+00 -1.030133217573165894e-01 8.995065093040466309e-02 1.991593539714813232e-01 1.000000000000000000e+00 -1.036147922277450562e-01 9.497494250535964966e-02 2.062211036682128906e-01 1.000000000000000000e+00 -1.040248721837997437e-01 1.001262441277503967e-01 2.131235897541046143e-01 1.000000000000000000e+00 -1.042520552873611450e-01 1.054025292396545410e-01 2.198545634746551514e-01 1.000000000000000000e+00 -1.043053343892097473e-01 1.108014136552810669e-01 2.264022082090377808e-01 1.000000000000000000e+00 -1.041941866278648376e-01 1.163201928138732910e-01 2.327551990747451782e-01 1.000000000000000000e+00 -1.039285436272621155e-01 1.219558417797088623e-01 2.389027029275894165e-01 1.000000000000000000e+00 -1.035187691450119019e-01 1.277050077915191650e-01 2.448344230651855469e-01 1.000000000000000000e+00 -1.029756292700767517e-01 1.335640400648117065e-01 2.505405843257904053e-01 1.000000000000000000e+00 -1.023102551698684692e-01 1.395289897918701172e-01 2.560120224952697754e-01 1.000000000000000000e+00 -1.015341356396675110e-01 1.455956101417541504e-01 2.612401545047760010e-01 1.000000000000000000e+00 -1.006590873003005981e-01 1.517593860626220703e-01 2.662169635295867920e-01 1.000000000000000000e+00 -9.969720989465713501e-02 1.580155491828918457e-01 2.709350883960723877e-01 1.000000000000000000e+00 -9.866087138652801514e-02 1.643590480089187622e-01 2.753878235816955566e-01 1.000000000000000000e+00 -9.756266325712203979e-02 1.707846075296401978e-01 2.795691490173339844e-01 1.000000000000000000e+00 -9.641540795564651489e-02 1.772867143154144287e-01 2.834736406803131104e-01 1.000000000000000000e+00 -9.523206204175949097e-02 1.838596761226654053e-01 2.870965898036956787e-01 1.000000000000000000e+00 -9.402576088905334473e-02 1.904975324869155884e-01 2.904340028762817383e-01 1.000000000000000000e+00 -9.280972182750701904e-02 1.971942037343978882e-01 2.934825420379638672e-01 1.000000000000000000e+00 -9.159726649522781372e-02 2.039433866739273071e-01 2.962396442890167236e-01 1.000000000000000000e+00 -9.040175378322601318e-02 2.107386440038681030e-01 2.987034320831298828e-01 1.000000000000000000e+00 -8.923655748367309570e-02 2.175733894109725952e-01 3.008726537227630615e-01 1.000000000000000000e+00 -8.811505138874053955e-02 2.244409173727035522e-01 3.027469515800476074e-01 1.000000000000000000e+00 -8.705056458711624146e-02 2.313344031572341919e-01 3.043265044689178467e-01 1.000000000000000000e+00 -8.605633676052093506e-02 2.382469177246093750e-01 3.056123554706573486e-01 1.000000000000000000e+00 -8.514551818370819092e-02 2.451714724302291870e-01 3.066062033176422119e-01 1.000000000000000000e+00 -8.433111011981964111e-02 2.521010041236877441e-01 3.073104321956634521e-01 1.000000000000000000e+00 -8.362597227096557617e-02 2.590283751487731934e-01 3.077281713485717773e-01 1.000000000000000000e+00 -8.304274082183837891e-02 2.659464776515960693e-01 3.078632354736328125e-01 1.000000000000000000e+00 -8.259385824203491211e-02 2.728480994701385498e-01 3.077201247215270996e-01 1.000000000000000000e+00 -8.229149132966995239e-02 2.797261476516723633e-01 3.073040246963500977e-01 1.000000000000000000e+00 -8.214754611253738403e-02 2.865734398365020752e-01 3.066208362579345703e-01 1.000000000000000000e+00 -8.217360824346542358e-02 2.933828532695770264e-01 3.056769967079162598e-01 1.000000000000000000e+00 -8.238093554973602295e-02 3.001473844051361084e-01 3.044796884059906006e-01 1.000000000000000000e+00 -8.278044313192367554e-02 3.068599998950958252e-01 3.030366599559783936e-01 1.000000000000000000e+00 -8.338262885808944702e-02 3.135137856006622314e-01 3.013563454151153564e-01 1.000000000000000000e+00 -8.419763296842575073e-02 3.201019465923309326e-01 2.994476556777954102e-01 1.000000000000000000e+00 -8.523511886596679688e-02 3.266177773475646973e-01 2.973201274871826172e-01 1.000000000000000000e+00 -8.650432527065277100e-02 3.330547213554382324e-01 2.949838638305664062e-01 1.000000000000000000e+00 -8.801401406526565552e-02 3.394063115119934082e-01 2.924494743347167969e-01 1.000000000000000000e+00 -8.977246284484863281e-02 3.456662893295288086e-01 2.897280752658843994e-01 1.000000000000000000e+00 -9.178742021322250366e-02 3.518285453319549561e-01 2.868312299251556396e-01 1.000000000000000000e+00 -9.406612068414688110e-02 3.578871488571166992e-01 2.837709784507751465e-01 1.000000000000000000e+00 -9.661524742841720581e-02 3.638363778591156006e-01 2.805597782135009766e-01 1.000000000000000000e+00 -9.944093972444534302e-02 3.696707487106323242e-01 2.772105336189270020e-01 1.000000000000000000e+00 -1.025487333536148071e-01 3.753849267959594727e-01 2.737364470958709717e-01 1.000000000000000000e+00 -1.059436127543449402e-01 3.809739053249359131e-01 2.701511085033416748e-01 1.000000000000000000e+00 -1.096299365162849426e-01 3.864328265190124512e-01 2.664684057235717773e-01 1.000000000000000000e+00 -1.136114671826362610e-01 3.917571604251861572e-01 2.627024948596954346e-01 1.000000000000000000e+00 -1.178913488984107971e-01 3.969425857067108154e-01 2.588678300380706787e-01 1.000000000000000000e+00 -1.224720999598503113e-01 4.019851386547088623e-01 2.549790441989898682e-01 1.000000000000000000e+00 -1.273556202650070190e-01 4.068810641765594482e-01 2.510509788990020752e-01 1.000000000000000000e+00 -1.325431615114212036e-01 4.116269350051879883e-01 2.470986098051071167e-01 1.000000000000000000e+00 -1.380353271961212158e-01 4.162196218967437744e-01 2.431370615959167480e-01 1.000000000000000000e+00 -1.438320875167846680e-01 4.206563234329223633e-01 2.391815632581710815e-01 1.000000000000000000e+00 -1.499328017234802246e-01 4.249344766139984131e-01 2.352473586797714233e-01 1.000000000000000000e+00 -1.563361436128616333e-01 4.290519356727600098e-01 2.313497662544250488e-01 1.000000000000000000e+00 -1.630401760339736938e-01 4.330068230628967285e-01 2.275040894746780396e-01 1.000000000000000000e+00 -1.700423210859298706e-01 4.367975890636444092e-01 2.237255573272705078e-01 1.000000000000000000e+00 -1.773393601179122925e-01 4.404230713844299316e-01 2.200293689966201782e-01 1.000000000000000000e+00 -1.849274486303329468e-01 4.438824057579040527e-01 2.164306044578552246e-01 1.000000000000000000e+00 -1.928021460771560669e-01 4.471750557422637939e-01 2.129441946744918823e-01 1.000000000000000000e+00 -2.009583711624145508e-01 4.503008425235748291e-01 2.095849364995956421e-01 1.000000000000000000e+00 -2.093905061483383179e-01 4.532599449157714844e-01 2.063674032688140869e-01 1.000000000000000000e+00 -2.180922627449035645e-01 4.560528099536895752e-01 2.033059448003768921e-01 1.000000000000000000e+00 -2.270568460226058960e-01 4.586803615093231201e-01 2.004146575927734375e-01 1.000000000000000000e+00 -2.362768501043319702e-01 4.611437022686004639e-01 1.977073252201080322e-01 1.000000000000000000e+00 -2.457443773746490479e-01 4.634443819522857666e-01 1.951974183320999146e-01 1.000000000000000000e+00 -2.554509639739990234e-01 4.655842483043670654e-01 1.928980797529220581e-01 1.000000000000000000e+00 -2.653876245021820068e-01 4.675654768943786621e-01 1.908220648765563965e-01 1.000000000000000000e+00 -2.755448818206787109e-01 4.693906307220458984e-01 1.889817118644714355e-01 1.000000000000000000e+00 -2.859128713607788086e-01 4.710624516010284424e-01 1.873889416456222534e-01 1.000000000000000000e+00 -2.964811027050018311e-01 4.725841879844665527e-01 1.860552132129669189e-01 1.000000000000000000e+00 -3.072388172149658203e-01 4.739592075347900391e-01 1.849915087223052979e-01 1.000000000000000000e+00 -3.181747496128082275e-01 4.751913249492645264e-01 1.842083036899566650e-01 1.000000000000000000e+00 -3.292773067951202393e-01 4.762845635414123535e-01 1.837155520915985107e-01 1.000000000000000000e+00 -3.405344486236572266e-01 4.772432744503021240e-01 1.835226714611053467e-01 1.000000000000000000e+00 -3.519338667392730713e-01 4.780721068382263184e-01 1.836384832859039307e-01 1.000000000000000000e+00 -3.634629249572753906e-01 4.787759184837341309e-01 1.840712577104568481e-01 1.000000000000000000e+00 -3.751086592674255371e-01 4.793598949909210205e-01 1.848286241292953491e-01 1.000000000000000000e+00 -3.868579268455505371e-01 4.798294007778167725e-01 1.859176158905029297e-01 1.000000000000000000e+00 -3.986972570419311523e-01 4.801900684833526611e-01 1.873446404933929443e-01 1.000000000000000000e+00 -4.106130301952362061e-01 4.804477989673614502e-01 1.891154348850250244e-01 1.000000000000000000e+00 -4.225914180278778076e-01 4.806086421012878418e-01 1.912350803613662720e-01 1.000000000000000000e+00 -4.346184432506561279e-01 4.806789159774780273e-01 1.937079876661300659e-01 1.000000000000000000e+00 -4.466800093650817871e-01 4.806650280952453613e-01 1.965378969907760620e-01 1.000000000000000000e+00 -4.587619900703430176e-01 4.805736541748046875e-01 1.997278481721878052e-01 1.000000000000000000e+00 -4.708500504493713379e-01 4.804116189479827881e-01 2.032801955938339233e-01 1.000000000000000000e+00 -4.829299449920654297e-01 4.801858663558959961e-01 2.071965634822845459e-01 1.000000000000000000e+00 -4.949873983860015869e-01 4.799034893512725830e-01 2.114778906106948853e-01 1.000000000000000000e+00 -5.070081353187561035e-01 4.795717000961303711e-01 2.161244302988052368e-01 1.000000000000000000e+00 -5.189779400825500488e-01 4.791978001594543457e-01 2.211356908082962036e-01 1.000000000000000000e+00 -5.308826565742492676e-01 4.787892103195190430e-01 2.265104800462722778e-01 1.000000000000000000e+00 -5.427082777023315430e-01 4.783534109592437744e-01 2.322469204664230347e-01 1.000000000000000000e+00 -5.544409155845642090e-01 4.778979718685150146e-01 2.383424043655395508e-01 1.000000000000000000e+00 -5.660668611526489258e-01 4.774304330348968506e-01 2.447936534881591797e-01 1.000000000000000000e+00 -5.775726437568664551e-01 4.769584238529205322e-01 2.515966892242431641e-01 1.000000000000000000e+00 -5.889448523521423340e-01 4.764896035194396973e-01 2.587468326091766357e-01 1.000000000000000000e+00 -6.001705527305603027e-01 4.760315716266632080e-01 2.662387788295745850e-01 1.000000000000000000e+00 -6.112369298934936523e-01 4.755919277667999268e-01 2.740665078163146973e-01 1.000000000000000000e+00 -6.221315264701843262e-01 4.751782715320587158e-01 2.822233736515045166e-01 1.000000000000000000e+00 -6.328422427177429199e-01 4.747981131076812744e-01 2.907021045684814453e-01 1.000000000000000000e+00 -6.433572769165039062e-01 4.744589328765869141e-01 2.994947731494903564e-01 1.000000000000000000e+00 -6.536651849746704102e-01 4.741680920124053955e-01 3.085928559303283691e-01 1.000000000000000000e+00 -6.637550592422485352e-01 4.739328920841217041e-01 3.179873228073120117e-01 1.000000000000000000e+00 -6.736162304878234863e-01 4.737605154514312744e-01 3.276684284210205078e-01 1.000000000000000000e+00 -6.832386851310729980e-01 4.736579954624176025e-01 3.376260101795196533e-01 1.000000000000000000e+00 -6.926126480102539062e-01 4.736322760581970215e-01 3.478492796421051025e-01 1.000000000000000000e+00 -7.017289996147155762e-01 4.736901223659515381e-01 3.583270013332366943e-01 1.000000000000000000e+00 -7.105790376663208008e-01 4.738381206989288330e-01 3.690474331378936768e-01 1.000000000000000000e+00 -7.191546559333801270e-01 4.740826785564422607e-01 3.799983859062194824e-01 1.000000000000000000e+00 -7.274482250213623047e-01 4.744300246238708496e-01 3.911672532558441162e-01 1.000000000000000000e+00 -7.354526519775390625e-01 4.748861789703369141e-01 4.025409519672393799e-01 1.000000000000000000e+00 -7.431614995002746582e-01 4.754569530487060547e-01 4.141060709953308105e-01 1.000000000000000000e+00 -7.505688667297363281e-01 4.761479198932647705e-01 4.258488714694976807e-01 1.000000000000000000e+00 -7.576693892478942871e-01 4.769644141197204590e-01 4.377551972866058350e-01 1.000000000000000000e+00 -7.644583582878112793e-01 4.779114723205566406e-01 4.498107135295867920e-01 1.000000000000000000e+00 -7.709317207336425781e-01 4.789939522743225098e-01 4.620007574558258057e-01 1.000000000000000000e+00 -7.770860195159912109e-01 4.802163839340209961e-01 4.743103682994842529e-01 1.000000000000000000e+00 -7.829183340072631836e-01 4.815830290317535400e-01 4.867245256900787354e-01 1.000000000000000000e+00 -7.884265184402465820e-01 4.830978810787200928e-01 4.992278814315795898e-01 1.000000000000000000e+00 -7.936089038848876953e-01 4.847646057605743408e-01 5.118050575256347656e-01 1.000000000000000000e+00 -7.984646558761596680e-01 4.865865409374237061e-01 5.244405269622802734e-01 1.000000000000000000e+00 -8.029934167861938477e-01 4.885667860507965088e-01 5.371186733245849609e-01 1.000000000000000000e+00 -8.071955442428588867e-01 4.907080829143524170e-01 5.498238801956176758e-01 1.000000000000000000e+00 -8.110720515251159668e-01 4.930128455162048340e-01 5.625403523445129395e-01 1.000000000000000000e+00 -8.146245479583740234e-01 4.954831600189208984e-01 5.752525925636291504e-01 1.000000000000000000e+00 -8.178552389144897461e-01 4.981207847595214844e-01 5.879449248313903809e-01 1.000000000000000000e+00 -8.207671046257019043e-01 5.009271502494812012e-01 6.006018519401550293e-01 1.000000000000000000e+00 -8.233636021614074707e-01 5.039033293724060059e-01 6.132079362869262695e-01 1.000000000000000000e+00 -8.256489038467407227e-01 5.070500969886779785e-01 6.257479786872863770e-01 1.000000000000000000e+00 -8.276276588439941406e-01 5.103678107261657715e-01 6.382068991661071777e-01 1.000000000000000000e+00 -8.293052315711975098e-01 5.138565897941589355e-01 6.505697965621948242e-01 1.000000000000000000e+00 -8.306875824928283691e-01 5.175161361694335938e-01 6.628221273422241211e-01 1.000000000000000000e+00 -8.317810893058776855e-01 5.213457942008972168e-01 6.749494075775146484e-01 1.000000000000000000e+00 -8.325928449630737305e-01 5.253446698188781738e-01 6.869376897811889648e-01 1.000000000000000000e+00 -8.331304192543029785e-01 5.295114517211914062e-01 6.987732052803039551e-01 1.000000000000000000e+00 -8.334019184112548828e-01 5.338444709777832031e-01 7.104426622390747070e-01 1.000000000000000000e+00 -8.334159255027770996e-01 5.383418202400207520e-01 7.219330072402954102e-01 1.000000000000000000e+00 -8.331815004348754883e-01 5.430011749267578125e-01 7.332316637039184570e-01 1.000000000000000000e+00 -8.327082395553588867e-01 5.478200316429138184e-01 7.443265318870544434e-01 1.000000000000000000e+00 -8.320061564445495605e-01 5.527953505516052246e-01 7.552060484886169434e-01 1.000000000000000000e+00 -8.310856819152832031e-01 5.579239726066589355e-01 7.658588886260986328e-01 1.000000000000000000e+00 -8.299576640129089355e-01 5.632023811340332031e-01 7.762744426727294922e-01 1.000000000000000000e+00 -8.286333084106445312e-01 5.686267614364624023e-01 7.864426374435424805e-01 1.000000000000000000e+00 -8.271241188049316406e-01 5.741930007934570312e-01 7.963538169860839844e-01 1.000000000000000000e+00 -8.254420757293701172e-01 5.798967480659484863e-01 8.059990406036376953e-01 1.000000000000000000e+00 -8.235993981361389160e-01 5.857333540916442871e-01 8.153699040412902832e-01 1.000000000000000000e+00 -8.216084837913513184e-01 5.916978716850280762e-01 8.244585394859313965e-01 1.000000000000000000e+00 -8.194820880889892578e-01 5.977852940559387207e-01 8.332578539848327637e-01 1.000000000000000000e+00 -8.172332644462585449e-01 6.039900779724121094e-01 8.417612314224243164e-01 1.000000000000000000e+00 -8.148750066757202148e-01 6.103067994117736816e-01 8.499628901481628418e-01 1.000000000000000000e+00 -8.124207854270935059e-01 6.167295575141906738e-01 8.578575849533081055e-01 1.000000000000000000e+00 -8.098839521408081055e-01 6.232523918151855469e-01 8.654407262802124023e-01 1.000000000000000000e+00 -8.072780370712280273e-01 6.298691034317016602e-01 8.727085590362548828e-01 1.000000000000000000e+00 -8.046168088912963867e-01 6.365733742713928223e-01 8.796578645706176758e-01 1.000000000000000000e+00 -8.019139170646667480e-01 6.433586478233337402e-01 8.862861394882202148e-01 1.000000000000000000e+00 -7.991830110549926758e-01 6.502183675765991211e-01 8.925917148590087891e-01 1.000000000000000000e+00 -7.964378595352172852e-01 6.571456789970397949e-01 8.985735177993774414e-01 1.000000000000000000e+00 -7.936920523643493652e-01 6.641337275505065918e-01 9.042311906814575195e-01 1.000000000000000000e+00 -7.909592390060424805e-01 6.711755394935607910e-01 9.095650911331176758e-01 1.000000000000000000e+00 -7.882528901100158691e-01 6.782640814781188965e-01 9.145763516426086426e-01 1.000000000000000000e+00 -7.855862975120544434e-01 6.853922009468078613e-01 9.192667007446289062e-01 1.000000000000000000e+00 -7.829727530479431152e-01 6.925527453422546387e-01 9.236386418342590332e-01 1.000000000000000000e+00 -7.804252505302429199e-01 6.997384428977966309e-01 9.276953339576721191e-01 1.000000000000000000e+00 -7.779565453529357910e-01 7.069422006607055664e-01 9.314405918121337891e-01 1.000000000000000000e+00 -7.755791544914245605e-01 7.141566872596740723e-01 9.348790645599365234e-01 1.000000000000000000e+00 -7.733053565025329590e-01 7.213746905326843262e-01 9.380158782005310059e-01 1.000000000000000000e+00 -7.711471915245056152e-01 7.285891175270080566e-01 9.408568739891052246e-01 1.000000000000000000e+00 -7.691162824630737305e-01 7.357927560806274414e-01 9.434086084365844727e-01 1.000000000000000000e+00 -7.672238945960998535e-01 7.429785728454589844e-01 9.456781744956970215e-01 1.000000000000000000e+00 -7.654808759689331055e-01 7.501395940780639648e-01 9.476732611656188965e-01 1.000000000000000000e+00 -7.638978362083435059e-01 7.572689056396484375e-01 9.494022727012634277e-01 1.000000000000000000e+00 -7.624848484992980957e-01 7.643596529960632324e-01 9.508740901947021484e-01 1.000000000000000000e+00 -7.612514495849609375e-01 7.714053392410278320e-01 9.520981311798095703e-01 1.000000000000000000e+00 -7.602068781852722168e-01 7.783992886543273926e-01 9.530844092369079590e-01 1.000000000000000000e+00 -7.593597769737243652e-01 7.853352427482604980e-01 9.538434147834777832e-01 1.000000000000000000e+00 -7.587183117866516113e-01 7.922069430351257324e-01 9.543861150741577148e-01 1.000000000000000000e+00 -7.582900524139404297e-01 7.990084290504455566e-01 9.547239542007446289e-01 1.000000000000000000e+00 -7.580821514129638672e-01 8.057338595390319824e-01 9.548687934875488281e-01 1.000000000000000000e+00 -7.581010460853576660e-01 8.123776316642761230e-01 9.548329114913940430e-01 1.000000000000000000e+00 -7.583526968955993652e-01 8.189343810081481934e-01 9.546289443969726562e-01 1.000000000000000000e+00 -7.588424682617187500e-01 8.253990411758422852e-01 9.542699456214904785e-01 1.000000000000000000e+00 -7.595750093460083008e-01 8.317666053771972656e-01 9.537692070007324219e-01 1.000000000000000000e+00 -7.605544924736022949e-01 8.380325436592102051e-01 9.531403183937072754e-01 1.000000000000000000e+00 -7.617843747138977051e-01 8.441924452781677246e-01 9.523972272872924805e-01 1.000000000000000000e+00 -7.632674574851989746e-01 8.502422571182250977e-01 9.515540003776550293e-01 1.000000000000000000e+00 -7.650059461593627930e-01 8.561782240867614746e-01 9.506248831748962402e-01 1.000000000000000000e+00 -7.670014500617980957e-01 8.619968295097351074e-01 9.496243596076965332e-01 1.000000000000000000e+00 -7.692547440528869629e-01 8.676949143409729004e-01 9.485669732093811035e-01 1.000000000000000000e+00 -7.717661857604980469e-01 8.732696771621704102e-01 9.474674463272094727e-01 1.000000000000000000e+00 -7.745352387428283691e-01 8.787184953689575195e-01 9.463404417037963867e-01 1.000000000000000000e+00 -7.775608301162719727e-01 8.840392231941223145e-01 9.452008008956909180e-01 1.000000000000000000e+00 -7.808412313461303711e-01 8.892300724983215332e-01 9.440631866455078125e-01 1.000000000000000000e+00 -7.843739986419677734e-01 8.942894339561462402e-01 9.429422616958618164e-01 1.000000000000000000e+00 -7.881561517715454102e-01 8.992161750793457031e-01 9.418526887893676758e-01 1.000000000000000000e+00 -7.921838164329528809e-01 9.040095210075378418e-01 9.408089518547058105e-01 1.000000000000000000e+00 -7.964528203010559082e-01 9.086689949035644531e-01 9.398253560066223145e-01 1.000000000000000000e+00 -8.009580373764038086e-01 9.131944179534912109e-01 9.389160871505737305e-01 1.000000000000000000e+00 -8.056939840316772461e-01 9.175861477851867676e-01 9.380950331687927246e-01 1.000000000000000000e+00 -8.106543421745300293e-01 9.218447208404541016e-01 9.373759031295776367e-01 1.000000000000000000e+00 -8.158323168754577637e-01 9.259710907936096191e-01 9.367721080780029297e-01 1.000000000000000000e+00 -8.212205767631530762e-01 9.299666881561279297e-01 9.362966418266296387e-01 1.000000000000000000e+00 -8.268111348152160645e-01 9.338331222534179688e-01 9.359622597694396973e-01 1.000000000000000000e+00 -8.325954675674438477e-01 9.375724196434020996e-01 9.357812404632568359e-01 1.000000000000000000e+00 -8.385645151138305664e-01 9.411869645118713379e-01 9.357655644416809082e-01 1.000000000000000000e+00 -8.447087407112121582e-01 9.446794390678405762e-01 9.359266161918640137e-01 1.000000000000000000e+00 -8.510181307792663574e-01 9.480530023574829102e-01 9.362754225730895996e-01 1.000000000000000000e+00 -8.574820756912231445e-01 9.513109922409057617e-01 9.368224740028381348e-01 1.000000000000000000e+00 -8.640896677970886230e-01 9.544571042060852051e-01 9.375776648521423340e-01 1.000000000000000000e+00 -8.708295822143554688e-01 9.574954509735107422e-01 9.385503530502319336e-01 1.000000000000000000e+00 -8.776899576187133789e-01 9.604302644729614258e-01 9.397493004798889160e-01 1.000000000000000000e+00 -8.846586346626281738e-01 9.632663726806640625e-01 9.411827921867370605e-01 1.000000000000000000e+00 -8.917231559753417969e-01 9.660085439682006836e-01 9.428583383560180664e-01 1.000000000000000000e+00 -8.988707065582275391e-01 9.686621427536010742e-01 9.447827339172363281e-01 1.000000000000000000e+00 -9.060882329940795898e-01 9.712325930595397949e-01 9.469622969627380371e-01 1.000000000000000000e+00 -9.133623242378234863e-01 9.737257361412048340e-01 9.494024515151977539e-01 1.000000000000000000e+00 -9.206793904304504395e-01 9.761474728584289551e-01 9.521080255508422852e-01 1.000000000000000000e+00 -9.280257821083068848e-01 9.785040616989135742e-01 9.550830721855163574e-01 1.000000000000000000e+00 -9.353874921798706055e-01 9.808020591735839844e-01 9.583308696746826172e-01 1.000000000000000000e+00 -9.427505135536193848e-01 9.830480217933654785e-01 9.618541002273559570e-01 1.000000000000000000e+00 -9.501006603240966797e-01 9.852488636970520020e-01 9.656543731689453125e-01 1.000000000000000000e+00 -9.574237465858459473e-01 9.874115586280822754e-01 9.697328209877014160e-01 1.000000000000000000e+00 -9.647055268287658691e-01 9.895434379577636719e-01 9.740896224975585938e-01 1.000000000000000000e+00 -9.719318151473999023e-01 9.916517138481140137e-01 9.787241816520690918e-01 1.000000000000000000e+00 -9.790884852409362793e-01 9.937438368797302246e-01 9.836350679397583008e-01 1.000000000000000000e+00 -9.861613512039184570e-01 9.958274960517883301e-01 9.888201355934143066e-01 1.000000000000000000e+00 -9.931364655494689941e-01 9.979103207588195801e-01 9.942764043807983398e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/flag b/fastplotlib/utils/colormaps/flag deleted file mode 100644 index 0b54453c0..000000000 --- a/fastplotlib/utils/colormaps/flag +++ /dev/null @@ -1,256 +0,0 @@ -1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.784110546112060547e-01 2.097892612218856812e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.005430459976196289e-01 4.930701255798339844e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.184870123863220215e-01 7.773815989494323730e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.998292326927185059e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.029407262802124023e-01 9.324722290039062500e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.207872986793518066e-01 7.264335751533508301e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.355422377586364746e-01 4.123563170433044434e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 3.695150092244148254e-02 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 8.155673146247863770e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 5.346375703811645508e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 2.485564053058624268e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.719138771295547485e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.515239298343658447e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.383435368537902832e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.897156357765197754e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.090170025825500488e-01 1.595071256160736084e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.459280848503112793e-01 4.377020001411437988e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.867737054824829102e-01 7.251621484756469727e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.957341551780700684e-01 9.791350960731506348e-01 1.000000000000000000e+00 -8.527833819389343262e-01 9.566044211387634277e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.760986208915710449e-01 7.752040028572082520e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.880961894989013672e-01 4.785115718841552734e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.160904347896575928e-02 1.106526851654052734e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 8.649533390998840332e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 5.898733139038085938e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 3.014268577098846436e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 4.251315072178840637e-02 1.000000000000000000e+00 -1.230012699961662292e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.963827192783355713e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.851746439933776855e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.464265108108520508e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.379352003335952759e-01 1.110846400260925293e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.877852439880371094e-01 3.826741576194763184e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.502171635627746582e-01 6.717129349708557129e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.862007498741149902e-01 9.352137446403503418e-01 1.000000000000000000e+00 -9.006991982460021973e-01 9.755119681358337402e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.309943795204162598e-01 8.197404742240905762e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.418074548244476318e-01 5.420533418655395508e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.614769041538238525e-02 1.837495118379592896e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 9.123461246490478516e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 6.446181535720825195e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 3.553818464279174805e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 8.765384554862976074e-02 1.000000000000000000e+00 -7.614769041538238525e-02 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.418074548244476318e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.309943795204162598e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.006991982460021973e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.655538827180862427e-01 6.478627771139144897e-02 1.000000000000000000e+00 -1.000000000000000000e+00 5.264321565628051758e-01 3.282870948314666748e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.090170025825500488e-01 6.173258423805236816e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.712810516357421875e-01 8.889153599739074707e-01 1.000000000000000000e+00 -9.464265108108520508e-01 9.890916347503662109e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.851746439933776855e-01 8.597998619079589844e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.963827192783355713e-01 6.026346087455749512e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.230012699961662292e-01 2.558427751064300537e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 9.574868679046630859e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 6.985731124877929688e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 4.101267158985137939e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 1.350466459989547729e-01 1.000000000000000000e+00 -3.160904347896575928e-02 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.880961894989013672e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.760986208915710449e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.527833819389343262e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.226836264133453369e-02 2.086489647626876831e-02 1.000000000000000000e+00 -1.000000000000000000e+00 4.622038900852203369e-01 2.748378515243530273e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.633982896804809570e-01 5.622979998588562012e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.510565400123596191e-01 8.404929041862487793e-01 1.000000000000000000e+00 -9.897156357765197754e-01 9.972691535949707031e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.383435368537902832e-01 8.951632976531982422e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.515239298343658447e-01 6.599245071411132812e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.719138771295547485e-01 3.265387117862701416e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 7.514436244964599609e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 4.653623998165130615e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 1.844326853752136230e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.355422377586364746e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.207872986793518066e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.029407262802124023e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.847890578210353851e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.954512178897857666e-01 2.226183861494064331e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.136101722717285156e-01 5.069298744201660156e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.256376624107360840e-01 7.902107238769531250e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -7.902107238769531250e-01 9.256376624107360840e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.069298744201660156e-01 7.136101722717285156e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.226183861494064331e-01 3.954512178897857666e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 1.847890578210353851e-02 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 8.029407262802124023e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 5.207872986793518066e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 2.355422377586364746e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.844326853752136230e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.653623998165130615e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.514436244964599609e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.265387117862701416e-01 1.719138771295547485e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.599245071411132812e-01 4.515239298343658447e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.951632976531982422e-01 7.383435368537902832e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.972691535949707031e-01 9.897156357765197754e-01 1.000000000000000000e+00 -8.404929041862487793e-01 9.510565400123596191e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.622979998588562012e-01 7.633982896804809570e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.748378515243530273e-01 4.622038900852203369e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.086489647626876831e-02 9.226836264133453369e-02 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 8.527833819389343262e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 5.760986208915710449e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 2.880961894989013672e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 3.160904347896575928e-02 1.000000000000000000e+00 -1.350466459989547729e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.101267158985137939e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.985731124877929688e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.574868679046630859e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.558427751064300537e-01 1.230012699961662292e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.026346087455749512e-01 3.963827192783355713e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.597998619079589844e-01 6.851746439933776855e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.890916347503662109e-01 9.464265108108520508e-01 1.000000000000000000e+00 -8.889153599739074707e-01 9.712810516357421875e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.173258423805236816e-01 8.090170025825500488e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.282870948314666748e-01 5.264321565628051758e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.478627771139144897e-02 1.655538827180862427e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 9.006991982460021973e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 6.309943795204162598e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 3.418074548244476318e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 7.614769041538238525e-02 1.000000000000000000e+00 -8.765384554862976074e-02 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.553818464279174805e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.446181535720825195e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.123461246490478516e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.837495118379592896e-01 7.614769041538238525e-02 1.000000000000000000e+00 -1.000000000000000000e+00 5.420533418655395508e-01 3.418074548244476318e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.197404742240905762e-01 6.309943795204162598e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.755119681358337402e-01 9.006991982460021973e-01 1.000000000000000000e+00 -9.352137446403503418e-01 9.862007498741149902e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.717129349708557129e-01 8.502171635627746582e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.826741576194763184e-01 5.877852439880371094e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.110846400260925293e-01 2.379352003335952759e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 9.464265108108520508e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 6.851746439933776855e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 3.963827192783355713e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 1.230012699961662292e-01 1.000000000000000000e+00 -4.251315072178840637e-02 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.014268577098846436e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.898733139038085938e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.649533390998840332e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.106526851654052734e-01 3.160904347896575928e-02 1.000000000000000000e+00 -1.000000000000000000e+00 4.785115718841552734e-01 2.880961894989013672e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.752040028572082520e-01 5.760986208915710449e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.566044211387634277e-01 8.527833819389343262e-01 1.000000000000000000e+00 -9.791350960731506348e-01 9.957341551780700684e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.251621484756469727e-01 8.867737054824829102e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.377020001411437988e-01 6.459280848503112793e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.595071256160736084e-01 3.090170025825500488e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 9.897156357765197754e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 7.383435368537902832e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 4.515239298343658447e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 1.719138771295547485e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.485564053058624268e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.346375703811645508e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.155673146247863770e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.695150092244148254e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.123563170433044434e-01 2.355422377586364746e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.264335751533508301e-01 5.207872986793518066e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.324722290039062500e-01 8.029407262802124023e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.998292326927185059e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.773815989494323730e-01 9.184870123863220215e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.930701255798339844e-01 7.005430459976196289e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.097892612218856812e-01 3.784110546112060547e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 7.902107238769531250e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 5.069298744201660156e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 2.226183861494064331e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.970592439174652100e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.792127013206481934e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.644577622413635254e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.439489305019378662e-01 1.844326853752136230e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.736956238746643066e-01 4.653623998165130615e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.032471776008605957e-01 7.514436244964599609e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.984636306762695312e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.280861377716064453e-01 9.451838135719299316e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.484760999679565430e-01 7.513318657875061035e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.616564333438873291e-01 4.457383453845977783e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.028437074273824692e-02 7.385252416133880615e-02 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 8.404929041862487793e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 5.622979998588562012e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 2.748378515243530273e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 2.086489647626876831e-02 1.000000000000000000e+00 -1.472166478633880615e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.239013493061065674e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.119038105010986328e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.683909416198730469e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.736629843711853027e-01 1.350466459989547729e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.172782182693481445e-01 4.101267158985137939e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.690889477729797363e-01 6.985731124877929688e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.916446805000305176e-01 9.574868679046630859e-01 1.000000000000000000e+00 -8.769987225532531738e-01 9.667183756828308105e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.036172509193420410e-01 7.980172038078308105e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.148253560066223145e-01 5.106312036514282227e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.357348546385765076e-02 1.473017036914825439e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 8.889153599739074707e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 6.173258423805236816e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 3.282870948314666748e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 6.478627771139144897e-02 1.000000000000000000e+00 -9.930082410573959351e-02 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.690056204795837402e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.581925153732299805e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.238523244857788086e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.018824070692062378e-01 8.765384554862976074e-02 1.000000000000000000e+00 -1.000000000000000000e+00 5.574894547462463379e-01 3.553818464279174805e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.301840424537658691e-01 6.446181535720825195e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.794097542762756348e-01 9.123461246490478516e-01 1.000000000000000000e+00 -9.238523244857788086e-01 9.829730987548828125e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.581925153732299805e-01 8.403440713882446289e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.690056204795837402e-01 5.727351307868957520e-01 1.000000000000000000e+00 1.000000000000000000e+00 -9.930082410573959351e-02 2.199463546276092529e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 9.352137446403503418e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 6.717129349708557129e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 3.826741576194763184e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 1.110846400260925293e-01 1.000000000000000000e+00 -5.357348546385765076e-02 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.148253560066223145e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.036172509193420410e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.769987225532531738e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.289992183446884155e-01 4.251315072178840637e-02 1.000000000000000000e+00 -1.000000000000000000e+00 4.946558475494384766e-01 3.014268577098846436e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.867449522018432617e-01 5.898733139038085938e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.618256688117980957e-01 8.649533390998840332e-01 1.000000000000000000e+00 -9.683909416198730469e-01 9.938591122627258301e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.119038105010986328e-01 8.780812621116638184e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.239013493061065674e-01 6.317110061645507812e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.472166478633880615e-01 2.913897335529327393e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 9.791350960731506348e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 7.251621484756469727e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 4.377020001411437988e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 1.595071256160736084e-01 1.000000000000000000e+00 -1.028437074273824692e-02 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.616564333438873291e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.484760999679565430e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.280861377716064453e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.541147664189338684e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.291206002235412598e-01 2.485564053058624268e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.390089035034179688e-01 5.346375703811645508e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.389883875846862793e-01 8.155673146247863770e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.993170499801635742e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.644577622413635254e-01 9.110226631164550781e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.792127013206481934e-01 6.872366666793823242e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.970592439174652100e-01 3.612416684627532959e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 7.773815989494323730e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 4.930701255798339844e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 2.097892612218856812e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/gist_earth b/fastplotlib/utils/colormaps/gist_earth deleted file mode 100644 index 86667b3f7..000000000 --- a/fastplotlib/utils/colormaps/gist_earth +++ /dev/null @@ -1,256 +0,0 @@ -0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.613453427329659462e-03 0.000000000000000000e+00 1.686920076608657837e-01 1.000000000000000000e+00 -5.226906854659318924e-03 0.000000000000000000e+00 2.216635644435882568e-01 1.000000000000000000e+00 -7.840359583497047424e-03 0.000000000000000000e+00 2.638055086135864258e-01 1.000000000000000000e+00 -1.045381370931863785e-02 0.000000000000000000e+00 3.059474229812622070e-01 1.000000000000000000e+00 -1.306726690381765366e-02 0.000000000000000000e+00 3.480893671512603760e-01 1.000000000000000000e+00 -1.568071916699409485e-02 0.000000000000000000e+00 3.902312815189361572e-01 1.000000000000000000e+00 -1.829417236149311066e-02 0.000000000000000000e+00 4.323732256889343262e-01 1.000000000000000000e+00 -2.090762741863727570e-02 8.907333016395568848e-03 4.547451436519622803e-01 1.000000000000000000e+00 -2.352108061313629150e-02 1.792741753160953522e-02 4.556058943271636963e-01 1.000000000000000000e+00 -2.613453380763530731e-02 2.694750204682350159e-02 4.563167989253997803e-01 1.000000000000000000e+00 -2.874798700213432312e-02 3.596758469939231873e-02 4.570276737213134766e-01 1.000000000000000000e+00 -3.136143833398818970e-02 4.498767107725143433e-02 4.577385485172271729e-01 1.000000000000000000e+00 -3.397489339113235474e-02 5.400775372982025146e-02 4.584494233131408691e-01 1.000000000000000000e+00 -3.658834472298622131e-02 6.302783638238906860e-02 4.591603279113769531e-01 1.000000000000000000e+00 -3.920179978013038635e-02 7.204792648553848267e-02 4.598712027072906494e-01 1.000000000000000000e+00 -4.181525483727455139e-02 8.106800913810729980e-02 4.605820775032043457e-01 1.000000000000000000e+00 -4.442870616912841797e-02 9.008809179067611694e-02 4.612929522991180420e-01 1.000000000000000000e+00 -4.704216122627258301e-02 9.910817444324493408e-02 4.620038568973541260e-01 1.000000000000000000e+00 -4.965561255812644958e-02 1.081282645463943481e-01 4.627147316932678223e-01 1.000000000000000000e+00 -5.226906761527061462e-02 1.171483471989631653e-01 4.634256064891815186e-01 1.000000000000000000e+00 -5.488251894712448120e-02 1.261684298515319824e-01 4.641364812850952148e-01 1.000000000000000000e+00 -5.749597400426864624e-02 1.351885199546813965e-01 4.648473858833312988e-01 1.000000000000000000e+00 -6.010942533612251282e-02 1.442085951566696167e-01 4.655582606792449951e-01 1.000000000000000000e+00 -6.272287666797637939e-02 1.532286852598190308e-01 4.662691354751586914e-01 1.000000000000000000e+00 -6.533633172512054443e-02 1.622487604618072510e-01 4.669800102710723877e-01 1.000000000000000000e+00 -6.794978678226470947e-02 1.712688505649566650e-01 4.676908850669860840e-01 1.000000000000000000e+00 -7.056324183940887451e-02 1.802889406681060791e-01 4.684017896652221680e-01 1.000000000000000000e+00 -7.317668944597244263e-02 1.893081516027450562e-01 4.691126644611358643e-01 1.000000000000000000e+00 -7.579014450311660767e-02 1.974655985832214355e-01 4.698235392570495605e-01 1.000000000000000000e+00 -7.840359956026077271e-02 2.056230306625366211e-01 4.705344140529632568e-01 1.000000000000000000e+00 -8.101705461740493774e-02 2.137804627418518066e-01 4.712453186511993408e-01 1.000000000000000000e+00 -8.363050967454910278e-02 2.219378948211669922e-01 4.719561934471130371e-01 1.000000000000000000e+00 -8.624395728111267090e-02 2.300953269004821777e-01 4.726670682430267334e-01 1.000000000000000000e+00 -8.885741233825683594e-02 2.382527589797973633e-01 4.733779430389404297e-01 1.000000000000000000e+00 -9.147086739540100098e-02 2.464101910591125488e-01 4.740888476371765137e-01 1.000000000000000000e+00 -9.408432245254516602e-02 2.545676231384277344e-01 4.747997224330902100e-01 1.000000000000000000e+00 -9.669777005910873413e-02 2.627250552177429199e-01 4.755105972290039062e-01 1.000000000000000000e+00 -9.931122511625289917e-02 2.708824872970581055e-01 4.762214720249176025e-01 1.000000000000000000e+00 -1.019246801733970642e-01 2.790399193763732910e-01 4.769323766231536865e-01 1.000000000000000000e+00 -1.045381352305412292e-01 2.871973812580108643e-01 4.776432514190673828e-01 1.000000000000000000e+00 -1.071515828371047974e-01 2.953548133373260498e-01 4.783541262149810791e-01 1.000000000000000000e+00 -1.097650378942489624e-01 3.035109937191009521e-01 4.790650010108947754e-01 1.000000000000000000e+00 -1.123784929513931274e-01 3.108446002006530762e-01 4.797759056091308594e-01 1.000000000000000000e+00 -1.149919480085372925e-01 3.181782066822052002e-01 4.804867804050445557e-01 1.000000000000000000e+00 -1.176053956151008606e-01 3.255118131637573242e-01 4.811976552009582520e-01 1.000000000000000000e+00 -1.202188506722450256e-01 3.328454196453094482e-01 4.819085299968719482e-01 1.000000000000000000e+00 -1.228323057293891907e-01 3.401790261268615723e-01 4.826194345951080322e-01 1.000000000000000000e+00 -1.254457533359527588e-01 3.475126326084136963e-01 4.833303093910217285e-01 1.000000000000000000e+00 -1.280592083930969238e-01 3.548462390899658203e-01 4.840411841869354248e-01 1.000000000000000000e+00 -1.306726634502410889e-01 3.621798455715179443e-01 4.847520589828491211e-01 1.000000000000000000e+00 -1.332861185073852539e-01 3.695134520530700684e-01 4.854629337787628174e-01 1.000000000000000000e+00 -1.358995735645294189e-01 3.768470585346221924e-01 4.861738383769989014e-01 1.000000000000000000e+00 -1.385130286216735840e-01 3.841681778430938721e-01 4.868847131729125977e-01 1.000000000000000000e+00 -1.411264836788177490e-01 3.903659284114837646e-01 4.875955879688262939e-01 1.000000000000000000e+00 -1.437399387359619141e-01 3.965637087821960449e-01 4.883064627647399902e-01 1.000000000000000000e+00 -1.463533788919448853e-01 4.027614593505859375e-01 4.890173673629760742e-01 1.000000000000000000e+00 -1.489668339490890503e-01 4.089592099189758301e-01 4.897282421588897705e-01 1.000000000000000000e+00 -1.515802890062332153e-01 4.151569902896881104e-01 4.904391169548034668e-01 1.000000000000000000e+00 -1.541937440633773804e-01 4.213547408580780029e-01 4.911499917507171631e-01 1.000000000000000000e+00 -1.568071991205215454e-01 4.275524914264678955e-01 4.918608963489532471e-01 1.000000000000000000e+00 -1.594206541776657104e-01 4.337502717971801758e-01 4.925717711448669434e-01 1.000000000000000000e+00 -1.620341092348098755e-01 4.399480223655700684e-01 4.932826459407806396e-01 1.000000000000000000e+00 -1.646475642919540405e-01 4.461457729339599609e-01 4.939935207366943359e-01 1.000000000000000000e+00 -1.672610193490982056e-01 4.523435533046722412e-01 4.947044253349304199e-01 1.000000000000000000e+00 -1.698744595050811768e-01 4.585413038730621338e-01 4.954153001308441162e-01 1.000000000000000000e+00 -1.724879145622253418e-01 4.647390544414520264e-01 4.961261749267578125e-01 1.000000000000000000e+00 -1.751013696193695068e-01 4.709368348121643066e-01 4.968370497226715088e-01 1.000000000000000000e+00 -1.777148246765136719e-01 4.771345853805541992e-01 4.975479543209075928e-01 1.000000000000000000e+00 -1.803282797336578369e-01 4.833323359489440918e-01 4.982588291168212891e-01 1.000000000000000000e+00 -1.829417347908020020e-01 4.895301163196563721e-01 4.989697039127349854e-01 1.000000000000000000e+00 -1.855551898479461670e-01 4.957278668880462646e-01 4.996805787086486816e-01 1.000000000000000000e+00 -1.881686449050903320e-01 5.019256472587585449e-01 5.003914833068847656e-01 1.000000000000000000e+00 -1.900274306535720825e-01 5.042304396629333496e-01 4.956572353839874268e-01 1.000000000000000000e+00 -1.918770670890808105e-01 5.064879655838012695e-01 4.908568859100341797e-01 1.000000000000000000e+00 -1.937266886234283447e-01 5.087454915046691895e-01 4.860565364360809326e-01 1.000000000000000000e+00 -1.955763250589370728e-01 5.110030174255371094e-01 4.812561869621276855e-01 1.000000000000000000e+00 -1.974259465932846069e-01 5.132605433464050293e-01 4.764558076858520508e-01 1.000000000000000000e+00 -1.992755830287933350e-01 5.155180692672729492e-01 4.716554582118988037e-01 1.000000000000000000e+00 -2.011252045631408691e-01 5.177755951881408691e-01 4.668551087379455566e-01 1.000000000000000000e+00 -2.029748409986495972e-01 5.200331211090087891e-01 4.620547592639923096e-01 1.000000000000000000e+00 -2.048244625329971313e-01 5.222906470298767090e-01 4.572543799877166748e-01 1.000000000000000000e+00 -2.066740989685058594e-01 5.245481729507446289e-01 4.524540305137634277e-01 1.000000000000000000e+00 -2.085237205028533936e-01 5.268056988716125488e-01 4.476536810398101807e-01 1.000000000000000000e+00 -2.103733420372009277e-01 5.290632247924804688e-01 4.428533017635345459e-01 1.000000000000000000e+00 -2.122229784727096558e-01 5.313207507133483887e-01 4.380529522895812988e-01 1.000000000000000000e+00 -2.140726000070571899e-01 5.335782766342163086e-01 4.332526028156280518e-01 1.000000000000000000e+00 -2.159222364425659180e-01 5.358358025550842285e-01 4.284522533416748047e-01 1.000000000000000000e+00 -2.177718579769134521e-01 5.380933284759521484e-01 4.236518740653991699e-01 1.000000000000000000e+00 -2.196214944124221802e-01 5.403508543968200684e-01 4.188515245914459229e-01 1.000000000000000000e+00 -2.214711159467697144e-01 5.426083803176879883e-01 4.140511751174926758e-01 1.000000000000000000e+00 -2.233207523822784424e-01 5.448659062385559082e-01 4.092508256435394287e-01 1.000000000000000000e+00 -2.251703739166259766e-01 5.471234321594238281e-01 4.044504463672637939e-01 1.000000000000000000e+00 -2.270200103521347046e-01 5.493809580802917480e-01 3.996500968933105469e-01 1.000000000000000000e+00 -2.288696318864822388e-01 5.516384840011596680e-01 3.948497474193572998e-01 1.000000000000000000e+00 -2.307192683219909668e-01 5.538960099220275879e-01 3.900493681430816650e-01 1.000000000000000000e+00 -2.325688898563385010e-01 5.561535358428955078e-01 3.852490186691284180e-01 1.000000000000000000e+00 -2.344185262918472290e-01 5.584110617637634277e-01 3.804486691951751709e-01 1.000000000000000000e+00 -2.362681478261947632e-01 5.606685876846313477e-01 3.756483197212219238e-01 1.000000000000000000e+00 -2.381177842617034912e-01 5.629261136054992676e-01 3.708479404449462891e-01 1.000000000000000000e+00 -2.399674057960510254e-01 5.651836395263671875e-01 3.660475909709930420e-01 1.000000000000000000e+00 -2.418170422315597534e-01 5.674411654472351074e-01 3.612472414970397949e-01 1.000000000000000000e+00 -2.436666637659072876e-01 5.696986913681030273e-01 3.564468920230865479e-01 1.000000000000000000e+00 -2.455163002014160156e-01 5.719561576843261719e-01 3.516465127468109131e-01 1.000000000000000000e+00 -2.473659217357635498e-01 5.742136836051940918e-01 3.468461632728576660e-01 1.000000000000000000e+00 -2.492155581712722778e-01 5.764712095260620117e-01 3.420458137989044189e-01 1.000000000000000000e+00 -2.510651946067810059e-01 5.787287354469299316e-01 3.372454643249511719e-01 1.000000000000000000e+00 -2.529148161411285400e-01 5.809862613677978516e-01 3.324450850486755371e-01 1.000000000000000000e+00 -2.547644376754760742e-01 5.832437872886657715e-01 3.276447355747222900e-01 1.000000000000000000e+00 -2.566140592098236084e-01 5.855013132095336914e-01 3.228443861007690430e-01 1.000000000000000000e+00 -2.584637105464935303e-01 5.877588391304016113e-01 3.180440068244934082e-01 1.000000000000000000e+00 -2.603133320808410645e-01 5.900163650512695312e-01 3.132436573505401611e-01 1.000000000000000000e+00 -2.621629536151885986e-01 5.922738909721374512e-01 3.084433078765869141e-01 1.000000000000000000e+00 -2.640125751495361328e-01 5.945314168930053711e-01 3.036429584026336670e-01 1.000000000000000000e+00 -2.658621966838836670e-01 5.967889428138732910e-01 2.988425791263580322e-01 1.000000000000000000e+00 -2.677118480205535889e-01 5.990464687347412109e-01 2.940422296524047852e-01 1.000000000000000000e+00 -2.695614695549011230e-01 6.013039946556091309e-01 2.892418801784515381e-01 1.000000000000000000e+00 -2.714523077011108398e-01 6.035615205764770508e-01 2.844415307044982910e-01 1.000000000000000000e+00 -2.801693081855773926e-01 6.058190464973449707e-01 2.796411514282226562e-01 1.000000000000000000e+00 -2.888863384723663330e-01 6.080765724182128906e-01 2.748408019542694092e-01 1.000000000000000000e+00 -2.976033389568328857e-01 6.103340983390808105e-01 2.770664691925048828e-01 1.000000000000000000e+00 -3.063203394412994385e-01 6.125916242599487305e-01 2.793523967266082764e-01 1.000000000000000000e+00 -3.150373697280883789e-01 6.148491501808166504e-01 2.816383242607116699e-01 1.000000000000000000e+00 -3.237543702125549316e-01 6.171066761016845703e-01 2.839242219924926758e-01 1.000000000000000000e+00 -3.324714004993438721e-01 6.193642020225524902e-01 2.862101495265960693e-01 1.000000000000000000e+00 -3.411884009838104248e-01 6.216217279434204102e-01 2.884960472583770752e-01 1.000000000000000000e+00 -3.499054014682769775e-01 6.238792538642883301e-01 2.907819747924804688e-01 1.000000000000000000e+00 -3.586224317550659180e-01 6.261367797851562500e-01 2.930678725242614746e-01 1.000000000000000000e+00 -3.673394322395324707e-01 6.283943057060241699e-01 2.953538000583648682e-01 1.000000000000000000e+00 -3.760564625263214111e-01 6.306518316268920898e-01 2.976397275924682617e-01 1.000000000000000000e+00 -3.847734630107879639e-01 6.329093575477600098e-01 2.999256253242492676e-01 1.000000000000000000e+00 -3.934904634952545166e-01 6.351668834686279297e-01 3.022115528583526611e-01 1.000000000000000000e+00 -4.022074937820434570e-01 6.374244093894958496e-01 3.044974505901336670e-01 1.000000000000000000e+00 -4.109244942665100098e-01 6.396819353103637695e-01 3.067833781242370605e-01 1.000000000000000000e+00 -4.196415245532989502e-01 6.414068937301635742e-01 3.090692758560180664e-01 1.000000000000000000e+00 -4.283585250377655029e-01 6.431276202201843262e-01 3.113552033901214600e-01 1.000000000000000000e+00 -4.370755255222320557e-01 6.448482871055603027e-01 3.136411011219024658e-01 1.000000000000000000e+00 -4.457925558090209961e-01 6.465690135955810547e-01 3.159270286560058594e-01 1.000000000000000000e+00 -4.545095562934875488e-01 6.482896804809570312e-01 3.182129561901092529e-01 1.000000000000000000e+00 -4.632265865802764893e-01 6.500103473663330078e-01 3.204988539218902588e-01 1.000000000000000000e+00 -4.719323217868804932e-01 6.517310738563537598e-01 3.217388093471527100e-01 1.000000000000000000e+00 -4.783989787101745605e-01 6.534517407417297363e-01 3.229782283306121826e-01 1.000000000000000000e+00 -4.848656058311462402e-01 6.551724076271057129e-01 3.242176473140716553e-01 1.000000000000000000e+00 -4.913322627544403076e-01 6.568931341171264648e-01 3.254570960998535156e-01 1.000000000000000000e+00 -4.977988898754119873e-01 6.586138010025024414e-01 3.266965150833129883e-01 1.000000000000000000e+00 -5.042655467987060547e-01 6.603344678878784180e-01 3.279359340667724609e-01 1.000000000000000000e+00 -5.107321739196777344e-01 6.620551943778991699e-01 3.291753530502319336e-01 1.000000000000000000e+00 -5.171988010406494141e-01 6.637758612632751465e-01 3.304147720336914062e-01 1.000000000000000000e+00 -5.236654281616210938e-01 6.654965877532958984e-01 3.316542208194732666e-01 1.000000000000000000e+00 -5.301321148872375488e-01 6.672172546386718750e-01 3.328936398029327393e-01 1.000000000000000000e+00 -5.365987420082092285e-01 6.689379215240478516e-01 3.341330587863922119e-01 1.000000000000000000e+00 -5.430653691291809082e-01 6.706586480140686035e-01 3.353724777698516846e-01 1.000000000000000000e+00 -5.495319962501525879e-01 6.723793148994445801e-01 3.366119265556335449e-01 1.000000000000000000e+00 -5.559986829757690430e-01 6.740999817848205566e-01 3.378513455390930176e-01 1.000000000000000000e+00 -5.624653100967407227e-01 6.758207082748413086e-01 3.390907645225524902e-01 1.000000000000000000e+00 -5.689319372177124023e-01 6.775413751602172852e-01 3.403301835060119629e-01 1.000000000000000000e+00 -5.753985643386840820e-01 6.792620420455932617e-01 3.415696024894714355e-01 1.000000000000000000e+00 -5.818651914596557617e-01 6.809827685356140137e-01 3.428090512752532959e-01 1.000000000000000000e+00 -5.883318781852722168e-01 6.827034354209899902e-01 3.440484702587127686e-01 1.000000000000000000e+00 -5.947985053062438965e-01 6.844241023063659668e-01 3.452878892421722412e-01 1.000000000000000000e+00 -6.012651324272155762e-01 6.861448287963867188e-01 3.465273082256317139e-01 1.000000000000000000e+00 -6.077317595481872559e-01 6.878654956817626953e-01 3.477667272090911865e-01 1.000000000000000000e+00 -6.141984462738037109e-01 6.895862221717834473e-01 3.490061759948730469e-01 1.000000000000000000e+00 -6.206650733947753906e-01 6.913068890571594238e-01 3.502455949783325195e-01 1.000000000000000000e+00 -6.271317005157470703e-01 6.930275559425354004e-01 3.514850139617919922e-01 1.000000000000000000e+00 -6.335983276367187500e-01 6.947482824325561523e-01 3.527244329452514648e-01 1.000000000000000000e+00 -6.400649547576904297e-01 6.964689493179321289e-01 3.539638817310333252e-01 1.000000000000000000e+00 -6.465316414833068848e-01 6.981896162033081055e-01 3.552033007144927979e-01 1.000000000000000000e+00 -6.529982686042785645e-01 6.999103426933288574e-01 3.564427196979522705e-01 1.000000000000000000e+00 -6.594648957252502441e-01 7.016310095787048340e-01 3.576821386814117432e-01 1.000000000000000000e+00 -6.659315228462219238e-01 7.033516764640808105e-01 3.589215576648712158e-01 1.000000000000000000e+00 -6.723982095718383789e-01 7.050724029541015625e-01 3.601610064506530762e-01 1.000000000000000000e+00 -6.788648366928100586e-01 7.067930698394775391e-01 3.614004254341125488e-01 1.000000000000000000e+00 -6.853314638137817383e-01 7.085137367248535156e-01 3.626398444175720215e-01 1.000000000000000000e+00 -6.917980909347534180e-01 7.102344632148742676e-01 3.638792634010314941e-01 1.000000000000000000e+00 -6.982647180557250977e-01 7.119551301002502441e-01 3.651187121868133545e-01 1.000000000000000000e+00 -7.047314047813415527e-01 7.136758565902709961e-01 3.663581311702728271e-01 1.000000000000000000e+00 -7.111980319023132324e-01 7.153965234756469727e-01 3.675975501537322998e-01 1.000000000000000000e+00 -7.176163792610168457e-01 7.170661091804504395e-01 3.688369691371917725e-01 1.000000000000000000e+00 -7.192554473876953125e-01 7.136793136596679688e-01 3.700763881206512451e-01 1.000000000000000000e+00 -7.208945155143737793e-01 7.102925181388854980e-01 3.713158369064331055e-01 1.000000000000000000e+00 -7.225335836410522461e-01 7.069057226181030273e-01 3.725552558898925781e-01 1.000000000000000000e+00 -7.241726517677307129e-01 7.035188674926757812e-01 3.737946748733520508e-01 1.000000000000000000e+00 -7.258116602897644043e-01 7.001320719718933105e-01 3.750340938568115234e-01 1.000000000000000000e+00 -7.274507284164428711e-01 6.967452764511108398e-01 3.762735426425933838e-01 1.000000000000000000e+00 -7.290897965431213379e-01 6.933584809303283691e-01 3.775129616260528564e-01 1.000000000000000000e+00 -7.307288646697998047e-01 6.899716854095458984e-01 3.787523806095123291e-01 1.000000000000000000e+00 -7.323679327964782715e-01 6.865848302841186523e-01 3.799917995929718018e-01 1.000000000000000000e+00 -7.340070009231567383e-01 6.831980347633361816e-01 3.812312185764312744e-01 1.000000000000000000e+00 -7.356460690498352051e-01 6.798112392425537109e-01 3.824706673622131348e-01 1.000000000000000000e+00 -7.372850775718688965e-01 6.764244437217712402e-01 3.837100863456726074e-01 1.000000000000000000e+00 -7.389241456985473633e-01 6.730375885963439941e-01 3.849495053291320801e-01 1.000000000000000000e+00 -7.405632138252258301e-01 6.696507930755615234e-01 3.861889243125915527e-01 1.000000000000000000e+00 -7.422022819519042969e-01 6.662639975547790527e-01 3.874283730983734131e-01 1.000000000000000000e+00 -7.438413500785827637e-01 6.628772020339965820e-01 3.886677920818328857e-01 1.000000000000000000e+00 -7.454804182052612305e-01 6.594903469085693359e-01 3.899072110652923584e-01 1.000000000000000000e+00 -7.471194267272949219e-01 6.561035513877868652e-01 3.911466300487518311e-01 1.000000000000000000e+00 -7.487584948539733887e-01 6.527167558670043945e-01 3.923860490322113037e-01 1.000000000000000000e+00 -7.503975629806518555e-01 6.493299603462219238e-01 3.936254978179931641e-01 1.000000000000000000e+00 -7.520366311073303223e-01 6.459431648254394531e-01 3.948649168014526367e-01 1.000000000000000000e+00 -7.536756992340087891e-01 6.425563097000122070e-01 3.961336314678192139e-01 1.000000000000000000e+00 -7.553395032882690430e-01 6.392185091972351074e-01 4.057411253452301025e-01 1.000000000000000000e+00 -7.597258090972900391e-01 6.412773728370666504e-01 4.153485894203186035e-01 1.000000000000000000e+00 -7.641121149063110352e-01 6.429905891418457031e-01 4.249560832977294922e-01 1.000000000000000000e+00 -7.684984207153320312e-01 6.446999907493591309e-01 4.345635771751403809e-01 1.000000000000000000e+00 -7.728847265243530273e-01 6.464093923568725586e-01 4.441710412502288818e-01 1.000000000000000000e+00 -7.772710323333740234e-01 6.481371521949768066e-01 4.537785351276397705e-01 1.000000000000000000e+00 -7.816573381423950195e-01 6.515126824378967285e-01 4.633860290050506592e-01 1.000000000000000000e+00 -7.860436439514160156e-01 6.548882126808166504e-01 4.729935228824615479e-01 1.000000000000000000e+00 -7.904299497604370117e-01 6.582868099212646484e-01 4.826009869575500488e-01 1.000000000000000000e+00 -7.948162555694580078e-01 6.616854667663574219e-01 4.922084808349609375e-01 1.000000000000000000e+00 -7.992025613784790039e-01 6.650841832160949707e-01 5.018159747123718262e-01 1.000000000000000000e+00 -8.035888671875000000e-01 6.684828996658325195e-01 5.114234685897827148e-01 1.000000000000000000e+00 -8.079751729965209961e-01 6.718815565109252930e-01 5.210309624671936035e-01 1.000000000000000000e+00 -8.123614788055419922e-01 6.752802729606628418e-01 5.306384563446044922e-01 1.000000000000000000e+00 -8.167477846145629883e-01 6.786789298057556152e-01 5.402458906173706055e-01 1.000000000000000000e+00 -8.211340904235839844e-01 6.820776462554931641e-01 5.498533844947814941e-01 1.000000000000000000e+00 -8.255203962326049805e-01 6.854763627052307129e-01 5.594608783721923828e-01 1.000000000000000000e+00 -8.299067020416259766e-01 6.888750195503234863e-01 5.690683722496032715e-01 1.000000000000000000e+00 -8.342930078506469727e-01 6.922737360000610352e-01 5.786758661270141602e-01 1.000000000000000000e+00 -8.386793136596679688e-01 6.956723928451538086e-01 5.882833600044250488e-01 1.000000000000000000e+00 -8.430656194686889648e-01 6.990711092948913574e-01 5.978908538818359375e-01 1.000000000000000000e+00 -8.474519252777099609e-01 7.046831250190734863e-01 6.074982881546020508e-01 1.000000000000000000e+00 -8.518382310867309570e-01 7.103140354156494141e-01 6.171057820320129395e-01 1.000000000000000000e+00 -8.562245368957519531e-01 7.159233689308166504e-01 6.267132759094238281e-01 1.000000000000000000e+00 -8.606108427047729492e-01 7.215327024459838867e-01 6.363207697868347168e-01 1.000000000000000000e+00 -8.649971485137939453e-01 7.269150614738464355e-01 6.459282636642456055e-01 1.000000000000000000e+00 -8.693834543228149414e-01 7.322946190834045410e-01 6.555357575416564941e-01 1.000000000000000000e+00 -8.737697601318359375e-01 7.376742362976074219e-01 6.651532053947448730e-01 1.000000000000000000e+00 -8.781560659408569336e-01 7.430766820907592773e-01 6.769734621047973633e-01 1.000000000000000000e+00 -8.825423717498779297e-01 7.507473826408386230e-01 6.887937188148498535e-01 1.000000000000000000e+00 -8.869286775588989258e-01 7.584180235862731934e-01 7.006139755249023438e-01 1.000000000000000000e+00 -8.913149833679199219e-01 7.660886645317077637e-01 7.124341726303100586e-01 1.000000000000000000e+00 -8.957012891769409180e-01 7.737593650817871094e-01 7.242544293403625488e-01 1.000000000000000000e+00 -9.000875949859619141e-01 7.814300060272216797e-01 7.360746860504150391e-01 1.000000000000000000e+00 -9.044739007949829102e-01 7.891006469726562500e-01 7.478949427604675293e-01 1.000000000000000000e+00 -9.088602066040039062e-01 7.967713475227355957e-01 7.597151994705200195e-01 1.000000000000000000e+00 -9.132465124130249023e-01 8.044419884681701660e-01 7.715354561805725098e-01 1.000000000000000000e+00 -9.176328182220458984e-01 8.121126294136047363e-01 7.833557128906250000e-01 1.000000000000000000e+00 -9.220191240310668945e-01 8.197833299636840820e-01 7.951759696006774902e-01 1.000000000000000000e+00 -9.264054298400878906e-01 8.274539709091186523e-01 8.069962263107299805e-01 1.000000000000000000e+00 -9.307917356491088867e-01 8.364381790161132812e-01 8.188164830207824707e-01 1.000000000000000000e+00 -9.351780414581298828e-01 8.454303145408630371e-01 8.306367397308349609e-01 1.000000000000000000e+00 -9.395643472671508789e-01 8.544224500656127930e-01 8.424569964408874512e-01 1.000000000000000000e+00 -9.439506530761718750e-01 8.634145855903625488e-01 8.542772531509399414e-01 1.000000000000000000e+00 -9.483369588851928711e-01 8.725135922431945801e-01 8.660974502563476562e-01 1.000000000000000000e+00 -9.527232646942138672e-01 8.816171884536743164e-01 8.779177069664001465e-01 1.000000000000000000e+00 -9.571095705032348633e-01 8.930696845054626465e-01 8.897379636764526367e-01 1.000000000000000000e+00 -9.614958763122558594e-01 9.045221209526062012e-01 9.015582203865051270e-01 1.000000000000000000e+00 -9.658821821212768555e-01 9.159746170043945312e-01 9.133784770965576172e-01 1.000000000000000000e+00 -9.702684879302978516e-01 9.274271130561828613e-01 9.251987338066101074e-01 1.000000000000000000e+00 -9.746547937393188477e-01 9.388795495033264160e-01 9.370189905166625977e-01 1.000000000000000000e+00 -9.790410995483398438e-01 9.503320455551147461e-01 9.488392472267150879e-01 1.000000000000000000e+00 -9.834274053573608398e-01 9.617845416069030762e-01 9.606595039367675781e-01 1.000000000000000000e+00 -9.878137111663818359e-01 9.732370376586914062e-01 9.724797606468200684e-01 1.000000000000000000e+00 -9.922000169754028320e-01 9.843000173568725586e-01 9.843000173568725586e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/gist_gray b/fastplotlib/utils/colormaps/gist_gray deleted file mode 100644 index 42b875285..000000000 --- a/fastplotlib/utils/colormaps/gist_gray +++ /dev/null @@ -1,256 +0,0 @@ -0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.921568859368562698e-03 3.921568859368562698e-03 3.921568859368562698e-03 1.000000000000000000e+00 -7.843137718737125397e-03 7.843137718737125397e-03 7.843137718737125397e-03 1.000000000000000000e+00 -1.176470611244440079e-02 1.176470611244440079e-02 1.176470611244440079e-02 1.000000000000000000e+00 -1.568627543747425079e-02 1.568627543747425079e-02 1.568627543747425079e-02 1.000000000000000000e+00 -1.960784383118152618e-02 1.960784383118152618e-02 1.960784383118152618e-02 1.000000000000000000e+00 -2.352941222488880157e-02 2.352941222488880157e-02 2.352941222488880157e-02 1.000000000000000000e+00 -2.745098061859607697e-02 2.745098061859607697e-02 2.745098061859607697e-02 1.000000000000000000e+00 -3.137255087494850159e-02 3.137255087494850159e-02 3.137255087494850159e-02 1.000000000000000000e+00 -3.529411926865577698e-02 3.529411926865577698e-02 3.529411926865577698e-02 1.000000000000000000e+00 -3.921568766236305237e-02 3.921568766236305237e-02 3.921568766236305237e-02 1.000000000000000000e+00 -4.313725605607032776e-02 4.313725605607032776e-02 4.313725605607032776e-02 1.000000000000000000e+00 -4.705882444977760315e-02 4.705882444977760315e-02 4.705882444977760315e-02 1.000000000000000000e+00 -5.098039284348487854e-02 5.098039284348487854e-02 5.098039284348487854e-02 1.000000000000000000e+00 -5.490196123719215393e-02 5.490196123719215393e-02 5.490196123719215393e-02 1.000000000000000000e+00 -5.882352963089942932e-02 5.882352963089942932e-02 5.882352963089942932e-02 1.000000000000000000e+00 -6.274510174989700317e-02 6.274510174989700317e-02 6.274510174989700317e-02 1.000000000000000000e+00 -6.666667014360427856e-02 6.666667014360427856e-02 6.666667014360427856e-02 1.000000000000000000e+00 -7.058823853731155396e-02 7.058823853731155396e-02 7.058823853731155396e-02 1.000000000000000000e+00 -7.450980693101882935e-02 7.450980693101882935e-02 7.450980693101882935e-02 1.000000000000000000e+00 -7.843137532472610474e-02 7.843137532472610474e-02 7.843137532472610474e-02 1.000000000000000000e+00 -8.235294371843338013e-02 8.235294371843338013e-02 8.235294371843338013e-02 1.000000000000000000e+00 -8.627451211214065552e-02 8.627451211214065552e-02 8.627451211214065552e-02 1.000000000000000000e+00 -9.019608050584793091e-02 9.019608050584793091e-02 9.019608050584793091e-02 1.000000000000000000e+00 -9.411764889955520630e-02 9.411764889955520630e-02 9.411764889955520630e-02 1.000000000000000000e+00 -9.803921729326248169e-02 9.803921729326248169e-02 9.803921729326248169e-02 1.000000000000000000e+00 -1.019607856869697571e-01 1.019607856869697571e-01 1.019607856869697571e-01 1.000000000000000000e+00 -1.058823540806770325e-01 1.058823540806770325e-01 1.058823540806770325e-01 1.000000000000000000e+00 -1.098039224743843079e-01 1.098039224743843079e-01 1.098039224743843079e-01 1.000000000000000000e+00 -1.137254908680915833e-01 1.137254908680915833e-01 1.137254908680915833e-01 1.000000000000000000e+00 -1.176470592617988586e-01 1.176470592617988586e-01 1.176470592617988586e-01 1.000000000000000000e+00 -1.215686276555061340e-01 1.215686276555061340e-01 1.215686276555061340e-01 1.000000000000000000e+00 -1.254902034997940063e-01 1.254902034997940063e-01 1.254902034997940063e-01 1.000000000000000000e+00 -1.294117718935012817e-01 1.294117718935012817e-01 1.294117718935012817e-01 1.000000000000000000e+00 -1.333333402872085571e-01 1.333333402872085571e-01 1.333333402872085571e-01 1.000000000000000000e+00 -1.372549086809158325e-01 1.372549086809158325e-01 1.372549086809158325e-01 1.000000000000000000e+00 -1.411764770746231079e-01 1.411764770746231079e-01 1.411764770746231079e-01 1.000000000000000000e+00 -1.450980454683303833e-01 1.450980454683303833e-01 1.450980454683303833e-01 1.000000000000000000e+00 -1.490196138620376587e-01 1.490196138620376587e-01 1.490196138620376587e-01 1.000000000000000000e+00 -1.529411822557449341e-01 1.529411822557449341e-01 1.529411822557449341e-01 1.000000000000000000e+00 -1.568627506494522095e-01 1.568627506494522095e-01 1.568627506494522095e-01 1.000000000000000000e+00 -1.607843190431594849e-01 1.607843190431594849e-01 1.607843190431594849e-01 1.000000000000000000e+00 -1.647058874368667603e-01 1.647058874368667603e-01 1.647058874368667603e-01 1.000000000000000000e+00 -1.686274558305740356e-01 1.686274558305740356e-01 1.686274558305740356e-01 1.000000000000000000e+00 -1.725490242242813110e-01 1.725490242242813110e-01 1.725490242242813110e-01 1.000000000000000000e+00 -1.764705926179885864e-01 1.764705926179885864e-01 1.764705926179885864e-01 1.000000000000000000e+00 -1.803921610116958618e-01 1.803921610116958618e-01 1.803921610116958618e-01 1.000000000000000000e+00 -1.843137294054031372e-01 1.843137294054031372e-01 1.843137294054031372e-01 1.000000000000000000e+00 -1.882352977991104126e-01 1.882352977991104126e-01 1.882352977991104126e-01 1.000000000000000000e+00 -1.921568661928176880e-01 1.921568661928176880e-01 1.921568661928176880e-01 1.000000000000000000e+00 -1.960784345865249634e-01 1.960784345865249634e-01 1.960784345865249634e-01 1.000000000000000000e+00 -2.000000029802322388e-01 2.000000029802322388e-01 2.000000029802322388e-01 1.000000000000000000e+00 -2.039215713739395142e-01 2.039215713739395142e-01 2.039215713739395142e-01 1.000000000000000000e+00 -2.078431397676467896e-01 2.078431397676467896e-01 2.078431397676467896e-01 1.000000000000000000e+00 -2.117647081613540649e-01 2.117647081613540649e-01 2.117647081613540649e-01 1.000000000000000000e+00 -2.156862765550613403e-01 2.156862765550613403e-01 2.156862765550613403e-01 1.000000000000000000e+00 -2.196078449487686157e-01 2.196078449487686157e-01 2.196078449487686157e-01 1.000000000000000000e+00 -2.235294133424758911e-01 2.235294133424758911e-01 2.235294133424758911e-01 1.000000000000000000e+00 -2.274509817361831665e-01 2.274509817361831665e-01 2.274509817361831665e-01 1.000000000000000000e+00 -2.313725501298904419e-01 2.313725501298904419e-01 2.313725501298904419e-01 1.000000000000000000e+00 -2.352941185235977173e-01 2.352941185235977173e-01 2.352941185235977173e-01 1.000000000000000000e+00 -2.392156869173049927e-01 2.392156869173049927e-01 2.392156869173049927e-01 1.000000000000000000e+00 -2.431372553110122681e-01 2.431372553110122681e-01 2.431372553110122681e-01 1.000000000000000000e+00 -2.470588237047195435e-01 2.470588237047195435e-01 2.470588237047195435e-01 1.000000000000000000e+00 -2.509804069995880127e-01 2.509804069995880127e-01 2.509804069995880127e-01 1.000000000000000000e+00 -2.549019753932952881e-01 2.549019753932952881e-01 2.549019753932952881e-01 1.000000000000000000e+00 -2.588235437870025635e-01 2.588235437870025635e-01 2.588235437870025635e-01 1.000000000000000000e+00 -2.627451121807098389e-01 2.627451121807098389e-01 2.627451121807098389e-01 1.000000000000000000e+00 -2.666666805744171143e-01 2.666666805744171143e-01 2.666666805744171143e-01 1.000000000000000000e+00 -2.705882489681243896e-01 2.705882489681243896e-01 2.705882489681243896e-01 1.000000000000000000e+00 -2.745098173618316650e-01 2.745098173618316650e-01 2.745098173618316650e-01 1.000000000000000000e+00 -2.784313857555389404e-01 2.784313857555389404e-01 2.784313857555389404e-01 1.000000000000000000e+00 -2.823529541492462158e-01 2.823529541492462158e-01 2.823529541492462158e-01 1.000000000000000000e+00 -2.862745225429534912e-01 2.862745225429534912e-01 2.862745225429534912e-01 1.000000000000000000e+00 -2.901960909366607666e-01 2.901960909366607666e-01 2.901960909366607666e-01 1.000000000000000000e+00 -2.941176593303680420e-01 2.941176593303680420e-01 2.941176593303680420e-01 1.000000000000000000e+00 -2.980392277240753174e-01 2.980392277240753174e-01 2.980392277240753174e-01 1.000000000000000000e+00 -3.019607961177825928e-01 3.019607961177825928e-01 3.019607961177825928e-01 1.000000000000000000e+00 -3.058823645114898682e-01 3.058823645114898682e-01 3.058823645114898682e-01 1.000000000000000000e+00 -3.098039329051971436e-01 3.098039329051971436e-01 3.098039329051971436e-01 1.000000000000000000e+00 -3.137255012989044189e-01 3.137255012989044189e-01 3.137255012989044189e-01 1.000000000000000000e+00 -3.176470696926116943e-01 3.176470696926116943e-01 3.176470696926116943e-01 1.000000000000000000e+00 -3.215686380863189697e-01 3.215686380863189697e-01 3.215686380863189697e-01 1.000000000000000000e+00 -3.254902064800262451e-01 3.254902064800262451e-01 3.254902064800262451e-01 1.000000000000000000e+00 -3.294117748737335205e-01 3.294117748737335205e-01 3.294117748737335205e-01 1.000000000000000000e+00 -3.333333432674407959e-01 3.333333432674407959e-01 3.333333432674407959e-01 1.000000000000000000e+00 -3.372549116611480713e-01 3.372549116611480713e-01 3.372549116611480713e-01 1.000000000000000000e+00 -3.411764800548553467e-01 3.411764800548553467e-01 3.411764800548553467e-01 1.000000000000000000e+00 -3.450980484485626221e-01 3.450980484485626221e-01 3.450980484485626221e-01 1.000000000000000000e+00 -3.490196168422698975e-01 3.490196168422698975e-01 3.490196168422698975e-01 1.000000000000000000e+00 -3.529411852359771729e-01 3.529411852359771729e-01 3.529411852359771729e-01 1.000000000000000000e+00 -3.568627536296844482e-01 3.568627536296844482e-01 3.568627536296844482e-01 1.000000000000000000e+00 -3.607843220233917236e-01 3.607843220233917236e-01 3.607843220233917236e-01 1.000000000000000000e+00 -3.647058904170989990e-01 3.647058904170989990e-01 3.647058904170989990e-01 1.000000000000000000e+00 -3.686274588108062744e-01 3.686274588108062744e-01 3.686274588108062744e-01 1.000000000000000000e+00 -3.725490272045135498e-01 3.725490272045135498e-01 3.725490272045135498e-01 1.000000000000000000e+00 -3.764705955982208252e-01 3.764705955982208252e-01 3.764705955982208252e-01 1.000000000000000000e+00 -3.803921639919281006e-01 3.803921639919281006e-01 3.803921639919281006e-01 1.000000000000000000e+00 -3.843137323856353760e-01 3.843137323856353760e-01 3.843137323856353760e-01 1.000000000000000000e+00 -3.882353007793426514e-01 3.882353007793426514e-01 3.882353007793426514e-01 1.000000000000000000e+00 -3.921568691730499268e-01 3.921568691730499268e-01 3.921568691730499268e-01 1.000000000000000000e+00 -3.960784375667572021e-01 3.960784375667572021e-01 3.960784375667572021e-01 1.000000000000000000e+00 -4.000000059604644775e-01 4.000000059604644775e-01 4.000000059604644775e-01 1.000000000000000000e+00 -4.039215743541717529e-01 4.039215743541717529e-01 4.039215743541717529e-01 1.000000000000000000e+00 -4.078431427478790283e-01 4.078431427478790283e-01 4.078431427478790283e-01 1.000000000000000000e+00 -4.117647111415863037e-01 4.117647111415863037e-01 4.117647111415863037e-01 1.000000000000000000e+00 -4.156862795352935791e-01 4.156862795352935791e-01 4.156862795352935791e-01 1.000000000000000000e+00 -4.196078479290008545e-01 4.196078479290008545e-01 4.196078479290008545e-01 1.000000000000000000e+00 -4.235294163227081299e-01 4.235294163227081299e-01 4.235294163227081299e-01 1.000000000000000000e+00 -4.274509847164154053e-01 4.274509847164154053e-01 4.274509847164154053e-01 1.000000000000000000e+00 -4.313725531101226807e-01 4.313725531101226807e-01 4.313725531101226807e-01 1.000000000000000000e+00 -4.352941215038299561e-01 4.352941215038299561e-01 4.352941215038299561e-01 1.000000000000000000e+00 -4.392156898975372314e-01 4.392156898975372314e-01 4.392156898975372314e-01 1.000000000000000000e+00 -4.431372582912445068e-01 4.431372582912445068e-01 4.431372582912445068e-01 1.000000000000000000e+00 -4.470588266849517822e-01 4.470588266849517822e-01 4.470588266849517822e-01 1.000000000000000000e+00 -4.509803950786590576e-01 4.509803950786590576e-01 4.509803950786590576e-01 1.000000000000000000e+00 -4.549019634723663330e-01 4.549019634723663330e-01 4.549019634723663330e-01 1.000000000000000000e+00 -4.588235318660736084e-01 4.588235318660736084e-01 4.588235318660736084e-01 1.000000000000000000e+00 -4.627451002597808838e-01 4.627451002597808838e-01 4.627451002597808838e-01 1.000000000000000000e+00 -4.666666686534881592e-01 4.666666686534881592e-01 4.666666686534881592e-01 1.000000000000000000e+00 -4.705882370471954346e-01 4.705882370471954346e-01 4.705882370471954346e-01 1.000000000000000000e+00 -4.745098054409027100e-01 4.745098054409027100e-01 4.745098054409027100e-01 1.000000000000000000e+00 -4.784313738346099854e-01 4.784313738346099854e-01 4.784313738346099854e-01 1.000000000000000000e+00 -4.823529422283172607e-01 4.823529422283172607e-01 4.823529422283172607e-01 1.000000000000000000e+00 -4.862745106220245361e-01 4.862745106220245361e-01 4.862745106220245361e-01 1.000000000000000000e+00 -4.901960790157318115e-01 4.901960790157318115e-01 4.901960790157318115e-01 1.000000000000000000e+00 -4.941176474094390869e-01 4.941176474094390869e-01 4.941176474094390869e-01 1.000000000000000000e+00 -4.980392158031463623e-01 4.980392158031463623e-01 4.980392158031463623e-01 1.000000000000000000e+00 -5.019608139991760254e-01 5.019608139991760254e-01 5.019608139991760254e-01 1.000000000000000000e+00 -5.058823823928833008e-01 5.058823823928833008e-01 5.058823823928833008e-01 1.000000000000000000e+00 -5.098039507865905762e-01 5.098039507865905762e-01 5.098039507865905762e-01 1.000000000000000000e+00 -5.137255191802978516e-01 5.137255191802978516e-01 5.137255191802978516e-01 1.000000000000000000e+00 -5.176470875740051270e-01 5.176470875740051270e-01 5.176470875740051270e-01 1.000000000000000000e+00 -5.215686559677124023e-01 5.215686559677124023e-01 5.215686559677124023e-01 1.000000000000000000e+00 -5.254902243614196777e-01 5.254902243614196777e-01 5.254902243614196777e-01 1.000000000000000000e+00 -5.294117927551269531e-01 5.294117927551269531e-01 5.294117927551269531e-01 1.000000000000000000e+00 -5.333333611488342285e-01 5.333333611488342285e-01 5.333333611488342285e-01 1.000000000000000000e+00 -5.372549295425415039e-01 5.372549295425415039e-01 5.372549295425415039e-01 1.000000000000000000e+00 -5.411764979362487793e-01 5.411764979362487793e-01 5.411764979362487793e-01 1.000000000000000000e+00 -5.450980663299560547e-01 5.450980663299560547e-01 5.450980663299560547e-01 1.000000000000000000e+00 -5.490196347236633301e-01 5.490196347236633301e-01 5.490196347236633301e-01 1.000000000000000000e+00 -5.529412031173706055e-01 5.529412031173706055e-01 5.529412031173706055e-01 1.000000000000000000e+00 -5.568627715110778809e-01 5.568627715110778809e-01 5.568627715110778809e-01 1.000000000000000000e+00 -5.607843399047851562e-01 5.607843399047851562e-01 5.607843399047851562e-01 1.000000000000000000e+00 -5.647059082984924316e-01 5.647059082984924316e-01 5.647059082984924316e-01 1.000000000000000000e+00 -5.686274766921997070e-01 5.686274766921997070e-01 5.686274766921997070e-01 1.000000000000000000e+00 -5.725490450859069824e-01 5.725490450859069824e-01 5.725490450859069824e-01 1.000000000000000000e+00 -5.764706134796142578e-01 5.764706134796142578e-01 5.764706134796142578e-01 1.000000000000000000e+00 -5.803921818733215332e-01 5.803921818733215332e-01 5.803921818733215332e-01 1.000000000000000000e+00 -5.843137502670288086e-01 5.843137502670288086e-01 5.843137502670288086e-01 1.000000000000000000e+00 -5.882353186607360840e-01 5.882353186607360840e-01 5.882353186607360840e-01 1.000000000000000000e+00 -5.921568870544433594e-01 5.921568870544433594e-01 5.921568870544433594e-01 1.000000000000000000e+00 -5.960784554481506348e-01 5.960784554481506348e-01 5.960784554481506348e-01 1.000000000000000000e+00 -6.000000238418579102e-01 6.000000238418579102e-01 6.000000238418579102e-01 1.000000000000000000e+00 -6.039215922355651855e-01 6.039215922355651855e-01 6.039215922355651855e-01 1.000000000000000000e+00 -6.078431606292724609e-01 6.078431606292724609e-01 6.078431606292724609e-01 1.000000000000000000e+00 -6.117647290229797363e-01 6.117647290229797363e-01 6.117647290229797363e-01 1.000000000000000000e+00 -6.156862974166870117e-01 6.156862974166870117e-01 6.156862974166870117e-01 1.000000000000000000e+00 -6.196078658103942871e-01 6.196078658103942871e-01 6.196078658103942871e-01 1.000000000000000000e+00 -6.235294342041015625e-01 6.235294342041015625e-01 6.235294342041015625e-01 1.000000000000000000e+00 -6.274510025978088379e-01 6.274510025978088379e-01 6.274510025978088379e-01 1.000000000000000000e+00 -6.313725709915161133e-01 6.313725709915161133e-01 6.313725709915161133e-01 1.000000000000000000e+00 -6.352941393852233887e-01 6.352941393852233887e-01 6.352941393852233887e-01 1.000000000000000000e+00 -6.392157077789306641e-01 6.392157077789306641e-01 6.392157077789306641e-01 1.000000000000000000e+00 -6.431372761726379395e-01 6.431372761726379395e-01 6.431372761726379395e-01 1.000000000000000000e+00 -6.470588445663452148e-01 6.470588445663452148e-01 6.470588445663452148e-01 1.000000000000000000e+00 -6.509804129600524902e-01 6.509804129600524902e-01 6.509804129600524902e-01 1.000000000000000000e+00 -6.549019813537597656e-01 6.549019813537597656e-01 6.549019813537597656e-01 1.000000000000000000e+00 -6.588235497474670410e-01 6.588235497474670410e-01 6.588235497474670410e-01 1.000000000000000000e+00 -6.627451181411743164e-01 6.627451181411743164e-01 6.627451181411743164e-01 1.000000000000000000e+00 -6.666666865348815918e-01 6.666666865348815918e-01 6.666666865348815918e-01 1.000000000000000000e+00 -6.705882549285888672e-01 6.705882549285888672e-01 6.705882549285888672e-01 1.000000000000000000e+00 -6.745098233222961426e-01 6.745098233222961426e-01 6.745098233222961426e-01 1.000000000000000000e+00 -6.784313917160034180e-01 6.784313917160034180e-01 6.784313917160034180e-01 1.000000000000000000e+00 -6.823529601097106934e-01 6.823529601097106934e-01 6.823529601097106934e-01 1.000000000000000000e+00 -6.862745285034179688e-01 6.862745285034179688e-01 6.862745285034179688e-01 1.000000000000000000e+00 -6.901960968971252441e-01 6.901960968971252441e-01 6.901960968971252441e-01 1.000000000000000000e+00 -6.941176652908325195e-01 6.941176652908325195e-01 6.941176652908325195e-01 1.000000000000000000e+00 -6.980392336845397949e-01 6.980392336845397949e-01 6.980392336845397949e-01 1.000000000000000000e+00 -7.019608020782470703e-01 7.019608020782470703e-01 7.019608020782470703e-01 1.000000000000000000e+00 -7.058823704719543457e-01 7.058823704719543457e-01 7.058823704719543457e-01 1.000000000000000000e+00 -7.098039388656616211e-01 7.098039388656616211e-01 7.098039388656616211e-01 1.000000000000000000e+00 -7.137255072593688965e-01 7.137255072593688965e-01 7.137255072593688965e-01 1.000000000000000000e+00 -7.176470756530761719e-01 7.176470756530761719e-01 7.176470756530761719e-01 1.000000000000000000e+00 -7.215686440467834473e-01 7.215686440467834473e-01 7.215686440467834473e-01 1.000000000000000000e+00 -7.254902124404907227e-01 7.254902124404907227e-01 7.254902124404907227e-01 1.000000000000000000e+00 -7.294117808341979980e-01 7.294117808341979980e-01 7.294117808341979980e-01 1.000000000000000000e+00 -7.333333492279052734e-01 7.333333492279052734e-01 7.333333492279052734e-01 1.000000000000000000e+00 -7.372549176216125488e-01 7.372549176216125488e-01 7.372549176216125488e-01 1.000000000000000000e+00 -7.411764860153198242e-01 7.411764860153198242e-01 7.411764860153198242e-01 1.000000000000000000e+00 -7.450980544090270996e-01 7.450980544090270996e-01 7.450980544090270996e-01 1.000000000000000000e+00 -7.490196228027343750e-01 7.490196228027343750e-01 7.490196228027343750e-01 1.000000000000000000e+00 -7.529411911964416504e-01 7.529411911964416504e-01 7.529411911964416504e-01 1.000000000000000000e+00 -7.568627595901489258e-01 7.568627595901489258e-01 7.568627595901489258e-01 1.000000000000000000e+00 -7.607843279838562012e-01 7.607843279838562012e-01 7.607843279838562012e-01 1.000000000000000000e+00 -7.647058963775634766e-01 7.647058963775634766e-01 7.647058963775634766e-01 1.000000000000000000e+00 -7.686274647712707520e-01 7.686274647712707520e-01 7.686274647712707520e-01 1.000000000000000000e+00 -7.725490331649780273e-01 7.725490331649780273e-01 7.725490331649780273e-01 1.000000000000000000e+00 -7.764706015586853027e-01 7.764706015586853027e-01 7.764706015586853027e-01 1.000000000000000000e+00 -7.803921699523925781e-01 7.803921699523925781e-01 7.803921699523925781e-01 1.000000000000000000e+00 -7.843137383460998535e-01 7.843137383460998535e-01 7.843137383460998535e-01 1.000000000000000000e+00 -7.882353067398071289e-01 7.882353067398071289e-01 7.882353067398071289e-01 1.000000000000000000e+00 -7.921568751335144043e-01 7.921568751335144043e-01 7.921568751335144043e-01 1.000000000000000000e+00 -7.960784435272216797e-01 7.960784435272216797e-01 7.960784435272216797e-01 1.000000000000000000e+00 -8.000000119209289551e-01 8.000000119209289551e-01 8.000000119209289551e-01 1.000000000000000000e+00 -8.039215803146362305e-01 8.039215803146362305e-01 8.039215803146362305e-01 1.000000000000000000e+00 -8.078431487083435059e-01 8.078431487083435059e-01 8.078431487083435059e-01 1.000000000000000000e+00 -8.117647171020507812e-01 8.117647171020507812e-01 8.117647171020507812e-01 1.000000000000000000e+00 -8.156862854957580566e-01 8.156862854957580566e-01 8.156862854957580566e-01 1.000000000000000000e+00 -8.196078538894653320e-01 8.196078538894653320e-01 8.196078538894653320e-01 1.000000000000000000e+00 -8.235294222831726074e-01 8.235294222831726074e-01 8.235294222831726074e-01 1.000000000000000000e+00 -8.274509906768798828e-01 8.274509906768798828e-01 8.274509906768798828e-01 1.000000000000000000e+00 -8.313725590705871582e-01 8.313725590705871582e-01 8.313725590705871582e-01 1.000000000000000000e+00 -8.352941274642944336e-01 8.352941274642944336e-01 8.352941274642944336e-01 1.000000000000000000e+00 -8.392156958580017090e-01 8.392156958580017090e-01 8.392156958580017090e-01 1.000000000000000000e+00 -8.431372642517089844e-01 8.431372642517089844e-01 8.431372642517089844e-01 1.000000000000000000e+00 -8.470588326454162598e-01 8.470588326454162598e-01 8.470588326454162598e-01 1.000000000000000000e+00 -8.509804010391235352e-01 8.509804010391235352e-01 8.509804010391235352e-01 1.000000000000000000e+00 -8.549019694328308105e-01 8.549019694328308105e-01 8.549019694328308105e-01 1.000000000000000000e+00 -8.588235378265380859e-01 8.588235378265380859e-01 8.588235378265380859e-01 1.000000000000000000e+00 -8.627451062202453613e-01 8.627451062202453613e-01 8.627451062202453613e-01 1.000000000000000000e+00 -8.666666746139526367e-01 8.666666746139526367e-01 8.666666746139526367e-01 1.000000000000000000e+00 -8.705882430076599121e-01 8.705882430076599121e-01 8.705882430076599121e-01 1.000000000000000000e+00 -8.745098114013671875e-01 8.745098114013671875e-01 8.745098114013671875e-01 1.000000000000000000e+00 -8.784313797950744629e-01 8.784313797950744629e-01 8.784313797950744629e-01 1.000000000000000000e+00 -8.823529481887817383e-01 8.823529481887817383e-01 8.823529481887817383e-01 1.000000000000000000e+00 -8.862745165824890137e-01 8.862745165824890137e-01 8.862745165824890137e-01 1.000000000000000000e+00 -8.901960849761962891e-01 8.901960849761962891e-01 8.901960849761962891e-01 1.000000000000000000e+00 -8.941176533699035645e-01 8.941176533699035645e-01 8.941176533699035645e-01 1.000000000000000000e+00 -8.980392217636108398e-01 8.980392217636108398e-01 8.980392217636108398e-01 1.000000000000000000e+00 -9.019607901573181152e-01 9.019607901573181152e-01 9.019607901573181152e-01 1.000000000000000000e+00 -9.058823585510253906e-01 9.058823585510253906e-01 9.058823585510253906e-01 1.000000000000000000e+00 -9.098039269447326660e-01 9.098039269447326660e-01 9.098039269447326660e-01 1.000000000000000000e+00 -9.137254953384399414e-01 9.137254953384399414e-01 9.137254953384399414e-01 1.000000000000000000e+00 -9.176470637321472168e-01 9.176470637321472168e-01 9.176470637321472168e-01 1.000000000000000000e+00 -9.215686321258544922e-01 9.215686321258544922e-01 9.215686321258544922e-01 1.000000000000000000e+00 -9.254902005195617676e-01 9.254902005195617676e-01 9.254902005195617676e-01 1.000000000000000000e+00 -9.294117689132690430e-01 9.294117689132690430e-01 9.294117689132690430e-01 1.000000000000000000e+00 -9.333333373069763184e-01 9.333333373069763184e-01 9.333333373069763184e-01 1.000000000000000000e+00 -9.372549057006835938e-01 9.372549057006835938e-01 9.372549057006835938e-01 1.000000000000000000e+00 -9.411764740943908691e-01 9.411764740943908691e-01 9.411764740943908691e-01 1.000000000000000000e+00 -9.450980424880981445e-01 9.450980424880981445e-01 9.450980424880981445e-01 1.000000000000000000e+00 -9.490196108818054199e-01 9.490196108818054199e-01 9.490196108818054199e-01 1.000000000000000000e+00 -9.529411792755126953e-01 9.529411792755126953e-01 9.529411792755126953e-01 1.000000000000000000e+00 -9.568627476692199707e-01 9.568627476692199707e-01 9.568627476692199707e-01 1.000000000000000000e+00 -9.607843160629272461e-01 9.607843160629272461e-01 9.607843160629272461e-01 1.000000000000000000e+00 -9.647058844566345215e-01 9.647058844566345215e-01 9.647058844566345215e-01 1.000000000000000000e+00 -9.686274528503417969e-01 9.686274528503417969e-01 9.686274528503417969e-01 1.000000000000000000e+00 -9.725490212440490723e-01 9.725490212440490723e-01 9.725490212440490723e-01 1.000000000000000000e+00 -9.764705896377563477e-01 9.764705896377563477e-01 9.764705896377563477e-01 1.000000000000000000e+00 -9.803921580314636230e-01 9.803921580314636230e-01 9.803921580314636230e-01 1.000000000000000000e+00 -9.843137264251708984e-01 9.843137264251708984e-01 9.843137264251708984e-01 1.000000000000000000e+00 -9.882352948188781738e-01 9.882352948188781738e-01 9.882352948188781738e-01 1.000000000000000000e+00 -9.921568632125854492e-01 9.921568632125854492e-01 9.921568632125854492e-01 1.000000000000000000e+00 -9.960784316062927246e-01 9.960784316062927246e-01 9.960784316062927246e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/gist_heat b/fastplotlib/utils/colormaps/gist_heat deleted file mode 100644 index 9e17b7574..000000000 --- a/fastplotlib/utils/colormaps/gist_heat +++ /dev/null @@ -1,256 +0,0 @@ -0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.882353056222200394e-03 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.176470611244440079e-02 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.764705963432788849e-02 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.352941222488880157e-02 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.941176481544971466e-02 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.529411926865577698e-02 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.117647185921669006e-02 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.705882444977760315e-02 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.294117704033851624e-02 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.882352963089942932e-02 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.470588594675064087e-02 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.058823853731155396e-02 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.647059112787246704e-02 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.235294371843338013e-02 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.823529630899429321e-02 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.411764889955520630e-02 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000014901161194e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.058823540806770325e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.117647066712379456e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.176470592617988586e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.235294118523597717e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.294117718935012817e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.352941244840621948e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.411764770746231079e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.470588296651840210e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.529411822557449341e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.588235348463058472e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.647058874368667603e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.705882400274276733e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.764705926179885864e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.823529452085494995e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.882352977991104126e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.941176503896713257e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.000000029802322388e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.058823555707931519e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.117647081613540649e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.176470607519149780e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.235294133424758911e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.294117659330368042e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.352941185235977173e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.411764711141586304e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.470588237047195435e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.529411911964416504e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.588235437870025635e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.647058963775634766e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.705882489681243896e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.764706015586853027e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.823529541492462158e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.882353067398071289e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.941176593303680420e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.000000119209289551e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.058823645114898682e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.117647171020507812e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.176470696926116943e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.235294222831726074e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.294117748737335205e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.352941274642944336e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.411764800548553467e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.470588326454162598e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.529411852359771729e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.588235378265380859e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.647058904170989990e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.705882430076599121e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.764705955982208252e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.823529481887817383e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.882353007793426514e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.941176533699035645e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.000000059604644775e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.058823585510253906e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.117647111415863037e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.176470637321472168e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.235294163227081299e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.294117689132690430e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.352941215038299561e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.411764740943908691e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.470588266849517822e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.529411792755126953e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.588235318660736084e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.647058844566345215e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.705882370471954346e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.764705896377563477e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.823529422283172607e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.882352948188781738e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.941176474094390869e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.000000000000000000e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.058823823928833008e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.117647051811218262e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.176470875740051270e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.235294103622436523e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.294117927551269531e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.352941155433654785e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.411764979362487793e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.470588207244873047e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.529412031173706055e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.588235259056091309e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.647059082984924316e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.705882310867309570e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.764706134796142578e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.823529362678527832e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.882353186607360840e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.941176414489746094e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.000000238418579102e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.058823466300964355e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.117647290229797363e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.176470518112182617e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.235294342041015625e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.294117569923400879e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.352941393852233887e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.411764621734619141e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.470588445663452148e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.529411673545837402e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.588235497474670410e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.647058725357055664e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.705882549285888672e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.764705777168273926e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.823529601097106934e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.882352828979492188e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.941176652908325195e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.999999880790710449e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.058823704719543457e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.117646932601928711e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.176470756530761719e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.235293984413146973e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.294117808341979980e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.352941036224365234e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.411764860153198242e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.470588088035583496e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.529411911964416504e-01 3.921568859368562698e-03 0.000000000000000000e+00 1.000000000000000000e+00 -7.588235139846801758e-01 1.176470611244440079e-02 0.000000000000000000e+00 1.000000000000000000e+00 -7.647058963775634766e-01 1.960784383118152618e-02 0.000000000000000000e+00 1.000000000000000000e+00 -7.705882191658020020e-01 2.745098061859607697e-02 0.000000000000000000e+00 1.000000000000000000e+00 -7.764706015586853027e-01 3.529411926865577698e-02 0.000000000000000000e+00 1.000000000000000000e+00 -7.823529243469238281e-01 4.313725605607032776e-02 0.000000000000000000e+00 1.000000000000000000e+00 -7.882353067398071289e-01 5.098039284348487854e-02 0.000000000000000000e+00 1.000000000000000000e+00 -7.941176295280456543e-01 5.882352963089942932e-02 0.000000000000000000e+00 1.000000000000000000e+00 -8.000000119209289551e-01 6.666667014360427856e-02 0.000000000000000000e+00 1.000000000000000000e+00 -8.058823347091674805e-01 7.450980693101882935e-02 0.000000000000000000e+00 1.000000000000000000e+00 -8.117647171020507812e-01 8.235294371843338013e-02 0.000000000000000000e+00 1.000000000000000000e+00 -8.176470398902893066e-01 9.019608050584793091e-02 0.000000000000000000e+00 1.000000000000000000e+00 -8.235294222831726074e-01 9.803921729326248169e-02 0.000000000000000000e+00 1.000000000000000000e+00 -8.294117450714111328e-01 1.058823540806770325e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.352941274642944336e-01 1.137254908680915833e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.411764502525329590e-01 1.215686276555061340e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.470588326454162598e-01 1.294117718935012817e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.529411554336547852e-01 1.372549086809158325e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.588235378265380859e-01 1.450980454683303833e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.647058606147766113e-01 1.529411822557449341e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.705882430076599121e-01 1.607843190431594849e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.764705657958984375e-01 1.686274558305740356e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.823529481887817383e-01 1.764705926179885864e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.882352709770202637e-01 1.843137294054031372e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.941176533699035645e-01 1.921568661928176880e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.999999761581420898e-01 2.000000029802322388e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.058823585510253906e-01 2.078431397676467896e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.117646813392639160e-01 2.156862765550613403e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.176470637321472168e-01 2.235294133424758911e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.235293865203857422e-01 2.313725501298904419e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.294117689132690430e-01 2.392156869173049927e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.352940917015075684e-01 2.470588237047195435e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.411764740943908691e-01 2.549019753932952881e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.470587968826293945e-01 2.627451121807098389e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.529411792755126953e-01 2.705882489681243896e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.588235020637512207e-01 2.784313857555389404e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.647058844566345215e-01 2.862745225429534912e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.705882072448730469e-01 2.941176593303680420e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.764705896377563477e-01 3.019607961177825928e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.823529124259948730e-01 3.098039329051971436e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.882352948188781738e-01 3.176470696926116943e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.941176176071166992e-01 3.254902064800262451e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.333333432674407959e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.411764800548553467e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.490196168422698975e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.568627536296844482e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.647058904170989990e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.725490272045135498e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.803921639919281006e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.882353007793426514e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.960784375667572021e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.039215743541717529e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.117647111415863037e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.196078479290008545e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.274509847164154053e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.352941215038299561e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.431372582912445068e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.509803950786590576e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.588235318660736084e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.666666686534881592e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.745098054409027100e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.823529422283172607e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.901960790157318115e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.980392158031463623e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.058823823928833008e-01 1.176470611244440079e-02 1.000000000000000000e+00 -1.000000000000000000e+00 5.137255191802978516e-01 2.745098061859607697e-02 1.000000000000000000e+00 -1.000000000000000000e+00 5.215686559677124023e-01 4.313725605607032776e-02 1.000000000000000000e+00 -1.000000000000000000e+00 5.294117927551269531e-01 5.882352963089942932e-02 1.000000000000000000e+00 -1.000000000000000000e+00 5.372549295425415039e-01 7.450980693101882935e-02 1.000000000000000000e+00 -1.000000000000000000e+00 5.450980663299560547e-01 9.019608050584793091e-02 1.000000000000000000e+00 -1.000000000000000000e+00 5.529412031173706055e-01 1.058823540806770325e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.607843399047851562e-01 1.215686276555061340e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.686274766921997070e-01 1.372549086809158325e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.764706134796142578e-01 1.529411822557449341e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.843137502670288086e-01 1.686274558305740356e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.921568870544433594e-01 1.843137294054031372e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.000000238418579102e-01 2.000000029802322388e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.078431606292724609e-01 2.156862765550613403e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.156862974166870117e-01 2.313725501298904419e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.235294342041015625e-01 2.470588237047195435e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.313725709915161133e-01 2.627451121807098389e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.392157077789306641e-01 2.784313857555389404e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.470588445663452148e-01 2.941176593303680420e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.549019813537597656e-01 3.098039329051971436e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.627451181411743164e-01 3.254902064800262451e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.705882549285888672e-01 3.411764800548553467e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.784313917160034180e-01 3.568627536296844482e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.862745285034179688e-01 3.725490272045135498e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.941176652908325195e-01 3.882353007793426514e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.019608020782470703e-01 4.039215743541717529e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.098039388656616211e-01 4.196078479290008545e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.176470756530761719e-01 4.352941215038299561e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.254902124404907227e-01 4.509803950786590576e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.333333492279052734e-01 4.666666686534881592e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.411764860153198242e-01 4.823529422283172607e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.490196228027343750e-01 4.980392158031463623e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.568627595901489258e-01 5.137255191802978516e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.647058963775634766e-01 5.294117927551269531e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.725490331649780273e-01 5.450980663299560547e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.803921699523925781e-01 5.607843399047851562e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.882353067398071289e-01 5.764706134796142578e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.960784435272216797e-01 5.921568870544433594e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.039215803146362305e-01 6.078431606292724609e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.117647171020507812e-01 6.235294342041015625e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.196078538894653320e-01 6.392157077789306641e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.274509906768798828e-01 6.549019813537597656e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.352941274642944336e-01 6.705882549285888672e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.431372642517089844e-01 6.862745285034179688e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.509804010391235352e-01 7.019608020782470703e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.588235378265380859e-01 7.176470756530761719e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.666666746139526367e-01 7.333333492279052734e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.745098114013671875e-01 7.490196228027343750e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.823529481887817383e-01 7.647058963775634766e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.901960849761962891e-01 7.803921699523925781e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.980392217636108398e-01 7.960784435272216797e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.058823585510253906e-01 8.117647171020507812e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.137254953384399414e-01 8.274509906768798828e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.215686321258544922e-01 8.431372642517089844e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.294117689132690430e-01 8.588235378265380859e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.372549057006835938e-01 8.745098114013671875e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.450980424880981445e-01 8.901960849761962891e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.529411792755126953e-01 9.058823585510253906e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.607843160629272461e-01 9.215686321258544922e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.686274528503417969e-01 9.372549057006835938e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.764705896377563477e-01 9.529411792755126953e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.843137264251708984e-01 9.686274528503417969e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.921568632125854492e-01 9.843137264251708984e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/gist_ncar b/fastplotlib/utils/colormaps/gist_ncar deleted file mode 100644 index 333046723..000000000 --- a/fastplotlib/utils/colormaps/gist_ncar +++ /dev/null @@ -1,256 +0,0 @@ -0.000000000000000000e+00 0.000000000000000000e+00 5.019999742507934570e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.861976251006126404e-02 4.651064872741699219e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.723952502012252808e-02 4.282130002975463867e-01 1.000000000000000000e+00 -0.000000000000000000e+00 8.585928380489349365e-02 3.913194835186004639e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.144790500402450562e-01 3.544259965419769287e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.430988013744354248e-01 3.175324797630310059e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.717185676097869873e-01 2.806389927864074707e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.003383338451385498e-01 2.437454760074615479e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.289581000804901123e-01 2.068519741296768188e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.575778663158416748e-01 1.699584722518920898e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.861976027488708496e-01 1.330649703741073608e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.148173689842224121e-01 9.617147594690322876e-02 1.000000000000000000e+00 -0.000000000000000000e+00 3.434371352195739746e-01 5.927797034382820129e-02 1.000000000000000000e+00 -0.000000000000000000e+00 3.720569014549255371e-01 2.238446660339832306e-02 1.000000000000000000e+00 -0.000000000000000000e+00 3.457462787628173828e-01 8.708668500185012817e-02 1.000000000000000000e+00 -0.000000000000000000e+00 3.191595971584320068e-01 1.522994339466094971e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.925729453563690186e-01 2.175121903419494629e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.659862637519836426e-01 2.827249467372894287e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.393996119499206543e-01 3.479377031326293945e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.128129452466964722e-01 4.131504595279693604e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.862262934446334839e-01 4.783631861209869385e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.596396267414093018e-01 5.435759425163269043e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.330529600381851196e-01 6.087887287139892578e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.064662933349609375e-01 6.740014553070068359e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.987963408231735229e-02 7.392141819000244141e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.329296737909317017e-02 8.044269680976867676e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.670630440115928650e-02 8.696396946907043457e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.196399898617528379e-04 9.348524808883666992e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.512949451804161072e-02 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 1.105081960558891296e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 1.658868938684463501e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 2.212655991315841675e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 2.766442894935607910e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 3.320229947566986084e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 3.874017000198364258e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 4.427804052829742432e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 4.981591105461120605e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 5.535377860069274902e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 6.089165210723876953e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 6.642951965332031250e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 7.196739315986633301e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 7.535652518272399902e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 7.752259373664855957e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 7.925115823745727539e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 8.097972273826599121e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 8.270829319953918457e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 8.443685770034790039e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 8.616542220115661621e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 8.789398670196533203e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 8.962255716323852539e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 9.135112166404724121e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 9.307968616485595703e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 9.480825066566467285e-01 9.998586177825927734e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.653682112693786621e-01 9.741483330726623535e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.826538562774658203e-01 9.484380483627319336e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.999395012855529785e-01 9.227277636528015137e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.982228875160217285e-01 8.970174789428710938e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.964395165443420410e-01 8.713071942329406738e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.946561455726623535e-01 8.455969095230102539e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.928728342056274414e-01 8.198866248130798340e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.910894632339477539e-01 7.941763401031494141e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.893060922622680664e-01 7.684660553932189941e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.875227212905883789e-01 7.427557706832885742e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.857393503189086914e-01 7.170454859733581543e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.839560389518737793e-01 6.913352012634277344e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.821726679801940918e-01 6.656249165534973145e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.804000258445739746e-01 6.399146318435668945e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.804000258445739746e-01 6.140294671058654785e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.804000258445739746e-01 5.731160044670104980e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.804000258445739746e-01 5.322025418281555176e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.820304512977600098e-01 4.912890493869781494e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.836658239364624023e-01 4.503755867481231689e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.853012561798095703e-01 4.094620943069458008e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.869366288185119629e-01 3.685486316680908203e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.885720014572143555e-01 3.276351392269134521e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.902073740959167480e-01 2.867216765880584717e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.918427467346191406e-01 2.458081841468811035e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.934781193733215332e-01 2.048947066068649292e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.951134920120239258e-01 1.639812290668487549e-01 1.000000000000000000e+00 -2.497420064173638821e-05 9.967488646507263184e-01 1.230677440762519836e-01 1.000000000000000000e+00 -2.499917522072792053e-02 9.983842372894287109e-01 8.215426653623580933e-02 1.000000000000000000e+00 -4.997337609529495239e-02 9.998229146003723145e-01 4.124078527092933655e-02 1.000000000000000000e+00 -7.494757324457168579e-02 9.850670695304870605e-01 3.273078473284840584e-04 1.000000000000000000e+00 -9.992177784442901611e-02 9.703112244606018066e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.248959749937057495e-01 9.555553197860717773e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.498701721429824829e-01 9.407994747161865234e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.748443692922592163e-01 9.260436296463012695e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.998185813426971436e-01 9.112877249717712402e-01 0.000000000000000000e+00 1.000000000000000000e+00 -2.247927784919738770e-01 8.965318799018859863e-01 0.000000000000000000e+00 1.000000000000000000e+00 -2.497669756412506104e-01 8.817760348320007324e-01 0.000000000000000000e+00 1.000000000000000000e+00 -2.747411727905273438e-01 8.670201897621154785e-01 0.000000000000000000e+00 1.000000000000000000e+00 -2.997153699398040771e-01 8.522642850875854492e-01 0.000000000000000000e+00 1.000000000000000000e+00 -3.246895670890808105e-01 8.375084400177001953e-01 0.000000000000000000e+00 1.000000000000000000e+00 -3.496637642383575439e-01 8.227525949478149414e-01 0.000000000000000000e+00 1.000000000000000000e+00 -3.746379911899566650e-01 8.081894516944885254e-01 0.000000000000000000e+00 1.000000000000000000e+00 -3.993970751762390137e-01 8.209661841392517090e-01 0.000000000000000000e+00 1.000000000000000000e+00 -4.071633219718933105e-01 8.337428569793701172e-01 0.000000000000000000e+00 1.000000000000000000e+00 -4.149295687675476074e-01 8.465195894241333008e-01 0.000000000000000000e+00 1.000000000000000000e+00 -4.226958155632019043e-01 8.592963218688964844e-01 0.000000000000000000e+00 1.000000000000000000e+00 -4.304620623588562012e-01 8.720730543136596680e-01 0.000000000000000000e+00 1.000000000000000000e+00 -4.382283091545104980e-01 8.848497867584228516e-01 0.000000000000000000e+00 1.000000000000000000e+00 -4.459945261478424072e-01 8.976265192031860352e-01 0.000000000000000000e+00 1.000000000000000000e+00 -4.537607729434967041e-01 9.104032516479492188e-01 0.000000000000000000e+00 1.000000000000000000e+00 -4.615270197391510010e-01 9.231799244880676270e-01 0.000000000000000000e+00 1.000000000000000000e+00 -4.692932665348052979e-01 9.359566569328308105e-01 0.000000000000000000e+00 1.000000000000000000e+00 -4.770595133304595947e-01 9.487333893775939941e-01 0.000000000000000000e+00 1.000000000000000000e+00 -4.848257601261138916e-01 9.615101218223571777e-01 0.000000000000000000e+00 1.000000000000000000e+00 -4.925920069217681885e-01 9.742868542671203613e-01 1.556491293013095856e-02 1.000000000000000000e+00 -5.004338622093200684e-01 9.870635867118835449e-01 3.118449449539184570e-02 1.000000000000000000e+00 -5.182809233665466309e-01 9.998403191566467285e-01 4.680407419800758362e-02 1.000000000000000000e+00 -5.361279845237731934e-01 1.000000000000000000e+00 6.242365762591362000e-02 1.000000000000000000e+00 -5.539750456809997559e-01 1.000000000000000000e+00 7.804323732852935791e-02 1.000000000000000000e+00 -5.718221068382263184e-01 1.000000000000000000e+00 9.366282075643539429e-02 1.000000000000000000e+00 -5.896691679954528809e-01 1.000000000000000000e+00 1.092823967337608337e-01 1.000000000000000000e+00 -6.075162291526794434e-01 1.000000000000000000e+00 1.249019801616668701e-01 1.000000000000000000e+00 -6.253632903099060059e-01 1.000000000000000000e+00 1.405215561389923096e-01 1.000000000000000000e+00 -6.432103514671325684e-01 1.000000000000000000e+00 1.561411470174789429e-01 1.000000000000000000e+00 -6.610574722290039062e-01 1.000000000000000000e+00 1.717607229948043823e-01 1.000000000000000000e+00 -6.789045333862304688e-01 1.000000000000000000e+00 1.873802989721298218e-01 1.000000000000000000e+00 -6.967515945434570312e-01 1.000000000000000000e+00 2.029998898506164551e-01 1.000000000000000000e+00 -7.145986557006835938e-01 1.000000000000000000e+00 2.186194658279418945e-01 1.000000000000000000e+00 -7.324457168579101562e-01 1.000000000000000000e+00 2.341609448194503784e-01 1.000000000000000000e+00 -7.502927780151367188e-01 1.000000000000000000e+00 2.185413688421249390e-01 1.000000000000000000e+00 -7.681398391723632812e-01 1.000000000000000000e+00 2.029217928647994995e-01 1.000000000000000000e+00 -7.859869003295898438e-01 1.000000000000000000e+00 1.873022019863128662e-01 1.000000000000000000e+00 -8.038339614868164062e-01 1.000000000000000000e+00 1.716826260089874268e-01 1.000000000000000000e+00 -8.216810226440429688e-01 1.000000000000000000e+00 1.560630500316619873e-01 1.000000000000000000e+00 -8.395280838012695312e-01 1.000000000000000000e+00 1.404434591531753540e-01 1.000000000000000000e+00 -8.573751449584960938e-01 1.000000000000000000e+00 1.248238831758499146e-01 1.000000000000000000e+00 -8.752222657203674316e-01 1.000000000000000000e+00 1.092042997479438782e-01 1.000000000000000000e+00 -8.930693268775939941e-01 1.000000000000000000e+00 9.358472377061843872e-02 1.000000000000000000e+00 -9.109163880348205566e-01 1.000000000000000000e+00 7.796514034271240234e-02 1.000000000000000000e+00 -9.287634491920471191e-01 1.000000000000000000e+00 6.234555691480636597e-02 1.000000000000000000e+00 -9.466105103492736816e-01 1.000000000000000000e+00 4.672597721219062805e-02 1.000000000000000000e+00 -9.644575715065002441e-01 9.904056191444396973e-01 3.110639564692974091e-02 1.000000000000000000e+00 -9.823046326637268066e-01 9.807338118553161621e-01 1.548681501299142838e-02 1.000000000000000000e+00 -1.000000000000000000e+00 9.710620641708374023e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.613902568817138672e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.517185091972351074e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.420467019081115723e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.323749542236328125e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.227032065391540527e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.130313992500305176e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.033596515655517578e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.936878442764282227e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.840160965919494629e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.743442893028259277e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.646725416183471680e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.550007939338684082e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.453289866447448730e-01 4.213010426610708237e-03 1.000000000000000000e+00 -1.000000000000000000e+00 8.356572389602661133e-01 8.434463292360305786e-03 1.000000000000000000e+00 -1.000000000000000000e+00 8.259854316711425781e-01 1.265591662377119064e-02 1.000000000000000000e+00 -1.000000000000000000e+00 8.163136839866638184e-01 1.687736995518207550e-02 1.000000000000000000e+00 -1.000000000000000000e+00 8.066418766975402832e-01 2.109882421791553497e-02 1.000000000000000000e+00 -1.000000000000000000e+00 7.969701290130615234e-01 2.532027661800384521e-02 1.000000000000000000e+00 -1.000000000000000000e+00 7.872983217239379883e-01 2.954173088073730469e-02 1.000000000000000000e+00 -1.000000000000000000e+00 7.776265740394592285e-01 3.376318514347076416e-02 1.000000000000000000e+00 -1.000000000000000000e+00 7.679548263549804688e-01 3.798463568091392517e-02 1.000000000000000000e+00 -1.000000000000000000e+00 7.582830190658569336e-01 4.220608994364738464e-02 1.000000000000000000e+00 -1.000000000000000000e+00 7.486112713813781738e-01 4.642754420638084412e-02 1.000000000000000000e+00 -1.000000000000000000e+00 7.389394640922546387e-01 5.064899474382400513e-02 1.000000000000000000e+00 -1.000000000000000000e+00 7.292677164077758789e-01 5.487044900655746460e-02 1.000000000000000000e+00 -1.000000000000000000e+00 6.973093748092651367e-01 5.126416683197021484e-02 1.000000000000000000e+00 -1.000000000000000000e+00 6.651939153671264648e-01 4.760270193219184875e-02 1.000000000000000000e+00 -1.000000000000000000e+00 6.330785155296325684e-01 4.394123703241348267e-02 1.000000000000000000e+00 -1.000000000000000000e+00 6.009630560874938965e-01 4.027977213263511658e-02 1.000000000000000000e+00 -1.000000000000000000e+00 5.688476562500000000e-01 3.661830723285675049e-02 1.000000000000000000e+00 -1.000000000000000000e+00 5.367321968078613281e-01 3.295684233307838440e-02 1.000000000000000000e+00 -1.000000000000000000e+00 5.046167969703674316e-01 2.929537743330001831e-02 1.000000000000000000e+00 -1.000000000000000000e+00 4.725013375282287598e-01 2.563391439616680145e-02 1.000000000000000000e+00 -1.000000000000000000e+00 4.403859078884124756e-01 2.197244949638843536e-02 1.000000000000000000e+00 -1.000000000000000000e+00 4.082704782485961914e-01 1.831098459661006927e-02 1.000000000000000000e+00 -1.000000000000000000e+00 3.761550486087799072e-01 1.464951969683170319e-02 1.000000000000000000e+00 -1.000000000000000000e+00 3.440396189689636230e-01 1.098805479705333710e-02 1.000000000000000000e+00 -1.000000000000000000e+00 3.119241893291473389e-01 7.326590828597545624e-03 1.000000000000000000e+00 -1.000000000000000000e+00 2.798087596893310547e-01 3.665126161649823189e-03 1.000000000000000000e+00 -1.000000000000000000e+00 2.610737383365631104e-01 3.661464688775595278e-06 1.000000000000000000e+00 -1.000000000000000000e+00 2.424262911081314087e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.237788289785385132e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.051313668489456177e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.864839196205139160e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.678364574909210205e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.491889953613281250e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.305415332317352295e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.118940785527229309e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.324661642313003540e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.459916174411773682e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.595169961452484131e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.730424121022224426e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.865678280591964722e-02 6.875969469547271729e-02 1.000000000000000000e+00 -1.000000000000000000e+00 9.323729500465560704e-06 1.383193135261535645e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 2.078789472579956055e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 2.774385809898376465e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 3.469982147216796875e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 4.165578186511993408e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 4.861174523830413818e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 5.556770563125610352e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 6.252366900444030762e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 6.947963237762451172e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 7.643559575080871582e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 8.339155912399291992e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 9.034752249717712402e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 9.730348587036132812e-01 1.000000000000000000e+00 -9.732819199562072754e-01 1.335734780877828598e-02 9.868275523185729980e-01 1.000000000000000000e+00 -9.462666511535644531e-01 2.686326205730438232e-02 1.000000000000000000e+00 1.000000000000000000e+00 -9.192514419555664062e-01 4.036917537450790405e-02 1.000000000000000000e+00 1.000000000000000000e+00 -8.922361731529235840e-01 5.387508869171142578e-02 1.000000000000000000e+00 1.000000000000000000e+00 -8.652209043502807617e-01 6.738100200891494751e-02 1.000000000000000000e+00 1.000000000000000000e+00 -8.382056355476379395e-01 8.088691532611846924e-02 1.000000000000000000e+00 1.000000000000000000e+00 -8.111904263496398926e-01 9.439282864332199097e-02 1.000000000000000000e+00 1.000000000000000000e+00 -7.841751575469970703e-01 1.078987419605255127e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.571598887443542480e-01 1.214046552777290344e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.301446795463562012e-01 1.349105685949325562e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.031294107437133789e-01 1.484164744615554810e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.761141419410705566e-01 1.619223952293395996e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.490989327430725098e-01 1.755203455686569214e-01 9.999552965164184570e-01 1.000000000000000000e+00 -6.220836639404296875e-01 1.987140327692031860e-01 9.952479600906372070e-01 1.000000000000000000e+00 -6.448003053665161133e-01 2.219077050685882568e-01 9.905406832695007324e-01 1.000000000000000000e+00 -6.680446267127990723e-01 2.451013922691345215e-01 9.858333468437194824e-01 1.000000000000000000e+00 -6.912889480590820312e-01 2.682950794696807861e-01 9.811260104179382324e-01 1.000000000000000000e+00 -7.145333290100097656e-01 2.914887666702270508e-01 9.764187335968017578e-01 1.000000000000000000e+00 -7.377776503562927246e-01 3.146824538707733154e-01 9.717113971710205078e-01 1.000000000000000000e+00 -7.610220313072204590e-01 3.378761410713195801e-01 9.670041203498840332e-01 1.000000000000000000e+00 -7.842663526535034180e-01 3.610698282718658447e-01 9.622967839241027832e-01 1.000000000000000000e+00 -8.075107336044311523e-01 3.842635154724121094e-01 9.575895071029663086e-01 1.000000000000000000e+00 -8.307550549507141113e-01 4.074572026729583740e-01 9.528821706771850586e-01 1.000000000000000000e+00 -8.539993762969970703e-01 4.306508898735046387e-01 9.481748342514038086e-01 1.000000000000000000e+00 -8.772437572479248047e-01 4.538445770740509033e-01 9.434675574302673340e-01 1.000000000000000000e+00 -9.004880785942077637e-01 4.770382642745971680e-01 9.387602210044860840e-01 1.000000000000000000e+00 -9.235278964042663574e-01 5.001816749572753906e-01 9.341238141059875488e-01 1.000000000000000000e+00 -9.263191223144531250e-01 5.183477401733398438e-01 9.365075230598449707e-01 1.000000000000000000e+00 -9.291104078292846680e-01 5.365138649940490723e-01 9.388912320137023926e-01 1.000000000000000000e+00 -9.319016337394714355e-01 5.546799302101135254e-01 9.412749409675598145e-01 1.000000000000000000e+00 -9.346928596496582031e-01 5.728459954261779785e-01 9.436586499214172363e-01 1.000000000000000000e+00 -9.374840855598449707e-01 5.910121202468872070e-01 9.460423588752746582e-01 1.000000000000000000e+00 -9.402753114700317383e-01 6.091781854629516602e-01 9.484260082244873047e-01 1.000000000000000000e+00 -9.430665373802185059e-01 6.273443102836608887e-01 9.508097171783447266e-01 1.000000000000000000e+00 -9.458577632904052734e-01 6.455103754997253418e-01 9.531934261322021484e-01 1.000000000000000000e+00 -9.486490488052368164e-01 6.636765003204345703e-01 9.555771350860595703e-01 1.000000000000000000e+00 -9.514402747154235840e-01 6.818425655364990234e-01 9.579608440399169922e-01 1.000000000000000000e+00 -9.542315006256103516e-01 7.000086307525634766e-01 9.603444933891296387e-01 1.000000000000000000e+00 -9.570227265357971191e-01 7.181747555732727051e-01 9.627282023429870605e-01 1.000000000000000000e+00 -9.598139524459838867e-01 7.363408207893371582e-01 9.651119112968444824e-01 1.000000000000000000e+00 -9.626051783561706543e-01 7.545069456100463867e-01 9.674956202507019043e-01 1.000000000000000000e+00 -9.653964042663574219e-01 7.726730108261108398e-01 9.698793292045593262e-01 1.000000000000000000e+00 -9.681876301765441895e-01 7.908390760421752930e-01 9.722630381584167480e-01 1.000000000000000000e+00 -9.709789156913757324e-01 8.090052008628845215e-01 9.746466875076293945e-01 1.000000000000000000e+00 -9.737701416015625000e-01 8.271712660789489746e-01 9.770303964614868164e-01 1.000000000000000000e+00 -9.765613675117492676e-01 8.453373908996582031e-01 9.794141054153442383e-01 1.000000000000000000e+00 -9.793525934219360352e-01 8.635034561157226562e-01 9.817978143692016602e-01 1.000000000000000000e+00 -9.821438193321228027e-01 8.816695213317871094e-01 9.841815233230590820e-01 1.000000000000000000e+00 -9.849350452423095703e-01 8.998356461524963379e-01 9.865652322769165039e-01 1.000000000000000000e+00 -9.877262711524963379e-01 9.180017113685607910e-01 9.889488816261291504e-01 1.000000000000000000e+00 -9.905175566673278809e-01 9.361678361892700195e-01 9.913325905799865723e-01 1.000000000000000000e+00 -9.933087825775146484e-01 9.543339014053344727e-01 9.937162995338439941e-01 1.000000000000000000e+00 -9.961000084877014160e-01 9.725000262260437012e-01 9.961000084877014160e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/gist_rainbow b/fastplotlib/utils/colormaps/gist_rainbow deleted file mode 100644 index fb672f385..000000000 --- a/fastplotlib/utils/colormaps/gist_rainbow +++ /dev/null @@ -1,256 +0,0 @@ -1.000000000000000000e+00 0.000000000000000000e+00 1.599999964237213135e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 1.390849649906158447e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 1.181699335575103760e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 9.725490212440490723e-02 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 7.633987069129943848e-02 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 5.542483553290367126e-02 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 3.450980409979820251e-02 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 1.359477080404758453e-02 1.000000000000000000e+00 -1.000000000000000000e+00 7.419183850288391113e-03 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.861685305833816528e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.981451854109764099e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.101219147443771362e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.220985323190689087e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.134075224399566650e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.346051990985870361e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.558028608560562134e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.770005226135253906e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.981981992721557617e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.193958610296249390e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.405935376882553101e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.617911994457244873e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.829888761043548584e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.041865527629852295e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.253841996192932129e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.465818762779235840e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.677795529365539551e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.889771997928619385e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.101748764514923096e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.313725531101226807e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.525702297687530518e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.737678766250610352e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.949655532836914062e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.161632299423217773e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.373609066009521484e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.585585832595825195e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.797562003135681152e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.009538769721984863e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.221515536308288574e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.433492302894592285e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.645469069480895996e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.857445836067199707e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.069422602653503418e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.281398773193359375e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.493375539779663086e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.705352306365966797e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.917329072952270508e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.129305839538574219e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.341282606124877930e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.553259372711181641e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.765235543251037598e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.977212309837341309e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.189189076423645020e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.401165843009948730e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.613142609596252441e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.825119376182556152e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.962903857231140137e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.750927686691284180e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.538950920104980469e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.326974153518676758e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.114997386932373047e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.903020620346069336e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.691043853759765625e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.479067087173461914e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.267090916633605957e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.055114150047302246e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.843137383460998535e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.631160616874694824e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.419183850288391113e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.207207083702087402e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.995230317115783691e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.783253550529479980e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.571277379989624023e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.359300613403320312e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.147323846817016602e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.935347080230712891e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.723370313644409180e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.511393547058105469e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.299416780471801758e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.087440609931945801e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.875463843345642090e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.663487076759338379e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.451510310173034668e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.239533543586730957e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.027557075023651123e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.815580308437347412e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.603603541851043701e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.391626775264739990e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.179650306701660156e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.967673540115356445e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.755696773529052734e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.543720304965972900e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.331743538379669189e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.119766771793365479e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.907790154218673706e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.695813387632369995e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.483836770057678223e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.271860152482986450e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.059883385896682739e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.479066938161849976e-02 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.359300762414932251e-02 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.239533469080924988e-02 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.119766734540462494e-02 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 2.108370140194892883e-02 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 4.216740280389785767e-02 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 6.325110793113708496e-02 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 8.433480560779571533e-02 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 1.054185107350349426e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 1.265022158622741699e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 1.475859135389328003e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 1.686696112155914307e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 1.897533237934112549e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 2.108370214700698853e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 2.319207191467285156e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 2.530044317245483398e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 2.740881443023681641e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 2.951718270778656006e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 3.162555396556854248e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 3.373392224311828613e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 3.584229350090026855e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 3.795066475868225098e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 4.005903303623199463e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 4.216740429401397705e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 4.427577555179595947e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 4.638414382934570312e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 4.849251508712768555e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 5.060088634490966797e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 5.270925760269165039e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 5.481762886047363281e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 5.692599415779113770e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 5.903436541557312012e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 6.114273667335510254e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 6.325110793113708496e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 6.535947918891906738e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 6.746784448623657227e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 6.957621574401855469e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 7.168458700180053711e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 7.379295825958251953e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 7.590132951736450195e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 7.800970077514648438e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 8.011806607246398926e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 8.222643733024597168e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 8.433480858802795410e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 8.644317984580993652e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 8.855155110359191895e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 9.065992236137390137e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 9.276828765869140625e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 9.487665891647338867e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 9.698503017425537109e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 9.909340143203735352e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.878516793251037598e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 9.665387868881225586e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 9.452258944511413574e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 9.239130616188049316e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 9.026001691818237305e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 8.812872767448425293e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 8.599744439125061035e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 8.386615514755249023e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 8.173486590385437012e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 7.960358262062072754e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 7.747229337692260742e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 7.534100413322448730e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 7.320972084999084473e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 7.107843160629272461e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 6.894714236259460449e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 6.681585907936096191e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 6.468456983566284180e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 6.255328059196472168e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 6.042199730873107910e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 5.829070806503295898e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 5.615941882133483887e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 5.402813553810119629e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 5.189684629440307617e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 4.976555705070495605e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 4.763427078723907471e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 4.550298452377319336e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 4.337169528007507324e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 4.124040901660919189e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 3.910912275314331055e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 3.697783350944519043e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 3.484654724597930908e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 3.271526098251342773e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 3.058397173881530762e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 2.845268547534942627e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 2.632139921188354492e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 2.419011145830154419e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 2.205882370471954346e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 1.992753595113754272e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 1.779624819755554199e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 1.566496193408966064e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 1.353367418050765991e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 1.140238717198371887e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 9.271099418401718140e-02 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 7.139812409877777100e-02 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 5.008525028824806213e-02 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 2.877237834036350250e-02 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 7.459505461156368256e-03 1.000000000000000000e+00 1.000000000000000000e+00 -1.385336741805076599e-02 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -3.516624122858047485e-02 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -5.647911503911018372e-02 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -7.779198884963989258e-02 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -9.910485893487930298e-02 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -1.204177290201187134e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -1.417306065559387207e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -1.630434840917587280e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -1.843563467264175415e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -2.056692242622375488e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -2.269821017980575562e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -2.482949644327163696e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -2.696078419685363770e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -2.909207046031951904e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -3.122335970401763916e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -3.335464596748352051e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -3.548593223094940186e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -3.761722147464752197e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -3.974850773811340332e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -4.187979400157928467e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -4.401108324527740479e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -4.614236950874328613e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -4.827365875244140625e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -5.040494203567504883e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -5.253623127937316895e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -5.466752052307128906e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -5.679880380630493164e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -5.893009305000305176e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -6.106138229370117188e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -6.319266557693481445e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -6.532395482063293457e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -6.745524406433105469e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -6.958652734756469727e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -7.171781659126281738e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -7.384910583496093750e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -7.598039507865905762e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -7.811167836189270020e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -8.024296760559082031e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -8.237425684928894043e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -8.450554013252258301e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -8.663682937622070312e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -8.876811861991882324e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -9.089940190315246582e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -9.303069114685058594e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -9.516198039054870605e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -9.729326367378234863e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -9.942455291748046875e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 9.844415783882141113e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 9.631287455558776855e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 9.418158531188964844e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 9.205029606819152832e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 8.991901278495788574e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 8.778772354125976562e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 8.565643429756164551e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 8.352515101432800293e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 8.139386177062988281e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 7.926257252693176270e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 7.713128924369812012e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 7.500000000000000000e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/gist_stern b/fastplotlib/utils/colormaps/gist_stern deleted file mode 100644 index 797229f41..000000000 --- a/fastplotlib/utils/colormaps/gist_stern +++ /dev/null @@ -1,256 +0,0 @@ -0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.169229537248611450e-02 3.921568859368562698e-03 7.843137718737125397e-03 1.000000000000000000e+00 -1.433845907449722290e-01 7.843137718737125397e-03 1.568627543747425079e-02 1.000000000000000000e+00 -2.150768935680389404e-01 1.176470611244440079e-02 2.352941222488880157e-02 1.000000000000000000e+00 -2.867691814899444580e-01 1.568627543747425079e-02 3.137255087494850159e-02 1.000000000000000000e+00 -3.584614694118499756e-01 1.960784383118152618e-02 3.921568766236305237e-02 1.000000000000000000e+00 -4.301537871360778809e-01 2.352941222488880157e-02 4.705882444977760315e-02 1.000000000000000000e+00 -5.018460750579833984e-01 2.745098061859607697e-02 5.490196123719215393e-02 1.000000000000000000e+00 -5.735383629798889160e-01 3.137255087494850159e-02 6.274510174989700317e-02 1.000000000000000000e+00 -6.452306509017944336e-01 3.529411926865577698e-02 7.058823853731155396e-02 1.000000000000000000e+00 -7.169229388236999512e-01 3.921568766236305237e-02 7.843137532472610474e-02 1.000000000000000000e+00 -7.886152863502502441e-01 4.313725605607032776e-02 8.627451211214065552e-02 1.000000000000000000e+00 -8.603075742721557617e-01 4.705882444977760315e-02 9.411764889955520630e-02 1.000000000000000000e+00 -9.319998621940612793e-01 5.098039284348487854e-02 1.019607856869697571e-01 1.000000000000000000e+00 -9.989938139915466309e-01 5.490196123719215393e-02 1.098039224743843079e-01 1.000000000000000000e+00 -9.794562458992004395e-01 5.882352963089942932e-02 1.176470592617988586e-01 1.000000000000000000e+00 -9.599186778068542480e-01 6.274510174989700317e-02 1.254902034997940063e-01 1.000000000000000000e+00 -9.403811097145080566e-01 6.666667014360427856e-02 1.333333402872085571e-01 1.000000000000000000e+00 -9.208435416221618652e-01 7.058823853731155396e-02 1.411764770746231079e-01 1.000000000000000000e+00 -9.013059735298156738e-01 7.450980693101882935e-02 1.490196138620376587e-01 1.000000000000000000e+00 -8.817684054374694824e-01 7.843137532472610474e-02 1.568627506494522095e-01 1.000000000000000000e+00 -8.622308373451232910e-01 8.235294371843338013e-02 1.647058874368667603e-01 1.000000000000000000e+00 -8.426933288574218750e-01 8.627451211214065552e-02 1.725490242242813110e-01 1.000000000000000000e+00 -8.231557607650756836e-01 9.019608050584793091e-02 1.803921610116958618e-01 1.000000000000000000e+00 -8.036181926727294922e-01 9.411764889955520630e-02 1.882352977991104126e-01 1.000000000000000000e+00 -7.840806245803833008e-01 9.803921729326248169e-02 1.960784345865249634e-01 1.000000000000000000e+00 -7.645430564880371094e-01 1.019607856869697571e-01 2.039215713739395142e-01 1.000000000000000000e+00 -7.450054883956909180e-01 1.058823540806770325e-01 2.117647081613540649e-01 1.000000000000000000e+00 -7.254679203033447266e-01 1.098039224743843079e-01 2.196078449487686157e-01 1.000000000000000000e+00 -7.059303522109985352e-01 1.137254908680915833e-01 2.274509817361831665e-01 1.000000000000000000e+00 -6.863927841186523438e-01 1.176470592617988586e-01 2.352941185235977173e-01 1.000000000000000000e+00 -6.668552160263061523e-01 1.215686276555061340e-01 2.431372553110122681e-01 1.000000000000000000e+00 -6.473176479339599609e-01 1.254902034997940063e-01 2.509804069995880127e-01 1.000000000000000000e+00 -6.277800798416137695e-01 1.294117718935012817e-01 2.588235437870025635e-01 1.000000000000000000e+00 -6.082425117492675781e-01 1.333333402872085571e-01 2.666666805744171143e-01 1.000000000000000000e+00 -5.887049436569213867e-01 1.372549086809158325e-01 2.745098173618316650e-01 1.000000000000000000e+00 -5.691673755645751953e-01 1.411764770746231079e-01 2.823529541492462158e-01 1.000000000000000000e+00 -5.496298670768737793e-01 1.450980454683303833e-01 2.901960909366607666e-01 1.000000000000000000e+00 -5.300922989845275879e-01 1.490196138620376587e-01 2.980392277240753174e-01 1.000000000000000000e+00 -5.105547308921813965e-01 1.529411822557449341e-01 3.058823645114898682e-01 1.000000000000000000e+00 -4.910171627998352051e-01 1.568627506494522095e-01 3.137255012989044189e-01 1.000000000000000000e+00 -4.714795947074890137e-01 1.607843190431594849e-01 3.215686380863189697e-01 1.000000000000000000e+00 -4.519420266151428223e-01 1.647058874368667603e-01 3.294117748737335205e-01 1.000000000000000000e+00 -4.324044585227966309e-01 1.686274558305740356e-01 3.372549116611480713e-01 1.000000000000000000e+00 -4.128668904304504395e-01 1.725490242242813110e-01 3.450980484485626221e-01 1.000000000000000000e+00 -3.933293223381042480e-01 1.764705926179885864e-01 3.529411852359771729e-01 1.000000000000000000e+00 -3.737917542457580566e-01 1.803921610116958618e-01 3.607843220233917236e-01 1.000000000000000000e+00 -3.542541861534118652e-01 1.843137294054031372e-01 3.686274588108062744e-01 1.000000000000000000e+00 -3.347166478633880615e-01 1.882352977991104126e-01 3.764705955982208252e-01 1.000000000000000000e+00 -3.151790797710418701e-01 1.921568661928176880e-01 3.843137323856353760e-01 1.000000000000000000e+00 -2.956415116786956787e-01 1.960784345865249634e-01 3.921568691730499268e-01 1.000000000000000000e+00 -2.761039435863494873e-01 2.000000029802322388e-01 4.000000059604644775e-01 1.000000000000000000e+00 -2.565663754940032959e-01 2.039215713739395142e-01 4.078431427478790283e-01 1.000000000000000000e+00 -2.370288074016571045e-01 2.078431397676467896e-01 4.156862795352935791e-01 1.000000000000000000e+00 -2.174912542104721069e-01 2.117647081613540649e-01 4.235294163227081299e-01 1.000000000000000000e+00 -1.979536861181259155e-01 2.156862765550613403e-01 4.313725531101226807e-01 1.000000000000000000e+00 -1.784161180257797241e-01 2.196078449487686157e-01 4.392156898975372314e-01 1.000000000000000000e+00 -1.588785648345947266e-01 2.235294133424758911e-01 4.470588266849517822e-01 1.000000000000000000e+00 -1.393409967422485352e-01 2.274509817361831665e-01 4.549019634723663330e-01 1.000000000000000000e+00 -1.198034286499023438e-01 2.313725501298904419e-01 4.627451002597808838e-01 1.000000000000000000e+00 -1.002658680081367493e-01 2.352941185235977173e-01 4.705882370471954346e-01 1.000000000000000000e+00 -8.072829991579055786e-02 2.392156869173049927e-01 4.784313738346099854e-01 1.000000000000000000e+00 -6.119073554873466492e-02 2.431372553110122681e-01 4.862745106220245361e-01 1.000000000000000000e+00 -4.165317490696907043e-02 2.470588237047195435e-01 4.941176474094390869e-01 1.000000000000000000e+00 -2.509804069995880127e-01 2.509804069995880127e-01 5.019608139991760254e-01 1.000000000000000000e+00 -2.549019753932952881e-01 2.549019753932952881e-01 5.098039507865905762e-01 1.000000000000000000e+00 -2.588235437870025635e-01 2.588235437870025635e-01 5.176470875740051270e-01 1.000000000000000000e+00 -2.627451121807098389e-01 2.627451121807098389e-01 5.254902243614196777e-01 1.000000000000000000e+00 -2.666666805744171143e-01 2.666666805744171143e-01 5.333333611488342285e-01 1.000000000000000000e+00 -2.705882489681243896e-01 2.705882489681243896e-01 5.411764979362487793e-01 1.000000000000000000e+00 -2.745098173618316650e-01 2.745098173618316650e-01 5.490196347236633301e-01 1.000000000000000000e+00 -2.784313857555389404e-01 2.784313857555389404e-01 5.568627715110778809e-01 1.000000000000000000e+00 -2.823529541492462158e-01 2.823529541492462158e-01 5.647059082984924316e-01 1.000000000000000000e+00 -2.862745225429534912e-01 2.862745225429534912e-01 5.725490450859069824e-01 1.000000000000000000e+00 -2.901960909366607666e-01 2.901960909366607666e-01 5.803921818733215332e-01 1.000000000000000000e+00 -2.941176593303680420e-01 2.941176593303680420e-01 5.882353186607360840e-01 1.000000000000000000e+00 -2.980392277240753174e-01 2.980392277240753174e-01 5.960784554481506348e-01 1.000000000000000000e+00 -3.019607961177825928e-01 3.019607961177825928e-01 6.039215922355651855e-01 1.000000000000000000e+00 -3.058823645114898682e-01 3.058823645114898682e-01 6.117647290229797363e-01 1.000000000000000000e+00 -3.098039329051971436e-01 3.098039329051971436e-01 6.196078658103942871e-01 1.000000000000000000e+00 -3.137255012989044189e-01 3.137255012989044189e-01 6.274510025978088379e-01 1.000000000000000000e+00 -3.176470696926116943e-01 3.176470696926116943e-01 6.352941393852233887e-01 1.000000000000000000e+00 -3.215686380863189697e-01 3.215686380863189697e-01 6.431372761726379395e-01 1.000000000000000000e+00 -3.254902064800262451e-01 3.254902064800262451e-01 6.509804129600524902e-01 1.000000000000000000e+00 -3.294117748737335205e-01 3.294117748737335205e-01 6.588235497474670410e-01 1.000000000000000000e+00 -3.333333432674407959e-01 3.333333432674407959e-01 6.666666865348815918e-01 1.000000000000000000e+00 -3.372549116611480713e-01 3.372549116611480713e-01 6.745098233222961426e-01 1.000000000000000000e+00 -3.411764800548553467e-01 3.411764800548553467e-01 6.823529601097106934e-01 1.000000000000000000e+00 -3.450980484485626221e-01 3.450980484485626221e-01 6.901960968971252441e-01 1.000000000000000000e+00 -3.490196168422698975e-01 3.490196168422698975e-01 6.980392336845397949e-01 1.000000000000000000e+00 -3.529411852359771729e-01 3.529411852359771729e-01 7.058823704719543457e-01 1.000000000000000000e+00 -3.568627536296844482e-01 3.568627536296844482e-01 7.137255072593688965e-01 1.000000000000000000e+00 -3.607843220233917236e-01 3.607843220233917236e-01 7.215686440467834473e-01 1.000000000000000000e+00 -3.647058904170989990e-01 3.647058904170989990e-01 7.294117808341979980e-01 1.000000000000000000e+00 -3.686274588108062744e-01 3.686274588108062744e-01 7.372549176216125488e-01 1.000000000000000000e+00 -3.725490272045135498e-01 3.725490272045135498e-01 7.450980544090270996e-01 1.000000000000000000e+00 -3.764705955982208252e-01 3.764705955982208252e-01 7.529411911964416504e-01 1.000000000000000000e+00 -3.803921639919281006e-01 3.803921639919281006e-01 7.607843279838562012e-01 1.000000000000000000e+00 -3.843137323856353760e-01 3.843137323856353760e-01 7.686274647712707520e-01 1.000000000000000000e+00 -3.882353007793426514e-01 3.882353007793426514e-01 7.764706015586853027e-01 1.000000000000000000e+00 -3.921568691730499268e-01 3.921568691730499268e-01 7.843137383460998535e-01 1.000000000000000000e+00 -3.960784375667572021e-01 3.960784375667572021e-01 7.921568751335144043e-01 1.000000000000000000e+00 -4.000000059604644775e-01 4.000000059604644775e-01 8.000000119209289551e-01 1.000000000000000000e+00 -4.039215743541717529e-01 4.039215743541717529e-01 8.078431487083435059e-01 1.000000000000000000e+00 -4.078431427478790283e-01 4.078431427478790283e-01 8.156862854957580566e-01 1.000000000000000000e+00 -4.117647111415863037e-01 4.117647111415863037e-01 8.235294222831726074e-01 1.000000000000000000e+00 -4.156862795352935791e-01 4.156862795352935791e-01 8.313725590705871582e-01 1.000000000000000000e+00 -4.196078479290008545e-01 4.196078479290008545e-01 8.392156958580017090e-01 1.000000000000000000e+00 -4.235294163227081299e-01 4.235294163227081299e-01 8.470588326454162598e-01 1.000000000000000000e+00 -4.274509847164154053e-01 4.274509847164154053e-01 8.549019694328308105e-01 1.000000000000000000e+00 -4.313725531101226807e-01 4.313725531101226807e-01 8.627451062202453613e-01 1.000000000000000000e+00 -4.352941215038299561e-01 4.352941215038299561e-01 8.705882430076599121e-01 1.000000000000000000e+00 -4.392156898975372314e-01 4.392156898975372314e-01 8.784313797950744629e-01 1.000000000000000000e+00 -4.431372582912445068e-01 4.431372582912445068e-01 8.862745165824890137e-01 1.000000000000000000e+00 -4.470588266849517822e-01 4.470588266849517822e-01 8.941176533699035645e-01 1.000000000000000000e+00 -4.509803950786590576e-01 4.509803950786590576e-01 9.019607901573181152e-01 1.000000000000000000e+00 -4.549019634723663330e-01 4.549019634723663330e-01 9.098039269447326660e-01 1.000000000000000000e+00 -4.588235318660736084e-01 4.588235318660736084e-01 9.176470637321472168e-01 1.000000000000000000e+00 -4.627451002597808838e-01 4.627451002597808838e-01 9.254902005195617676e-01 1.000000000000000000e+00 -4.666666686534881592e-01 4.666666686534881592e-01 9.333333373069763184e-01 1.000000000000000000e+00 -4.705882370471954346e-01 4.705882370471954346e-01 9.411764740943908691e-01 1.000000000000000000e+00 -4.745098054409027100e-01 4.745098054409027100e-01 9.490196108818054199e-01 1.000000000000000000e+00 -4.784313738346099854e-01 4.784313738346099854e-01 9.568627476692199707e-01 1.000000000000000000e+00 -4.823529422283172607e-01 4.823529422283172607e-01 9.647058844566345215e-01 1.000000000000000000e+00 -4.862745106220245361e-01 4.862745106220245361e-01 9.725490212440490723e-01 1.000000000000000000e+00 -4.901960790157318115e-01 4.901960790157318115e-01 9.803921580314636230e-01 1.000000000000000000e+00 -4.941176474094390869e-01 4.941176474094390869e-01 9.882352948188781738e-01 1.000000000000000000e+00 -4.980392158031463623e-01 4.980392158031463623e-01 9.960784316062927246e-01 1.000000000000000000e+00 -5.019608139991760254e-01 5.019608139991760254e-01 9.916562438011169434e-01 1.000000000000000000e+00 -5.058823823928833008e-01 5.058823823928833008e-01 9.749687314033508301e-01 1.000000000000000000e+00 -5.098039507865905762e-01 5.098039507865905762e-01 9.582811594009399414e-01 1.000000000000000000e+00 -5.137255191802978516e-01 5.137255191802978516e-01 9.415936470031738281e-01 1.000000000000000000e+00 -5.176470875740051270e-01 5.176470875740051270e-01 9.249061346054077148e-01 1.000000000000000000e+00 -5.215686559677124023e-01 5.215686559677124023e-01 9.082186222076416016e-01 1.000000000000000000e+00 -5.254902243614196777e-01 5.254902243614196777e-01 8.915311098098754883e-01 1.000000000000000000e+00 -5.294117927551269531e-01 5.294117927551269531e-01 8.748435378074645996e-01 1.000000000000000000e+00 -5.333333611488342285e-01 5.333333611488342285e-01 8.581560254096984863e-01 1.000000000000000000e+00 -5.372549295425415039e-01 5.372549295425415039e-01 8.414685130119323730e-01 1.000000000000000000e+00 -5.411764979362487793e-01 5.411764979362487793e-01 8.247810006141662598e-01 1.000000000000000000e+00 -5.450980663299560547e-01 5.450980663299560547e-01 8.080934286117553711e-01 1.000000000000000000e+00 -5.490196347236633301e-01 5.490196347236633301e-01 7.914059162139892578e-01 1.000000000000000000e+00 -5.529412031173706055e-01 5.529412031173706055e-01 7.747184038162231445e-01 1.000000000000000000e+00 -5.568627715110778809e-01 5.568627715110778809e-01 7.580308914184570312e-01 1.000000000000000000e+00 -5.607843399047851562e-01 5.607843399047851562e-01 7.413433194160461426e-01 1.000000000000000000e+00 -5.647059082984924316e-01 5.647059082984924316e-01 7.246558070182800293e-01 1.000000000000000000e+00 -5.686274766921997070e-01 5.686274766921997070e-01 7.079682946205139160e-01 1.000000000000000000e+00 -5.725490450859069824e-01 5.725490450859069824e-01 6.912807822227478027e-01 1.000000000000000000e+00 -5.764706134796142578e-01 5.764706134796142578e-01 6.745932698249816895e-01 1.000000000000000000e+00 -5.803921818733215332e-01 5.803921818733215332e-01 6.579056978225708008e-01 1.000000000000000000e+00 -5.843137502670288086e-01 5.843137502670288086e-01 6.412181854248046875e-01 1.000000000000000000e+00 -5.882353186607360840e-01 5.882353186607360840e-01 6.245306730270385742e-01 1.000000000000000000e+00 -5.921568870544433594e-01 5.921568870544433594e-01 6.078431606292724609e-01 1.000000000000000000e+00 -5.960784554481506348e-01 5.960784554481506348e-01 5.911555886268615723e-01 1.000000000000000000e+00 -6.000000238418579102e-01 6.000000238418579102e-01 5.744680762290954590e-01 1.000000000000000000e+00 -6.039215922355651855e-01 6.039215922355651855e-01 5.577805638313293457e-01 1.000000000000000000e+00 -6.078431606292724609e-01 6.078431606292724609e-01 5.410930514335632324e-01 1.000000000000000000e+00 -6.117647290229797363e-01 6.117647290229797363e-01 5.244054794311523438e-01 1.000000000000000000e+00 -6.156862974166870117e-01 6.156862974166870117e-01 5.077179670333862305e-01 1.000000000000000000e+00 -6.196078658103942871e-01 6.196078658103942871e-01 4.910304546356201172e-01 1.000000000000000000e+00 -6.235294342041015625e-01 6.235294342041015625e-01 4.743429422378540039e-01 1.000000000000000000e+00 -6.274510025978088379e-01 6.274510025978088379e-01 4.576554000377655029e-01 1.000000000000000000e+00 -6.313725709915161133e-01 6.313725709915161133e-01 4.409678876399993896e-01 1.000000000000000000e+00 -6.352941393852233887e-01 6.352941393852233887e-01 4.242803454399108887e-01 1.000000000000000000e+00 -6.392157077789306641e-01 6.392157077789306641e-01 4.075928330421447754e-01 1.000000000000000000e+00 -6.431372761726379395e-01 6.431372761726379395e-01 3.909052908420562744e-01 1.000000000000000000e+00 -6.470588445663452148e-01 6.470588445663452148e-01 3.742177784442901611e-01 1.000000000000000000e+00 -6.509804129600524902e-01 6.509804129600524902e-01 3.575302362442016602e-01 1.000000000000000000e+00 -6.549019813537597656e-01 6.549019813537597656e-01 3.408427238464355469e-01 1.000000000000000000e+00 -6.588235497474670410e-01 6.588235497474670410e-01 3.241551816463470459e-01 1.000000000000000000e+00 -6.627451181411743164e-01 6.627451181411743164e-01 3.074676692485809326e-01 1.000000000000000000e+00 -6.666666865348815918e-01 6.666666865348815918e-01 2.907801270484924316e-01 1.000000000000000000e+00 -6.705882549285888672e-01 6.705882549285888672e-01 2.740926146507263184e-01 1.000000000000000000e+00 -6.745098233222961426e-01 6.745098233222961426e-01 2.574051022529602051e-01 1.000000000000000000e+00 -6.784313917160034180e-01 6.784313917160034180e-01 2.407175600528717041e-01 1.000000000000000000e+00 -6.823529601097106934e-01 6.823529601097106934e-01 2.240300327539443970e-01 1.000000000000000000e+00 -6.862745285034179688e-01 6.862745285034179688e-01 2.073425054550170898e-01 1.000000000000000000e+00 -6.901960968971252441e-01 6.901960968971252441e-01 1.906549781560897827e-01 1.000000000000000000e+00 -6.941176652908325195e-01 6.941176652908325195e-01 1.739674657583236694e-01 1.000000000000000000e+00 -6.980392336845397949e-01 6.980392336845397949e-01 1.572799384593963623e-01 1.000000000000000000e+00 -7.019608020782470703e-01 7.019608020782470703e-01 1.405924111604690552e-01 1.000000000000000000e+00 -7.058823704719543457e-01 7.058823704719543457e-01 1.239048838615417480e-01 1.000000000000000000e+00 -7.098039388656616211e-01 7.098039388656616211e-01 1.072173565626144409e-01 1.000000000000000000e+00 -7.137255072593688965e-01 7.137255072593688965e-01 9.052982926368713379e-02 1.000000000000000000e+00 -7.176470756530761719e-01 7.176470756530761719e-01 7.384230196475982666e-02 1.000000000000000000e+00 -7.215686440467834473e-01 7.215686440467834473e-01 5.715477839112281799e-02 1.000000000000000000e+00 -7.254902124404907227e-01 7.254902124404907227e-01 4.046725109219551086e-02 1.000000000000000000e+00 -7.294117808341979980e-01 7.294117808341979980e-01 2.377972379326820374e-02 1.000000000000000000e+00 -7.333333492279052734e-01 7.333333492279052734e-01 7.092198356986045837e-03 1.000000000000000000e+00 -7.372549176216125488e-01 7.372549176216125488e-01 8.509064093232154846e-03 1.000000000000000000e+00 -7.411764860153198242e-01 7.411764860153198242e-01 2.330743707716464996e-02 1.000000000000000000e+00 -7.450980544090270996e-01 7.450980544090270996e-01 3.810580819845199585e-02 1.000000000000000000e+00 -7.490196228027343750e-01 7.490196228027343750e-01 5.290418118238449097e-02 1.000000000000000000e+00 -7.529411911964416504e-01 7.529411911964416504e-01 6.770255416631698608e-02 1.000000000000000000e+00 -7.568627595901489258e-01 7.568627595901489258e-01 8.250092715024948120e-02 1.000000000000000000e+00 -7.607843279838562012e-01 7.607843279838562012e-01 9.729930013418197632e-02 1.000000000000000000e+00 -7.647058963775634766e-01 7.647058963775634766e-01 1.120976656675338745e-01 1.000000000000000000e+00 -7.686274647712707520e-01 7.686274647712707520e-01 1.268960386514663696e-01 1.000000000000000000e+00 -7.725490331649780273e-01 7.725490331649780273e-01 1.416944116353988647e-01 1.000000000000000000e+00 -7.764706015586853027e-01 7.764706015586853027e-01 1.564927846193313599e-01 1.000000000000000000e+00 -7.803921699523925781e-01 7.803921699523925781e-01 1.712911576032638550e-01 1.000000000000000000e+00 -7.843137383460998535e-01 7.843137383460998535e-01 1.860895305871963501e-01 1.000000000000000000e+00 -7.882353067398071289e-01 7.882353067398071289e-01 2.008879035711288452e-01 1.000000000000000000e+00 -7.921568751335144043e-01 7.921568751335144043e-01 2.156862765550613403e-01 1.000000000000000000e+00 -7.960784435272216797e-01 7.960784435272216797e-01 2.304846495389938354e-01 1.000000000000000000e+00 -8.000000119209289551e-01 8.000000119209289551e-01 2.452830225229263306e-01 1.000000000000000000e+00 -8.039215803146362305e-01 8.039215803146362305e-01 2.600813806056976318e-01 1.000000000000000000e+00 -8.078431487083435059e-01 8.078431487083435059e-01 2.748797535896301270e-01 1.000000000000000000e+00 -8.117647171020507812e-01 8.117647171020507812e-01 2.896781265735626221e-01 1.000000000000000000e+00 -8.156862854957580566e-01 8.156862854957580566e-01 3.044764995574951172e-01 1.000000000000000000e+00 -8.196078538894653320e-01 8.196078538894653320e-01 3.192748725414276123e-01 1.000000000000000000e+00 -8.235294222831726074e-01 8.235294222831726074e-01 3.340732455253601074e-01 1.000000000000000000e+00 -8.274509906768798828e-01 8.274509906768798828e-01 3.488716185092926025e-01 1.000000000000000000e+00 -8.313725590705871582e-01 8.313725590705871582e-01 3.636699914932250977e-01 1.000000000000000000e+00 -8.352941274642944336e-01 8.352941274642944336e-01 3.784683644771575928e-01 1.000000000000000000e+00 -8.392156958580017090e-01 8.392156958580017090e-01 3.932667374610900879e-01 1.000000000000000000e+00 -8.431372642517089844e-01 8.431372642517089844e-01 4.080651104450225830e-01 1.000000000000000000e+00 -8.470588326454162598e-01 8.470588326454162598e-01 4.228634834289550781e-01 1.000000000000000000e+00 -8.509804010391235352e-01 8.509804010391235352e-01 4.376618564128875732e-01 1.000000000000000000e+00 -8.549019694328308105e-01 8.549019694328308105e-01 4.524602293968200684e-01 1.000000000000000000e+00 -8.588235378265380859e-01 8.588235378265380859e-01 4.672586023807525635e-01 1.000000000000000000e+00 -8.627451062202453613e-01 8.627451062202453613e-01 4.820569753646850586e-01 1.000000000000000000e+00 -8.666666746139526367e-01 8.666666746139526367e-01 4.968553483486175537e-01 1.000000000000000000e+00 -8.705882430076599121e-01 8.705882430076599121e-01 5.116537213325500488e-01 1.000000000000000000e+00 -8.745098114013671875e-01 8.745098114013671875e-01 5.264520645141601562e-01 1.000000000000000000e+00 -8.784313797950744629e-01 8.784313797950744629e-01 5.412504673004150391e-01 1.000000000000000000e+00 -8.823529481887817383e-01 8.823529481887817383e-01 5.560488104820251465e-01 1.000000000000000000e+00 -8.862745165824890137e-01 8.862745165824890137e-01 5.708472132682800293e-01 1.000000000000000000e+00 -8.901960849761962891e-01 8.901960849761962891e-01 5.856455564498901367e-01 1.000000000000000000e+00 -8.941176533699035645e-01 8.941176533699035645e-01 6.004439592361450195e-01 1.000000000000000000e+00 -8.980392217636108398e-01 8.980392217636108398e-01 6.152423024177551270e-01 1.000000000000000000e+00 -9.019607901573181152e-01 9.019607901573181152e-01 6.300407052040100098e-01 1.000000000000000000e+00 -9.058823585510253906e-01 9.058823585510253906e-01 6.448390483856201172e-01 1.000000000000000000e+00 -9.098039269447326660e-01 9.098039269447326660e-01 6.596374511718750000e-01 1.000000000000000000e+00 -9.137254953384399414e-01 9.137254953384399414e-01 6.744357943534851074e-01 1.000000000000000000e+00 -9.176470637321472168e-01 9.176470637321472168e-01 6.892341971397399902e-01 1.000000000000000000e+00 -9.215686321258544922e-01 9.215686321258544922e-01 7.040325403213500977e-01 1.000000000000000000e+00 -9.254902005195617676e-01 9.254902005195617676e-01 7.188309431076049805e-01 1.000000000000000000e+00 -9.294117689132690430e-01 9.294117689132690430e-01 7.336292862892150879e-01 1.000000000000000000e+00 -9.333333373069763184e-01 9.333333373069763184e-01 7.484276890754699707e-01 1.000000000000000000e+00 -9.372549057006835938e-01 9.372549057006835938e-01 7.632260322570800781e-01 1.000000000000000000e+00 -9.411764740943908691e-01 9.411764740943908691e-01 7.780244350433349609e-01 1.000000000000000000e+00 -9.450980424880981445e-01 9.450980424880981445e-01 7.928227782249450684e-01 1.000000000000000000e+00 -9.490196108818054199e-01 9.490196108818054199e-01 8.076211810111999512e-01 1.000000000000000000e+00 -9.529411792755126953e-01 9.529411792755126953e-01 8.224195241928100586e-01 1.000000000000000000e+00 -9.568627476692199707e-01 9.568627476692199707e-01 8.372179269790649414e-01 1.000000000000000000e+00 -9.607843160629272461e-01 9.607843160629272461e-01 8.520162701606750488e-01 1.000000000000000000e+00 -9.647058844566345215e-01 9.647058844566345215e-01 8.668146729469299316e-01 1.000000000000000000e+00 -9.686274528503417969e-01 9.686274528503417969e-01 8.816130161285400391e-01 1.000000000000000000e+00 -9.725490212440490723e-01 9.725490212440490723e-01 8.964114189147949219e-01 1.000000000000000000e+00 -9.764705896377563477e-01 9.764705896377563477e-01 9.112097620964050293e-01 1.000000000000000000e+00 -9.803921580314636230e-01 9.803921580314636230e-01 9.260081648826599121e-01 1.000000000000000000e+00 -9.843137264251708984e-01 9.843137264251708984e-01 9.408065080642700195e-01 1.000000000000000000e+00 -9.882352948188781738e-01 9.882352948188781738e-01 9.556049108505249023e-01 1.000000000000000000e+00 -9.921568632125854492e-01 9.921568632125854492e-01 9.704032540321350098e-01 1.000000000000000000e+00 -9.960784316062927246e-01 9.960784316062927246e-01 9.852016568183898926e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/gist_yarg b/fastplotlib/utils/colormaps/gist_yarg deleted file mode 100644 index 19ae9bd30..000000000 --- a/fastplotlib/utils/colormaps/gist_yarg +++ /dev/null @@ -1,256 +0,0 @@ -1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -9.960784316062927246e-01 9.960784316062927246e-01 9.960784316062927246e-01 1.000000000000000000e+00 -9.921568632125854492e-01 9.921568632125854492e-01 9.921568632125854492e-01 1.000000000000000000e+00 -9.882352948188781738e-01 9.882352948188781738e-01 9.882352948188781738e-01 1.000000000000000000e+00 -9.843137264251708984e-01 9.843137264251708984e-01 9.843137264251708984e-01 1.000000000000000000e+00 -9.803921580314636230e-01 9.803921580314636230e-01 9.803921580314636230e-01 1.000000000000000000e+00 -9.764705896377563477e-01 9.764705896377563477e-01 9.764705896377563477e-01 1.000000000000000000e+00 -9.725490212440490723e-01 9.725490212440490723e-01 9.725490212440490723e-01 1.000000000000000000e+00 -9.686274528503417969e-01 9.686274528503417969e-01 9.686274528503417969e-01 1.000000000000000000e+00 -9.647058844566345215e-01 9.647058844566345215e-01 9.647058844566345215e-01 1.000000000000000000e+00 -9.607843160629272461e-01 9.607843160629272461e-01 9.607843160629272461e-01 1.000000000000000000e+00 -9.568627476692199707e-01 9.568627476692199707e-01 9.568627476692199707e-01 1.000000000000000000e+00 -9.529411792755126953e-01 9.529411792755126953e-01 9.529411792755126953e-01 1.000000000000000000e+00 -9.490196108818054199e-01 9.490196108818054199e-01 9.490196108818054199e-01 1.000000000000000000e+00 -9.450980424880981445e-01 9.450980424880981445e-01 9.450980424880981445e-01 1.000000000000000000e+00 -9.411764740943908691e-01 9.411764740943908691e-01 9.411764740943908691e-01 1.000000000000000000e+00 -9.372549057006835938e-01 9.372549057006835938e-01 9.372549057006835938e-01 1.000000000000000000e+00 -9.333333373069763184e-01 9.333333373069763184e-01 9.333333373069763184e-01 1.000000000000000000e+00 -9.294117689132690430e-01 9.294117689132690430e-01 9.294117689132690430e-01 1.000000000000000000e+00 -9.254902005195617676e-01 9.254902005195617676e-01 9.254902005195617676e-01 1.000000000000000000e+00 -9.215686321258544922e-01 9.215686321258544922e-01 9.215686321258544922e-01 1.000000000000000000e+00 -9.176470637321472168e-01 9.176470637321472168e-01 9.176470637321472168e-01 1.000000000000000000e+00 -9.137254953384399414e-01 9.137254953384399414e-01 9.137254953384399414e-01 1.000000000000000000e+00 -9.098039269447326660e-01 9.098039269447326660e-01 9.098039269447326660e-01 1.000000000000000000e+00 -9.058823585510253906e-01 9.058823585510253906e-01 9.058823585510253906e-01 1.000000000000000000e+00 -9.019607901573181152e-01 9.019607901573181152e-01 9.019607901573181152e-01 1.000000000000000000e+00 -8.980392217636108398e-01 8.980392217636108398e-01 8.980392217636108398e-01 1.000000000000000000e+00 -8.941176533699035645e-01 8.941176533699035645e-01 8.941176533699035645e-01 1.000000000000000000e+00 -8.901960849761962891e-01 8.901960849761962891e-01 8.901960849761962891e-01 1.000000000000000000e+00 -8.862745165824890137e-01 8.862745165824890137e-01 8.862745165824890137e-01 1.000000000000000000e+00 -8.823529481887817383e-01 8.823529481887817383e-01 8.823529481887817383e-01 1.000000000000000000e+00 -8.784313797950744629e-01 8.784313797950744629e-01 8.784313797950744629e-01 1.000000000000000000e+00 -8.745098114013671875e-01 8.745098114013671875e-01 8.745098114013671875e-01 1.000000000000000000e+00 -8.705882430076599121e-01 8.705882430076599121e-01 8.705882430076599121e-01 1.000000000000000000e+00 -8.666666746139526367e-01 8.666666746139526367e-01 8.666666746139526367e-01 1.000000000000000000e+00 -8.627451062202453613e-01 8.627451062202453613e-01 8.627451062202453613e-01 1.000000000000000000e+00 -8.588235378265380859e-01 8.588235378265380859e-01 8.588235378265380859e-01 1.000000000000000000e+00 -8.549019694328308105e-01 8.549019694328308105e-01 8.549019694328308105e-01 1.000000000000000000e+00 -8.509804010391235352e-01 8.509804010391235352e-01 8.509804010391235352e-01 1.000000000000000000e+00 -8.470588326454162598e-01 8.470588326454162598e-01 8.470588326454162598e-01 1.000000000000000000e+00 -8.431372642517089844e-01 8.431372642517089844e-01 8.431372642517089844e-01 1.000000000000000000e+00 -8.392156958580017090e-01 8.392156958580017090e-01 8.392156958580017090e-01 1.000000000000000000e+00 -8.352941274642944336e-01 8.352941274642944336e-01 8.352941274642944336e-01 1.000000000000000000e+00 -8.313725590705871582e-01 8.313725590705871582e-01 8.313725590705871582e-01 1.000000000000000000e+00 -8.274509906768798828e-01 8.274509906768798828e-01 8.274509906768798828e-01 1.000000000000000000e+00 -8.235294222831726074e-01 8.235294222831726074e-01 8.235294222831726074e-01 1.000000000000000000e+00 -8.196078538894653320e-01 8.196078538894653320e-01 8.196078538894653320e-01 1.000000000000000000e+00 -8.156862854957580566e-01 8.156862854957580566e-01 8.156862854957580566e-01 1.000000000000000000e+00 -8.117647171020507812e-01 8.117647171020507812e-01 8.117647171020507812e-01 1.000000000000000000e+00 -8.078431487083435059e-01 8.078431487083435059e-01 8.078431487083435059e-01 1.000000000000000000e+00 -8.039215803146362305e-01 8.039215803146362305e-01 8.039215803146362305e-01 1.000000000000000000e+00 -8.000000119209289551e-01 8.000000119209289551e-01 8.000000119209289551e-01 1.000000000000000000e+00 -7.960784435272216797e-01 7.960784435272216797e-01 7.960784435272216797e-01 1.000000000000000000e+00 -7.921568751335144043e-01 7.921568751335144043e-01 7.921568751335144043e-01 1.000000000000000000e+00 -7.882353067398071289e-01 7.882353067398071289e-01 7.882353067398071289e-01 1.000000000000000000e+00 -7.843137383460998535e-01 7.843137383460998535e-01 7.843137383460998535e-01 1.000000000000000000e+00 -7.803921699523925781e-01 7.803921699523925781e-01 7.803921699523925781e-01 1.000000000000000000e+00 -7.764706015586853027e-01 7.764706015586853027e-01 7.764706015586853027e-01 1.000000000000000000e+00 -7.725490331649780273e-01 7.725490331649780273e-01 7.725490331649780273e-01 1.000000000000000000e+00 -7.686274647712707520e-01 7.686274647712707520e-01 7.686274647712707520e-01 1.000000000000000000e+00 -7.647058963775634766e-01 7.647058963775634766e-01 7.647058963775634766e-01 1.000000000000000000e+00 -7.607843279838562012e-01 7.607843279838562012e-01 7.607843279838562012e-01 1.000000000000000000e+00 -7.568627595901489258e-01 7.568627595901489258e-01 7.568627595901489258e-01 1.000000000000000000e+00 -7.529411911964416504e-01 7.529411911964416504e-01 7.529411911964416504e-01 1.000000000000000000e+00 -7.490196228027343750e-01 7.490196228027343750e-01 7.490196228027343750e-01 1.000000000000000000e+00 -7.450980544090270996e-01 7.450980544090270996e-01 7.450980544090270996e-01 1.000000000000000000e+00 -7.411764860153198242e-01 7.411764860153198242e-01 7.411764860153198242e-01 1.000000000000000000e+00 -7.372549176216125488e-01 7.372549176216125488e-01 7.372549176216125488e-01 1.000000000000000000e+00 -7.333333492279052734e-01 7.333333492279052734e-01 7.333333492279052734e-01 1.000000000000000000e+00 -7.294117808341979980e-01 7.294117808341979980e-01 7.294117808341979980e-01 1.000000000000000000e+00 -7.254902124404907227e-01 7.254902124404907227e-01 7.254902124404907227e-01 1.000000000000000000e+00 -7.215686440467834473e-01 7.215686440467834473e-01 7.215686440467834473e-01 1.000000000000000000e+00 -7.176470756530761719e-01 7.176470756530761719e-01 7.176470756530761719e-01 1.000000000000000000e+00 -7.137255072593688965e-01 7.137255072593688965e-01 7.137255072593688965e-01 1.000000000000000000e+00 -7.098039388656616211e-01 7.098039388656616211e-01 7.098039388656616211e-01 1.000000000000000000e+00 -7.058823704719543457e-01 7.058823704719543457e-01 7.058823704719543457e-01 1.000000000000000000e+00 -7.019608020782470703e-01 7.019608020782470703e-01 7.019608020782470703e-01 1.000000000000000000e+00 -6.980392336845397949e-01 6.980392336845397949e-01 6.980392336845397949e-01 1.000000000000000000e+00 -6.941176652908325195e-01 6.941176652908325195e-01 6.941176652908325195e-01 1.000000000000000000e+00 -6.901960968971252441e-01 6.901960968971252441e-01 6.901960968971252441e-01 1.000000000000000000e+00 -6.862745285034179688e-01 6.862745285034179688e-01 6.862745285034179688e-01 1.000000000000000000e+00 -6.823529601097106934e-01 6.823529601097106934e-01 6.823529601097106934e-01 1.000000000000000000e+00 -6.784313917160034180e-01 6.784313917160034180e-01 6.784313917160034180e-01 1.000000000000000000e+00 -6.745098233222961426e-01 6.745098233222961426e-01 6.745098233222961426e-01 1.000000000000000000e+00 -6.705882549285888672e-01 6.705882549285888672e-01 6.705882549285888672e-01 1.000000000000000000e+00 -6.666666865348815918e-01 6.666666865348815918e-01 6.666666865348815918e-01 1.000000000000000000e+00 -6.627451181411743164e-01 6.627451181411743164e-01 6.627451181411743164e-01 1.000000000000000000e+00 -6.588235497474670410e-01 6.588235497474670410e-01 6.588235497474670410e-01 1.000000000000000000e+00 -6.549019813537597656e-01 6.549019813537597656e-01 6.549019813537597656e-01 1.000000000000000000e+00 -6.509804129600524902e-01 6.509804129600524902e-01 6.509804129600524902e-01 1.000000000000000000e+00 -6.470588445663452148e-01 6.470588445663452148e-01 6.470588445663452148e-01 1.000000000000000000e+00 -6.431372761726379395e-01 6.431372761726379395e-01 6.431372761726379395e-01 1.000000000000000000e+00 -6.392157077789306641e-01 6.392157077789306641e-01 6.392157077789306641e-01 1.000000000000000000e+00 -6.352941393852233887e-01 6.352941393852233887e-01 6.352941393852233887e-01 1.000000000000000000e+00 -6.313725709915161133e-01 6.313725709915161133e-01 6.313725709915161133e-01 1.000000000000000000e+00 -6.274510025978088379e-01 6.274510025978088379e-01 6.274510025978088379e-01 1.000000000000000000e+00 -6.235294342041015625e-01 6.235294342041015625e-01 6.235294342041015625e-01 1.000000000000000000e+00 -6.196078658103942871e-01 6.196078658103942871e-01 6.196078658103942871e-01 1.000000000000000000e+00 -6.156862974166870117e-01 6.156862974166870117e-01 6.156862974166870117e-01 1.000000000000000000e+00 -6.117647290229797363e-01 6.117647290229797363e-01 6.117647290229797363e-01 1.000000000000000000e+00 -6.078431606292724609e-01 6.078431606292724609e-01 6.078431606292724609e-01 1.000000000000000000e+00 -6.039215922355651855e-01 6.039215922355651855e-01 6.039215922355651855e-01 1.000000000000000000e+00 -6.000000238418579102e-01 6.000000238418579102e-01 6.000000238418579102e-01 1.000000000000000000e+00 -5.960784554481506348e-01 5.960784554481506348e-01 5.960784554481506348e-01 1.000000000000000000e+00 -5.921568870544433594e-01 5.921568870544433594e-01 5.921568870544433594e-01 1.000000000000000000e+00 -5.882353186607360840e-01 5.882353186607360840e-01 5.882353186607360840e-01 1.000000000000000000e+00 -5.843137502670288086e-01 5.843137502670288086e-01 5.843137502670288086e-01 1.000000000000000000e+00 -5.803921818733215332e-01 5.803921818733215332e-01 5.803921818733215332e-01 1.000000000000000000e+00 -5.764706134796142578e-01 5.764706134796142578e-01 5.764706134796142578e-01 1.000000000000000000e+00 -5.725490450859069824e-01 5.725490450859069824e-01 5.725490450859069824e-01 1.000000000000000000e+00 -5.686274766921997070e-01 5.686274766921997070e-01 5.686274766921997070e-01 1.000000000000000000e+00 -5.647059082984924316e-01 5.647059082984924316e-01 5.647059082984924316e-01 1.000000000000000000e+00 -5.607843399047851562e-01 5.607843399047851562e-01 5.607843399047851562e-01 1.000000000000000000e+00 -5.568627715110778809e-01 5.568627715110778809e-01 5.568627715110778809e-01 1.000000000000000000e+00 -5.529412031173706055e-01 5.529412031173706055e-01 5.529412031173706055e-01 1.000000000000000000e+00 -5.490196347236633301e-01 5.490196347236633301e-01 5.490196347236633301e-01 1.000000000000000000e+00 -5.450980663299560547e-01 5.450980663299560547e-01 5.450980663299560547e-01 1.000000000000000000e+00 -5.411764979362487793e-01 5.411764979362487793e-01 5.411764979362487793e-01 1.000000000000000000e+00 -5.372549295425415039e-01 5.372549295425415039e-01 5.372549295425415039e-01 1.000000000000000000e+00 -5.333333611488342285e-01 5.333333611488342285e-01 5.333333611488342285e-01 1.000000000000000000e+00 -5.294117927551269531e-01 5.294117927551269531e-01 5.294117927551269531e-01 1.000000000000000000e+00 -5.254902243614196777e-01 5.254902243614196777e-01 5.254902243614196777e-01 1.000000000000000000e+00 -5.215686559677124023e-01 5.215686559677124023e-01 5.215686559677124023e-01 1.000000000000000000e+00 -5.176470875740051270e-01 5.176470875740051270e-01 5.176470875740051270e-01 1.000000000000000000e+00 -5.137255191802978516e-01 5.137255191802978516e-01 5.137255191802978516e-01 1.000000000000000000e+00 -5.098039507865905762e-01 5.098039507865905762e-01 5.098039507865905762e-01 1.000000000000000000e+00 -5.058823823928833008e-01 5.058823823928833008e-01 5.058823823928833008e-01 1.000000000000000000e+00 -5.019608139991760254e-01 5.019608139991760254e-01 5.019608139991760254e-01 1.000000000000000000e+00 -4.980392158031463623e-01 4.980392158031463623e-01 4.980392158031463623e-01 1.000000000000000000e+00 -4.941176474094390869e-01 4.941176474094390869e-01 4.941176474094390869e-01 1.000000000000000000e+00 -4.901960790157318115e-01 4.901960790157318115e-01 4.901960790157318115e-01 1.000000000000000000e+00 -4.862745106220245361e-01 4.862745106220245361e-01 4.862745106220245361e-01 1.000000000000000000e+00 -4.823529422283172607e-01 4.823529422283172607e-01 4.823529422283172607e-01 1.000000000000000000e+00 -4.784313738346099854e-01 4.784313738346099854e-01 4.784313738346099854e-01 1.000000000000000000e+00 -4.745098054409027100e-01 4.745098054409027100e-01 4.745098054409027100e-01 1.000000000000000000e+00 -4.705882370471954346e-01 4.705882370471954346e-01 4.705882370471954346e-01 1.000000000000000000e+00 -4.666666686534881592e-01 4.666666686534881592e-01 4.666666686534881592e-01 1.000000000000000000e+00 -4.627451002597808838e-01 4.627451002597808838e-01 4.627451002597808838e-01 1.000000000000000000e+00 -4.588235318660736084e-01 4.588235318660736084e-01 4.588235318660736084e-01 1.000000000000000000e+00 -4.549019634723663330e-01 4.549019634723663330e-01 4.549019634723663330e-01 1.000000000000000000e+00 -4.509803950786590576e-01 4.509803950786590576e-01 4.509803950786590576e-01 1.000000000000000000e+00 -4.470588266849517822e-01 4.470588266849517822e-01 4.470588266849517822e-01 1.000000000000000000e+00 -4.431372582912445068e-01 4.431372582912445068e-01 4.431372582912445068e-01 1.000000000000000000e+00 -4.392156898975372314e-01 4.392156898975372314e-01 4.392156898975372314e-01 1.000000000000000000e+00 -4.352941215038299561e-01 4.352941215038299561e-01 4.352941215038299561e-01 1.000000000000000000e+00 -4.313725531101226807e-01 4.313725531101226807e-01 4.313725531101226807e-01 1.000000000000000000e+00 -4.274509847164154053e-01 4.274509847164154053e-01 4.274509847164154053e-01 1.000000000000000000e+00 -4.235294163227081299e-01 4.235294163227081299e-01 4.235294163227081299e-01 1.000000000000000000e+00 -4.196078479290008545e-01 4.196078479290008545e-01 4.196078479290008545e-01 1.000000000000000000e+00 -4.156862795352935791e-01 4.156862795352935791e-01 4.156862795352935791e-01 1.000000000000000000e+00 -4.117647111415863037e-01 4.117647111415863037e-01 4.117647111415863037e-01 1.000000000000000000e+00 -4.078431427478790283e-01 4.078431427478790283e-01 4.078431427478790283e-01 1.000000000000000000e+00 -4.039215743541717529e-01 4.039215743541717529e-01 4.039215743541717529e-01 1.000000000000000000e+00 -4.000000059604644775e-01 4.000000059604644775e-01 4.000000059604644775e-01 1.000000000000000000e+00 -3.960784375667572021e-01 3.960784375667572021e-01 3.960784375667572021e-01 1.000000000000000000e+00 -3.921568691730499268e-01 3.921568691730499268e-01 3.921568691730499268e-01 1.000000000000000000e+00 -3.882353007793426514e-01 3.882353007793426514e-01 3.882353007793426514e-01 1.000000000000000000e+00 -3.843137323856353760e-01 3.843137323856353760e-01 3.843137323856353760e-01 1.000000000000000000e+00 -3.803921639919281006e-01 3.803921639919281006e-01 3.803921639919281006e-01 1.000000000000000000e+00 -3.764705955982208252e-01 3.764705955982208252e-01 3.764705955982208252e-01 1.000000000000000000e+00 -3.725490272045135498e-01 3.725490272045135498e-01 3.725490272045135498e-01 1.000000000000000000e+00 -3.686274588108062744e-01 3.686274588108062744e-01 3.686274588108062744e-01 1.000000000000000000e+00 -3.647058904170989990e-01 3.647058904170989990e-01 3.647058904170989990e-01 1.000000000000000000e+00 -3.607843220233917236e-01 3.607843220233917236e-01 3.607843220233917236e-01 1.000000000000000000e+00 -3.568627536296844482e-01 3.568627536296844482e-01 3.568627536296844482e-01 1.000000000000000000e+00 -3.529411852359771729e-01 3.529411852359771729e-01 3.529411852359771729e-01 1.000000000000000000e+00 -3.490196168422698975e-01 3.490196168422698975e-01 3.490196168422698975e-01 1.000000000000000000e+00 -3.450980484485626221e-01 3.450980484485626221e-01 3.450980484485626221e-01 1.000000000000000000e+00 -3.411764800548553467e-01 3.411764800548553467e-01 3.411764800548553467e-01 1.000000000000000000e+00 -3.372549116611480713e-01 3.372549116611480713e-01 3.372549116611480713e-01 1.000000000000000000e+00 -3.333333432674407959e-01 3.333333432674407959e-01 3.333333432674407959e-01 1.000000000000000000e+00 -3.294117748737335205e-01 3.294117748737335205e-01 3.294117748737335205e-01 1.000000000000000000e+00 -3.254902064800262451e-01 3.254902064800262451e-01 3.254902064800262451e-01 1.000000000000000000e+00 -3.215686380863189697e-01 3.215686380863189697e-01 3.215686380863189697e-01 1.000000000000000000e+00 -3.176470696926116943e-01 3.176470696926116943e-01 3.176470696926116943e-01 1.000000000000000000e+00 -3.137255012989044189e-01 3.137255012989044189e-01 3.137255012989044189e-01 1.000000000000000000e+00 -3.098039329051971436e-01 3.098039329051971436e-01 3.098039329051971436e-01 1.000000000000000000e+00 -3.058823645114898682e-01 3.058823645114898682e-01 3.058823645114898682e-01 1.000000000000000000e+00 -3.019607961177825928e-01 3.019607961177825928e-01 3.019607961177825928e-01 1.000000000000000000e+00 -2.980392277240753174e-01 2.980392277240753174e-01 2.980392277240753174e-01 1.000000000000000000e+00 -2.941176593303680420e-01 2.941176593303680420e-01 2.941176593303680420e-01 1.000000000000000000e+00 -2.901960909366607666e-01 2.901960909366607666e-01 2.901960909366607666e-01 1.000000000000000000e+00 -2.862745225429534912e-01 2.862745225429534912e-01 2.862745225429534912e-01 1.000000000000000000e+00 -2.823529541492462158e-01 2.823529541492462158e-01 2.823529541492462158e-01 1.000000000000000000e+00 -2.784313857555389404e-01 2.784313857555389404e-01 2.784313857555389404e-01 1.000000000000000000e+00 -2.745098173618316650e-01 2.745098173618316650e-01 2.745098173618316650e-01 1.000000000000000000e+00 -2.705882489681243896e-01 2.705882489681243896e-01 2.705882489681243896e-01 1.000000000000000000e+00 -2.666666805744171143e-01 2.666666805744171143e-01 2.666666805744171143e-01 1.000000000000000000e+00 -2.627451121807098389e-01 2.627451121807098389e-01 2.627451121807098389e-01 1.000000000000000000e+00 -2.588235437870025635e-01 2.588235437870025635e-01 2.588235437870025635e-01 1.000000000000000000e+00 -2.549019753932952881e-01 2.549019753932952881e-01 2.549019753932952881e-01 1.000000000000000000e+00 -2.509804069995880127e-01 2.509804069995880127e-01 2.509804069995880127e-01 1.000000000000000000e+00 -2.470588237047195435e-01 2.470588237047195435e-01 2.470588237047195435e-01 1.000000000000000000e+00 -2.431372553110122681e-01 2.431372553110122681e-01 2.431372553110122681e-01 1.000000000000000000e+00 -2.392156869173049927e-01 2.392156869173049927e-01 2.392156869173049927e-01 1.000000000000000000e+00 -2.352941185235977173e-01 2.352941185235977173e-01 2.352941185235977173e-01 1.000000000000000000e+00 -2.313725501298904419e-01 2.313725501298904419e-01 2.313725501298904419e-01 1.000000000000000000e+00 -2.274509817361831665e-01 2.274509817361831665e-01 2.274509817361831665e-01 1.000000000000000000e+00 -2.235294133424758911e-01 2.235294133424758911e-01 2.235294133424758911e-01 1.000000000000000000e+00 -2.196078449487686157e-01 2.196078449487686157e-01 2.196078449487686157e-01 1.000000000000000000e+00 -2.156862765550613403e-01 2.156862765550613403e-01 2.156862765550613403e-01 1.000000000000000000e+00 -2.117647081613540649e-01 2.117647081613540649e-01 2.117647081613540649e-01 1.000000000000000000e+00 -2.078431397676467896e-01 2.078431397676467896e-01 2.078431397676467896e-01 1.000000000000000000e+00 -2.039215713739395142e-01 2.039215713739395142e-01 2.039215713739395142e-01 1.000000000000000000e+00 -2.000000029802322388e-01 2.000000029802322388e-01 2.000000029802322388e-01 1.000000000000000000e+00 -1.960784345865249634e-01 1.960784345865249634e-01 1.960784345865249634e-01 1.000000000000000000e+00 -1.921568661928176880e-01 1.921568661928176880e-01 1.921568661928176880e-01 1.000000000000000000e+00 -1.882352977991104126e-01 1.882352977991104126e-01 1.882352977991104126e-01 1.000000000000000000e+00 -1.843137294054031372e-01 1.843137294054031372e-01 1.843137294054031372e-01 1.000000000000000000e+00 -1.803921610116958618e-01 1.803921610116958618e-01 1.803921610116958618e-01 1.000000000000000000e+00 -1.764705926179885864e-01 1.764705926179885864e-01 1.764705926179885864e-01 1.000000000000000000e+00 -1.725490242242813110e-01 1.725490242242813110e-01 1.725490242242813110e-01 1.000000000000000000e+00 -1.686274558305740356e-01 1.686274558305740356e-01 1.686274558305740356e-01 1.000000000000000000e+00 -1.647058874368667603e-01 1.647058874368667603e-01 1.647058874368667603e-01 1.000000000000000000e+00 -1.607843190431594849e-01 1.607843190431594849e-01 1.607843190431594849e-01 1.000000000000000000e+00 -1.568627506494522095e-01 1.568627506494522095e-01 1.568627506494522095e-01 1.000000000000000000e+00 -1.529411822557449341e-01 1.529411822557449341e-01 1.529411822557449341e-01 1.000000000000000000e+00 -1.490196138620376587e-01 1.490196138620376587e-01 1.490196138620376587e-01 1.000000000000000000e+00 -1.450980454683303833e-01 1.450980454683303833e-01 1.450980454683303833e-01 1.000000000000000000e+00 -1.411764770746231079e-01 1.411764770746231079e-01 1.411764770746231079e-01 1.000000000000000000e+00 -1.372549086809158325e-01 1.372549086809158325e-01 1.372549086809158325e-01 1.000000000000000000e+00 -1.333333402872085571e-01 1.333333402872085571e-01 1.333333402872085571e-01 1.000000000000000000e+00 -1.294117718935012817e-01 1.294117718935012817e-01 1.294117718935012817e-01 1.000000000000000000e+00 -1.254902034997940063e-01 1.254902034997940063e-01 1.254902034997940063e-01 1.000000000000000000e+00 -1.215686276555061340e-01 1.215686276555061340e-01 1.215686276555061340e-01 1.000000000000000000e+00 -1.176470592617988586e-01 1.176470592617988586e-01 1.176470592617988586e-01 1.000000000000000000e+00 -1.137254908680915833e-01 1.137254908680915833e-01 1.137254908680915833e-01 1.000000000000000000e+00 -1.098039224743843079e-01 1.098039224743843079e-01 1.098039224743843079e-01 1.000000000000000000e+00 -1.058823540806770325e-01 1.058823540806770325e-01 1.058823540806770325e-01 1.000000000000000000e+00 -1.019607856869697571e-01 1.019607856869697571e-01 1.019607856869697571e-01 1.000000000000000000e+00 -9.803921729326248169e-02 9.803921729326248169e-02 9.803921729326248169e-02 1.000000000000000000e+00 -9.411764889955520630e-02 9.411764889955520630e-02 9.411764889955520630e-02 1.000000000000000000e+00 -9.019608050584793091e-02 9.019608050584793091e-02 9.019608050584793091e-02 1.000000000000000000e+00 -8.627451211214065552e-02 8.627451211214065552e-02 8.627451211214065552e-02 1.000000000000000000e+00 -8.235294371843338013e-02 8.235294371843338013e-02 8.235294371843338013e-02 1.000000000000000000e+00 -7.843137532472610474e-02 7.843137532472610474e-02 7.843137532472610474e-02 1.000000000000000000e+00 -7.450980693101882935e-02 7.450980693101882935e-02 7.450980693101882935e-02 1.000000000000000000e+00 -7.058823853731155396e-02 7.058823853731155396e-02 7.058823853731155396e-02 1.000000000000000000e+00 -6.666667014360427856e-02 6.666667014360427856e-02 6.666667014360427856e-02 1.000000000000000000e+00 -6.274510174989700317e-02 6.274510174989700317e-02 6.274510174989700317e-02 1.000000000000000000e+00 -5.882352963089942932e-02 5.882352963089942932e-02 5.882352963089942932e-02 1.000000000000000000e+00 -5.490196123719215393e-02 5.490196123719215393e-02 5.490196123719215393e-02 1.000000000000000000e+00 -5.098039284348487854e-02 5.098039284348487854e-02 5.098039284348487854e-02 1.000000000000000000e+00 -4.705882444977760315e-02 4.705882444977760315e-02 4.705882444977760315e-02 1.000000000000000000e+00 -4.313725605607032776e-02 4.313725605607032776e-02 4.313725605607032776e-02 1.000000000000000000e+00 -3.921568766236305237e-02 3.921568766236305237e-02 3.921568766236305237e-02 1.000000000000000000e+00 -3.529411926865577698e-02 3.529411926865577698e-02 3.529411926865577698e-02 1.000000000000000000e+00 -3.137255087494850159e-02 3.137255087494850159e-02 3.137255087494850159e-02 1.000000000000000000e+00 -2.745098061859607697e-02 2.745098061859607697e-02 2.745098061859607697e-02 1.000000000000000000e+00 -2.352941222488880157e-02 2.352941222488880157e-02 2.352941222488880157e-02 1.000000000000000000e+00 -1.960784383118152618e-02 1.960784383118152618e-02 1.960784383118152618e-02 1.000000000000000000e+00 -1.568627543747425079e-02 1.568627543747425079e-02 1.568627543747425079e-02 1.000000000000000000e+00 -1.176470611244440079e-02 1.176470611244440079e-02 1.176470611244440079e-02 1.000000000000000000e+00 -7.843137718737125397e-03 7.843137718737125397e-03 7.843137718737125397e-03 1.000000000000000000e+00 -3.921568859368562698e-03 3.921568859368562698e-03 3.921568859368562698e-03 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/gnuplot b/fastplotlib/utils/colormaps/gnuplot deleted file mode 100644 index 481a27626..000000000 --- a/fastplotlib/utils/colormaps/gnuplot +++ /dev/null @@ -1,256 +0,0 @@ -0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.262242794036865234e-02 6.030862920169965946e-08 2.463744953274726868e-02 1.000000000000000000e+00 -8.856149017810821533e-02 4.824690336135972757e-07 4.925994202494621277e-02 1.000000000000000000e+00 -1.084652319550514221e-01 1.628332938707899302e-06 7.385252416133880615e-02 1.000000000000000000e+00 -1.252448558807373047e-01 3.859752268908778206e-06 9.840027987957000732e-02 1.000000000000000000e+00 -1.400280147790908813e-01 7.538578756793867797e-06 1.228882893919944763e-01 1.000000000000000000e+00 -1.533930003643035889e-01 1.302666350966319442e-05 1.473017036914825439e-01 1.000000000000000000e+00 -1.656833738088607788e-01 2.068586036330088973e-05 1.716256737709045410e-01 1.000000000000000000e+00 -1.771229803562164307e-01 3.087801815127022564e-05 1.958454698324203491e-01 1.000000000000000000e+00 -1.878672838211059570e-01 4.396499207359738648e-05 2.199463546276092529e-01 1.000000000000000000e+00 -1.980295032262802124e-01 6.030863005435094237e-05 2.439137250185012817e-01 1.000000000000000000e+00 -2.076950967311859131e-01 8.027078729355707765e-05 2.677330076694488525e-01 1.000000000000000000e+00 -2.169304639101028442e-01 1.042133080773055553e-04 2.913897335529327393e-01 1.000000000000000000e+00 -2.257883846759796143e-01 1.324980548815801740e-04 3.148695826530456543e-01 1.000000000000000000e+00 -2.343116700649261475e-01 1.654868829064071178e-04 3.381582796573638916e-01 1.000000000000000000e+00 -2.425356209278106689e-01 2.035416255239397287e-04 3.612416684627532959e-01 1.000000000000000000e+00 -2.504897117614746094e-01 2.470241452101618052e-04 3.841057419776916504e-01 1.000000000000000000e+00 -2.581988871097564697e-01 2.962962898891419172e-04 4.067366421222686768e-01 1.000000000000000000e+00 -2.656844556331634521e-01 3.517199365887790918e-04 4.291206002235412598e-01 1.000000000000000000e+00 -2.729648351669311523e-01 4.136568750254809856e-04 4.512440562248229980e-01 1.000000000000000000e+00 -2.800560295581817627e-01 4.824690404348075390e-04 4.730935692787170410e-01 1.000000000000000000e+00 -2.869720160961151123e-01 5.585182225331664085e-04 4.946558475494384766e-01 1.000000000000000000e+00 -2.937252223491668701e-01 6.421662983484566212e-04 5.159178376197814941e-01 1.000000000000000000e+00 -3.003266155719757080e-01 7.337750867009162903e-04 5.368666052818298340e-01 1.000000000000000000e+00 -3.067860007286071777e-01 8.337064646184444427e-04 5.574894547462463379e-01 1.000000000000000000e+00 -3.131121397018432617e-01 9.423223091289401054e-04 5.777738094329833984e-01 1.000000000000000000e+00 -3.193129897117614746e-01 1.059984439052641392e-03 5.977074503898620605e-01 1.000000000000000000e+00 -3.253956735134124756e-01 1.187054789625108242e-03 6.172782182693481445e-01 1.000000000000000000e+00 -3.313667476177215576e-01 1.323895063251256943e-03 6.364742517471313477e-01 1.000000000000000000e+00 -3.372321128845214844e-01 1.470867195166647434e-03 6.552838683128356934e-01 1.000000000000000000e+00 -3.429971635341644287e-01 1.628333004191517830e-03 6.736956238746643066e-01 1.000000000000000000e+00 -3.486669361591339111e-01 1.796654425561428070e-03 6.916984319686889648e-01 1.000000000000000000e+00 -3.542459607124328613e-01 1.976193161681294441e-03 7.092813253402709961e-01 1.000000000000000000e+00 -3.597384691238403320e-01 2.167311264201998711e-03 7.264335751533508301e-01 1.000000000000000000e+00 -3.651483654975891113e-01 2.370370319113135338e-03 7.431448101997375488e-01 1.000000000000000000e+00 -3.704792857170104980e-01 2.585732378065586090e-03 7.594048976898193359e-01 1.000000000000000000e+00 -3.757345676422119141e-01 2.813759492710232735e-03 7.752040028572082520e-01 1.000000000000000000e+00 -3.809173703193664551e-01 3.054813016206026077e-03 7.905324101448059082e-01 1.000000000000000000e+00 -3.860305845737457275e-01 3.309255000203847885e-03 8.053809404373168945e-01 1.000000000000000000e+00 -3.910769522190093994e-01 3.577447496354579926e-03 8.197404742240905762e-01 1.000000000000000000e+00 -3.960590064525604248e-01 3.859752323478460312e-03 8.336023688316345215e-01 1.000000000000000000e+00 -4.009791910648345947e-01 4.156530834734439850e-03 8.469582200050354004e-01 1.000000000000000000e+00 -4.058397114276885986e-01 4.468145780265331268e-03 8.597998619079589844e-01 1.000000000000000000e+00 -4.106427431106567383e-01 4.794958047568798065e-03 8.721194863319396973e-01 1.000000000000000000e+00 -4.153901934623718262e-01 5.137330386787652969e-03 8.839097023010253906e-01 1.000000000000000000e+00 -4.200840294361114502e-01 5.495623685419559479e-03 8.951632976531982422e-01 1.000000000000000000e+00 -4.247259795665740967e-01 5.870200693607330322e-03 9.058734178543090820e-01 1.000000000000000000e+00 -4.293177425861358643e-01 6.261422764509916306e-03 9.160336256027221680e-01 1.000000000000000000e+00 -4.338609278202056885e-01 6.669651716947555542e-03 9.256376624107360840e-01 1.000000000000000000e+00 -4.383569955825805664e-01 7.095249835401773453e-03 9.346797466278076172e-01 1.000000000000000000e+00 -4.428074359893798828e-01 7.538578473031520844e-03 9.431544542312622070e-01 1.000000000000000000e+00 -4.472135901451110840e-01 8.000000379979610443e-03 9.510565400123596191e-01 1.000000000000000000e+00 -4.515767693519592285e-01 8.479875512421131134e-03 9.583812355995178223e-01 1.000000000000000000e+00 -4.558981657028198242e-01 8.978568017482757568e-03 9.651240706443786621e-01 1.000000000000000000e+00 -4.601790010929107666e-01 9.496438317000865936e-03 9.712810516357421875e-01 1.000000000000000000e+00 -4.644203782081604004e-01 1.003384776413440704e-02 9.768483042716979980e-01 1.000000000000000000e+00 -4.686233401298522949e-01 1.059116050601005554e-02 9.818225502967834473e-01 1.000000000000000000e+00 -4.727889597415924072e-01 1.116873603314161301e-02 9.862007498741149902e-01 1.000000000000000000e+00 -4.769182205200195312e-01 1.176693756133317947e-02 9.899802207946777344e-01 1.000000000000000000e+00 -4.810120165348052979e-01 1.238612644374370575e-02 9.931586384773254395e-01 1.000000000000000000e+00 -4.850712418556213379e-01 1.302666403353214264e-02 9.957341551780700684e-01 1.000000000000000000e+00 -4.890968203544616699e-01 1.368891261518001556e-02 9.977051615715026855e-01 1.000000000000000000e+00 -4.930894970893859863e-01 1.437323540449142456e-02 9.990704655647277832e-01 1.000000000000000000e+00 -4.970501363277435303e-01 1.507999189198017120e-02 9.998292326927185059e-01 1.000000000000000000e+00 -5.009794235229492188e-01 1.580954529345035553e-02 9.999810457229614258e-01 1.000000000000000000e+00 -5.048781633377075195e-01 1.656225696206092834e-02 9.995257258415222168e-01 1.000000000000000000e+00 -5.087470412254333496e-01 1.733849011361598969e-02 9.984636306762695312e-01 1.000000000000000000e+00 -5.125866532325744629e-01 1.813860423862934113e-02 9.967952966690063477e-01 1.000000000000000000e+00 -5.163977742195129395e-01 1.896296255290508270e-02 9.945219159126281738e-01 1.000000000000000000e+00 -5.201809406280517578e-01 1.981192827224731445e-02 9.916446805000305176e-01 1.000000000000000000e+00 -5.239368081092834473e-01 2.068585902452468872e-02 9.881654977798461914e-01 1.000000000000000000e+00 -5.276659727096557617e-01 2.158512175083160400e-02 9.840863347053527832e-01 1.000000000000000000e+00 -5.313689112663269043e-01 2.251007594168186188e-02 9.794097542762756348e-01 1.000000000000000000e+00 -5.350462794303894043e-01 2.346108295023441315e-02 9.741386175155639648e-01 1.000000000000000000e+00 -5.386984944343566895e-01 2.443850412964820862e-02 9.682760238647460938e-01 1.000000000000000000e+00 -5.423261523246765137e-01 2.544270269572734833e-02 9.618256688117980957e-01 1.000000000000000000e+00 -5.459296703338623047e-01 2.647404000163078308e-02 9.547913074493408203e-01 1.000000000000000000e+00 -5.495095849037170410e-01 2.753287926316261292e-02 9.471773505210876465e-01 1.000000000000000000e+00 -5.530663132667541504e-01 2.861957997083663940e-02 9.389883875846862793e-01 1.000000000000000000e+00 -5.566003322601318359e-01 2.973450720310211182e-02 9.302293062210083008e-01 1.000000000000000000e+00 -5.601120591163635254e-01 3.087801858782768250e-02 9.209055304527282715e-01 1.000000000000000000e+00 -5.636018514633178711e-01 3.205047920346260071e-02 9.110226631164550781e-01 1.000000000000000000e+00 -5.670701861381530762e-01 3.325224667787551880e-02 9.005867242813110352e-01 1.000000000000000000e+00 -5.705174803733825684e-01 3.448368981480598450e-02 8.896040320396423340e-01 1.000000000000000000e+00 -5.739440321922302246e-01 3.574516624212265015e-02 8.780812621116638184e-01 1.000000000000000000e+00 -5.773502588272094727e-01 3.703703731298446655e-02 8.660253882408142090e-01 1.000000000000000000e+00 -5.807365179061889648e-01 3.835966438055038452e-02 8.534438014030456543e-01 1.000000000000000000e+00 -5.841031074523925781e-01 3.971341252326965332e-02 8.403440713882446289e-01 1.000000000000000000e+00 -5.874504446983337402e-01 4.109864309430122375e-02 8.267341852188110352e-01 1.000000000000000000e+00 -5.907788276672363281e-01 4.251571372151374817e-02 8.126223683357238770e-01 1.000000000000000000e+00 -5.940885543823242188e-01 4.396498948335647583e-02 7.980172038078308105e-01 1.000000000000000000e+00 -5.973799228668212891e-01 4.544683545827865601e-02 7.829276323318481445e-01 1.000000000000000000e+00 -6.006532311439514160e-01 4.696160554885864258e-02 7.673626542091369629e-01 1.000000000000000000e+00 -6.039088368415832520e-01 4.850966855883598328e-02 7.513318657875061035e-01 1.000000000000000000e+00 -6.071469783782958984e-01 5.009138211607933044e-02 7.348449826240539551e-01 1.000000000000000000e+00 -6.103679537773132324e-01 5.170711129903793335e-02 7.179118990898132324e-01 1.000000000000000000e+00 -6.135720014572143555e-01 5.335721373558044434e-02 7.005430459976196289e-01 1.000000000000000000e+00 -6.167594194412231445e-01 5.504205822944641113e-02 6.827488541603088379e-01 1.000000000000000000e+00 -6.199304461479187012e-01 5.676199868321418762e-02 6.645401716232299805e-01 1.000000000000000000e+00 -6.230853199958801270e-01 5.851740390062332153e-02 6.459280848503112793e-01 1.000000000000000000e+00 -6.262242794036865234e-01 6.030862778425216675e-02 6.269237995147705078e-01 1.000000000000000000e+00 -6.293476223945617676e-01 6.213604286313056946e-02 6.075389385223388672e-01 1.000000000000000000e+00 -6.324555277824401855e-01 6.400000303983688354e-02 5.877852439880371094e-01 1.000000000000000000e+00 -6.355482339859008789e-01 6.590086966753005981e-02 5.676746964454650879e-01 1.000000000000000000e+00 -6.386259794235229492e-01 6.783900409936904907e-02 5.472195744514465332e-01 1.000000000000000000e+00 -6.416889429092407227e-01 6.981477886438369751e-02 5.264321565628051758e-01 1.000000000000000000e+00 -6.447373628616333008e-01 7.182854413986206055e-02 5.053251981735229492e-01 1.000000000000000000e+00 -6.477714180946350098e-01 7.388066500425338745e-02 4.839114248752593994e-01 1.000000000000000000e+00 -6.507913470268249512e-01 7.597150653600692749e-02 4.622038900852203369e-01 1.000000000000000000e+00 -6.537973284721374512e-01 7.810142636299133301e-02 4.402157366275787354e-01 1.000000000000000000e+00 -6.567896008491516113e-01 8.027078211307525635e-02 4.179603457450866699e-01 1.000000000000000000e+00 -6.597682237625122070e-01 8.247995376586914062e-02 3.954512178897857666e-01 1.000000000000000000e+00 -6.627334952354431152e-01 8.472928404808044434e-02 3.727020025253295898e-01 1.000000000000000000e+00 -6.656855344772338867e-01 8.701913803815841675e-02 3.497264981269836426e-01 1.000000000000000000e+00 -6.686245799064636230e-01 8.934988826513290405e-02 3.265387117862701416e-01 1.000000000000000000e+00 -6.715507507324218750e-01 9.172188490629196167e-02 3.031526803970336914e-01 1.000000000000000000e+00 -6.744642257690429688e-01 9.413550049066543579e-02 2.795825898647308350e-01 1.000000000000000000e+00 -6.773651242256164551e-01 9.659108519554138184e-02 2.558427751064300537e-01 1.000000000000000000e+00 -6.802536845207214355e-01 9.908901154994964600e-02 2.319476455450057983e-01 1.000000000000000000e+00 -6.831300258636474609e-01 1.016296297311782837e-01 2.079116851091384888e-01 1.000000000000000000e+00 -6.859943270683288574e-01 1.042133122682571411e-01 1.837495118379592896e-01 1.000000000000000000e+00 -6.888467073440551758e-01 1.068404167890548706e-01 1.594757884740829468e-01 1.000000000000000000e+00 -6.916873455047607422e-01 1.095113009214401245e-01 1.351052522659301758e-01 1.000000000000000000e+00 -6.945163607597351074e-01 1.122263371944427490e-01 1.106526851654052734e-01 1.000000000000000000e+00 -6.973338723182678223e-01 1.149858832359313965e-01 8.613293617963790894e-02 1.000000000000000000e+00 -7.001400589942932129e-01 1.177902892231941223e-01 6.156090646982192993e-02 1.000000000000000000e+00 -7.029350399971008301e-01 1.206399351358413696e-01 3.695150092244148254e-02 1.000000000000000000e+00 -7.057189345359802246e-01 1.235351711511611938e-01 1.231965981423854828e-02 1.000000000000000000e+00 -7.084919214248657227e-01 1.264763623476028442e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.112540602684020996e-01 1.294638663530349731e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.140055298805236816e-01 1.324980556964874268e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.167464494705200195e-01 1.355792880058288574e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.194769382476806641e-01 1.387079209089279175e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.221970558166503906e-01 1.418843120336532593e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.249070405960083008e-01 1.451088339090347290e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.276068925857543945e-01 1.483818441629409790e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.302967309951782227e-01 1.517037004232406616e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.329767346382141113e-01 1.550747752189636230e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.356469631195068359e-01 1.584954261779785156e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.383075356483459473e-01 1.619659960269927979e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.409585714340209961e-01 1.654868721961975098e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.436001300811767578e-01 1.690584123134613037e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.462323904037475586e-01 1.726809740066528320e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.488553524017333984e-01 1.763549149036407471e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.514691352844238281e-01 1.800806075334548950e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.540739178657531738e-01 1.838583946228027344e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.566697001457214355e-01 1.876886636018753052e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.592566013336181641e-01 1.915717422962188721e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.618347406387329102e-01 1.955080330371856689e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.644041776657104492e-01 1.994978636503219604e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.669649720191955566e-01 2.035416215658187866e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.695173025131225586e-01 2.076396495103836060e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.720611691474914551e-01 2.117923200130462646e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.745966911315917969e-01 2.160000056028366089e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.771239280700683594e-01 2.202630341053009033e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.796429395675659180e-01 2.245817929506301880e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.821539044380187988e-01 2.289566397666931152e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.846567630767822266e-01 2.333879470825195312e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.871517539024353027e-01 2.378760576248168945e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.896387577056884766e-01 2.424213290214538574e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.921180129051208496e-01 2.470241487026214600e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.945895195007324219e-01 2.516848444938659668e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.970533967018127441e-01 2.564038336277008057e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.995096445083618164e-01 2.611814141273498535e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.019583821296691895e-01 2.660179734230041504e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.043996691703796387e-01 2.709138989448547363e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.068335652351379395e-01 2.758695185184478760e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.092601299285888672e-01 2.808852195739746094e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.116794228553771973e-01 2.859613299369812012e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.140915632247924805e-01 2.910982370376586914e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.164966106414794922e-01 2.962962985038757324e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.188945055007934570e-01 3.015558719635009766e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.212854862213134766e-01 3.068773150444030762e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.236694335937500000e-01 3.122610151767730713e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.260465860366821289e-01 3.177073001861572266e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.284168839454650879e-01 3.232165575027465820e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.307803869247436523e-01 3.287891447544097900e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.331372141838073730e-01 3.344253897666931152e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.354874253273010254e-01 3.401257097721099854e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.378310203552246094e-01 3.458904325962066650e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.401680588722229004e-01 3.517199158668518066e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.424986004829406738e-01 3.576145470142364502e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.448227643966674805e-01 3.635746836662292480e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.471404910087585449e-01 3.696006536483764648e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.494519591331481934e-01 3.756928443908691406e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.517571091651916504e-01 3.818516135215759277e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.540560603141784668e-01 3.880773484706878662e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.563488125801086426e-01 3.943703770637512207e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.586354851722717285e-01 4.007310569286346436e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.609160780906677246e-01 4.071597754955291748e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.631905913352966309e-01 4.136568903923034668e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.654592037200927734e-01 4.202227592468261719e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.677218556404113770e-01 4.268577098846435547e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.699786067008972168e-01 4.335621595382690430e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.722295165061950684e-01 4.403364658355712891e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.744746446609497070e-01 4.471809566020965576e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.767139911651611328e-01 4.540959894657135010e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.789476752281188965e-01 4.610819816589355469e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.811756968498229980e-01 4.681392312049865723e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.833980560302734375e-01 4.752681255340576172e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.856148719787597656e-01 4.824690222740173340e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.878261446952819824e-01 4.897423088550567627e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.900319337844848633e-01 4.970883429050445557e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.922322988510131836e-01 5.045074224472045898e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.944271802902221680e-01 5.120000243186950684e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.966167569160461426e-01 5.195663571357727051e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.988009691238403320e-01 5.272069573402404785e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.009798765182495117e-01 5.349220633506774902e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.031535387039184570e-01 5.427120327949523926e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.053219556808471680e-01 5.505773425102233887e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.074851870536804199e-01 5.585182309150695801e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.096433520317077637e-01 5.665351152420043945e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.117963314056396484e-01 5.746283531188964844e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.139442443847656250e-01 5.827983021736145020e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.160871505737304688e-01 5.910453200340270996e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.182250499725341797e-01 5.993697643280029297e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.203580021858215332e-01 6.077720522880554199e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.224860072135925293e-01 6.162524223327636719e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.246090650558471680e-01 6.248114109039306641e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.267272949218750000e-01 6.334491968154907227e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.288407564163208008e-01 6.421662569046020508e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.309493303298950195e-01 6.509629487991333008e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.330531954765319824e-01 6.598396301269531250e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.351522922515869141e-01 6.687965989112854004e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.372466802597045898e-01 6.778342723846435547e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.393364191055297852e-01 6.869530081748962402e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.414215087890625000e-01 6.961531043052673340e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.435020089149475098e-01 7.054350376129150391e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.455779194831848145e-01 7.147991061210632324e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.476493000984191895e-01 7.242456674575805664e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.497161507606506348e-01 7.337750792503356934e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.517785310745239258e-01 7.433877587318420410e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.538364410400390625e-01 7.530840039253234863e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.558898806571960449e-01 7.628641724586486816e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.579389691352844238e-01 7.727286815643310547e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.599836468696594238e-01 7.826778292655944824e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.620240330696105957e-01 7.927120923995971680e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.640600681304931641e-01 8.028316497802734375e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.660918116569519043e-01 8.130370378494262695e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.681192636489868164e-01 8.233284950256347656e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.701424837112426758e-01 8.337064981460571289e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.721615314483642578e-01 8.441712856292724609e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.741763472557067871e-01 8.547233343124389648e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.761870503425598145e-01 8.653629422187805176e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.781936407089233398e-01 8.760904073715209961e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.801960587501525879e-01 8.869062662124633789e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.821944236755371094e-01 8.978106975555419922e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.841887354850769043e-01 9.088041782379150391e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.861789941787719727e-01 9.198870658874511719e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.881652593612670898e-01 9.310596585273742676e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.901475310325622559e-01 9.423223137855529785e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.921258687973022461e-01 9.536755084991455078e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.941002726554870605e-01 9.651194810867309570e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.960706830024719238e-01 9.766546487808227539e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.980372786521911621e-01 9.882813692092895508e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/gnuplot2 b/fastplotlib/utils/colormaps/gnuplot2 deleted file mode 100644 index 6c9146d47..000000000 --- a/fastplotlib/utils/colormaps/gnuplot2 +++ /dev/null @@ -1,256 +0,0 @@ -0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 1.568627543747425079e-02 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 3.137255087494850159e-02 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 4.705882444977760315e-02 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 6.274510174989700317e-02 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 7.843137532472610474e-02 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 9.411764889955520630e-02 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 1.098039224743843079e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 1.254902034997940063e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 1.411764770746231079e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 1.568627506494522095e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 1.725490242242813110e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 1.882352977991104126e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 2.039215713739395142e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 2.196078449487686157e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 2.352941185235977173e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 2.509804069995880127e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 2.666666805744171143e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 2.823529541492462158e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 2.980392277240753174e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 3.137255012989044189e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 3.294117748737335205e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 3.450980484485626221e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 3.607843220233917236e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 3.764705955982208252e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 3.921568691730499268e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 4.078431427478790283e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 4.235294163227081299e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 4.392156898975372314e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 4.549019634723663330e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 4.705882370471954346e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 4.862745106220245361e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 5.019608139991760254e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 5.176470875740051270e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 5.333333611488342285e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 5.490196347236633301e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 5.647059082984924316e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 5.803921818733215332e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 5.960784554481506348e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 6.117647290229797363e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 6.274510025978088379e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 6.431372761726379395e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 6.588235497474670410e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 6.745098233222961426e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 6.901960968971252441e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 7.058823704719543457e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 7.215686440467834473e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 7.372549176216125488e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 7.529411911964416504e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 7.686274647712707520e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 7.843137383460998535e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 8.000000119209289551e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 8.156862854957580566e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 8.313725590705871582e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 8.470588326454162598e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 8.627451062202453613e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 8.784313797950744629e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 8.941176533699035645e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 9.098039269447326660e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 9.254902005195617676e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 9.411764740943908691e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 9.568627476692199707e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 9.725490212440490723e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 9.882352948188781738e-01 1.000000000000000000e+00 -3.063725540414452553e-03 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -1.531862746924161911e-02 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -2.757352963089942932e-02 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -3.982843086123466492e-02 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -5.208333209156990051e-02 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -6.433823704719543457e-02 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -7.659313827753067017e-02 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -8.884803950786590576e-02 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -1.011029407382011414e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -1.133578419685363770e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -1.256127506494522095e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -1.378676444292068481e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -1.501225531101226807e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -1.623774468898773193e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -1.746323555707931519e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -1.868872493505477905e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -1.991421580314636230e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -2.113970518112182617e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -2.236519604921340942e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -2.359068691730499268e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -2.481617629528045654e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -2.604166567325592041e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -2.726715803146362305e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -2.849264740943908691e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -2.971813678741455078e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -3.094362616539001465e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -3.216911852359771729e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -3.339460790157318115e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -3.462009727954864502e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -3.584558963775634766e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -3.707107901573181152e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -3.829656839370727539e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -3.952205777168273926e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -4.074755012989044189e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -4.197303950786590576e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -4.319852888584136963e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -4.442401826381683350e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -4.564951062202453613e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -4.687500000000000000e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -4.810048937797546387e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -4.932598173618316650e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -5.055146813392639160e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -5.177696347236633301e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -5.300245285034179688e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -5.422794222831726074e-01 7.058823481202125549e-03 9.929412007331848145e-01 1.000000000000000000e+00 -5.545343160629272461e-01 1.490196119993925095e-02 9.850980639457702637e-01 1.000000000000000000e+00 -5.667892098426818848e-01 2.274509891867637634e-02 9.772549271583557129e-01 1.000000000000000000e+00 -5.790441036224365234e-01 3.058823570609092712e-02 9.694117903709411621e-01 1.000000000000000000e+00 -5.912989974021911621e-01 3.843137249350547791e-02 9.615686535835266113e-01 1.000000000000000000e+00 -6.035539507865905762e-01 4.627450928092002869e-02 9.537255167961120605e-01 1.000000000000000000e+00 -6.158088445663452148e-01 5.411764606833457947e-02 9.458823800086975098e-01 1.000000000000000000e+00 -6.280637383460998535e-01 6.196078285574913025e-02 9.380392432212829590e-01 1.000000000000000000e+00 -6.403186321258544922e-01 6.980392336845397949e-02 9.301961064338684082e-01 1.000000000000000000e+00 -6.525735259056091309e-01 7.764706015586853027e-02 9.223529696464538574e-01 1.000000000000000000e+00 -6.648284196853637695e-01 8.549019694328308105e-02 9.145098328590393066e-01 1.000000000000000000e+00 -6.770833134651184082e-01 9.333333373069763184e-02 9.066666960716247559e-01 1.000000000000000000e+00 -6.893382072448730469e-01 1.011764705181121826e-01 8.988234996795654297e-01 1.000000000000000000e+00 -7.015931606292724609e-01 1.090196073055267334e-01 8.909803628921508789e-01 1.000000000000000000e+00 -7.138480544090270996e-01 1.168627440929412842e-01 8.831372261047363281e-01 1.000000000000000000e+00 -7.261029481887817383e-01 1.247058808803558350e-01 8.752940893173217773e-01 1.000000000000000000e+00 -7.383578419685363770e-01 1.325490176677703857e-01 8.674509525299072266e-01 1.000000000000000000e+00 -7.506127357482910156e-01 1.403921544551849365e-01 8.596078157424926758e-01 1.000000000000000000e+00 -7.628676295280456543e-01 1.482352912425994873e-01 8.517646789550781250e-01 1.000000000000000000e+00 -7.751225233078002930e-01 1.560784280300140381e-01 8.439215421676635742e-01 1.000000000000000000e+00 -7.873774766921997070e-01 1.639215648174285889e-01 8.360784053802490234e-01 1.000000000000000000e+00 -7.996323704719543457e-01 1.717647016048431396e-01 8.282352685928344727e-01 1.000000000000000000e+00 -8.118872642517089844e-01 1.796078383922576904e-01 8.203921318054199219e-01 1.000000000000000000e+00 -8.241421580314636230e-01 1.874509751796722412e-01 8.125489950180053711e-01 1.000000000000000000e+00 -8.363970518112182617e-01 1.952941119670867920e-01 8.047058582305908203e-01 1.000000000000000000e+00 -8.486519455909729004e-01 2.031372487545013428e-01 7.968627214431762695e-01 1.000000000000000000e+00 -8.609068393707275391e-01 2.109803855419158936e-01 7.890195846557617188e-01 1.000000000000000000e+00 -8.731617927551269531e-01 2.188235223293304443e-01 7.811764478683471680e-01 1.000000000000000000e+00 -8.854166865348815918e-01 2.266666740179061890e-01 7.733333110809326172e-01 1.000000000000000000e+00 -8.976715803146362305e-01 2.345098108053207397e-01 7.654901742935180664e-01 1.000000000000000000e+00 -9.099264740943908691e-01 2.423529475927352905e-01 7.576470375061035156e-01 1.000000000000000000e+00 -9.221813678741455078e-01 2.501960694789886475e-01 7.498039007186889648e-01 1.000000000000000000e+00 -9.344362616539001465e-01 2.580392062664031982e-01 7.419607639312744141e-01 1.000000000000000000e+00 -9.466911554336547852e-01 2.658823430538177490e-01 7.341176271438598633e-01 1.000000000000000000e+00 -9.589460492134094238e-01 2.737254798412322998e-01 7.262744903564453125e-01 1.000000000000000000e+00 -9.712010025978088379e-01 2.815686166286468506e-01 7.184313535690307617e-01 1.000000000000000000e+00 -9.834558963775634766e-01 2.894117534160614014e-01 7.105882167816162109e-01 1.000000000000000000e+00 -9.957107901573181152e-01 2.972548902034759521e-01 7.027450799942016602e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.050980269908905029e-01 6.949019432067871094e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.129411637783050537e-01 6.870588064193725586e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.207843005657196045e-01 6.792156696319580078e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.286274373531341553e-01 6.713725328445434570e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.364705741405487061e-01 6.635293960571289062e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.443137109279632568e-01 6.556862592697143555e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.521568775177001953e-01 6.478431224822998047e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.600000143051147461e-01 6.399999856948852539e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.678431510925292969e-01 6.321568489074707031e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.756862878799438477e-01 6.243137121200561523e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.835294246673583984e-01 6.164705753326416016e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.913725614547729492e-01 6.086274385452270508e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.992156982421875000e-01 6.007843017578125000e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.070588350296020508e-01 5.929411649703979492e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.149019718170166016e-01 5.850980281829833984e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.227451086044311523e-01 5.772548913955688477e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.305882453918457031e-01 5.694117546081542969e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.384313821792602539e-01 5.615686178207397461e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.462745189666748047e-01 5.537254810333251953e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.541176557540893555e-01 5.458823442459106445e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.619607925415039062e-01 5.380392074584960938e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.698039293289184570e-01 5.301960706710815430e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.776470661163330078e-01 5.223529338836669922e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.854902029037475586e-01 5.145097970962524414e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.933333396911621094e-01 5.066666603088378906e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.011764764785766602e-01 4.988235235214233398e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.090196132659912109e-01 4.909803867340087891e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.168627500534057617e-01 4.831372499465942383e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.247058868408203125e-01 4.752941131591796875e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.325490236282348633e-01 4.674509763717651367e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.403921604156494141e-01 4.596078395843505859e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.482352972030639648e-01 4.517647027969360352e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.560784339904785156e-01 4.439215660095214844e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.639215707778930664e-01 4.360784292221069336e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.717647075653076172e-01 4.282352924346923828e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.796078443527221680e-01 4.203921556472778320e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.874509811401367188e-01 4.125490188598632812e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.952941179275512695e-01 4.047058820724487305e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.031372547149658203e-01 3.968627452850341797e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.109803915023803711e-01 3.890196084976196289e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.188235282897949219e-01 3.811764717102050781e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.266666650772094727e-01 3.733333349227905273e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.345098018646240234e-01 3.654901981353759766e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.423529386520385742e-01 3.576470613479614258e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.501960754394531250e-01 3.498039245605468750e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.580392122268676758e-01 3.419607877731323242e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.658823490142822266e-01 3.341176509857177734e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.737254858016967773e-01 3.262745141983032227e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.815686225891113281e-01 3.184313774108886719e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.894117593765258789e-01 3.105882406234741211e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.972548961639404297e-01 3.027451038360595703e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.050980329513549805e-01 2.949019670486450195e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.129411697387695312e-01 2.870588302612304688e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.207843065261840820e-01 2.792156934738159180e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.286274433135986328e-01 2.713725566864013672e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.364705801010131836e-01 2.635294198989868164e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.443137168884277344e-01 2.556862831115722656e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.521568536758422852e-01 2.478431314229965210e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.599999904632568359e-01 2.399999946355819702e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.678431272506713867e-01 2.321568578481674194e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.756862640380859375e-01 2.243137210607528687e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.835294008255004883e-01 2.164705842733383179e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.913725376129150391e-01 2.086274474859237671e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.992156744003295898e-01 2.007843106985092163e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.070588111877441406e-01 1.929411739110946655e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.149019479751586914e-01 1.850980371236801147e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.227450847625732422e-01 1.772549003362655640e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.305882215499877930e-01 1.694117635488510132e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.384313583374023438e-01 1.615686267614364624e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.462744951248168945e-01 1.537254899740219116e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.541176319122314453e-01 1.458823531866073608e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.619607686996459961e-01 1.380392163991928101e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.698039054870605469e-01 1.301960796117782593e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.776470422744750977e-01 1.223529428243637085e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.854901790618896484e-01 1.145098060369491577e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.933333158493041992e-01 1.066666692495346069e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.011764526367187500e-01 9.882353246212005615e-02 1.000000000000000000e+00 -1.000000000000000000e+00 9.090195894241333008e-01 9.098039567470550537e-02 1.000000000000000000e+00 -1.000000000000000000e+00 9.168627262115478516e-01 8.313725143671035767e-02 1.000000000000000000e+00 -1.000000000000000000e+00 9.247058629989624023e-01 7.529411464929580688e-02 1.000000000000000000e+00 -1.000000000000000000e+00 9.325489997863769531e-01 6.745097786188125610e-02 1.000000000000000000e+00 -1.000000000000000000e+00 9.403921365737915039e-01 5.960784479975700378e-02 1.000000000000000000e+00 -1.000000000000000000e+00 9.482352733612060547e-01 5.176470428705215454e-02 1.000000000000000000e+00 -1.000000000000000000e+00 9.560784101486206055e-01 4.392156749963760376e-02 1.000000000000000000e+00 -1.000000000000000000e+00 9.639215469360351562e-01 3.607843071222305298e-02 1.000000000000000000e+00 -1.000000000000000000e+00 9.717646837234497070e-01 2.823529392480850220e-02 1.000000000000000000e+00 -1.000000000000000000e+00 9.796078205108642578e-01 2.039215713739395142e-02 1.000000000000000000e+00 -1.000000000000000000e+00 9.874509572982788086e-01 1.254901941865682602e-02 1.000000000000000000e+00 -1.000000000000000000e+00 9.952940940856933594e-01 4.705882165580987930e-03 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 1.960784383118152618e-02 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 6.862745434045791626e-02 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 1.176470592617988586e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 1.666666716337203979e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 2.156862765550613403e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 2.647058963775634766e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 3.137255012989044189e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 3.627451062202453613e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 4.117647111415863037e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 4.607843160629272461e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 5.098039507865905762e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 5.588235259056091309e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 6.078431606292724609e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 6.568627357482910156e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 7.058823704719543457e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 7.549019455909729004e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 8.039215803146362305e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 8.529411554336547852e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 9.019607901573181152e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 9.509803652763366699e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/gray b/fastplotlib/utils/colormaps/gray deleted file mode 100644 index 42b875285..000000000 --- a/fastplotlib/utils/colormaps/gray +++ /dev/null @@ -1,256 +0,0 @@ -0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.921568859368562698e-03 3.921568859368562698e-03 3.921568859368562698e-03 1.000000000000000000e+00 -7.843137718737125397e-03 7.843137718737125397e-03 7.843137718737125397e-03 1.000000000000000000e+00 -1.176470611244440079e-02 1.176470611244440079e-02 1.176470611244440079e-02 1.000000000000000000e+00 -1.568627543747425079e-02 1.568627543747425079e-02 1.568627543747425079e-02 1.000000000000000000e+00 -1.960784383118152618e-02 1.960784383118152618e-02 1.960784383118152618e-02 1.000000000000000000e+00 -2.352941222488880157e-02 2.352941222488880157e-02 2.352941222488880157e-02 1.000000000000000000e+00 -2.745098061859607697e-02 2.745098061859607697e-02 2.745098061859607697e-02 1.000000000000000000e+00 -3.137255087494850159e-02 3.137255087494850159e-02 3.137255087494850159e-02 1.000000000000000000e+00 -3.529411926865577698e-02 3.529411926865577698e-02 3.529411926865577698e-02 1.000000000000000000e+00 -3.921568766236305237e-02 3.921568766236305237e-02 3.921568766236305237e-02 1.000000000000000000e+00 -4.313725605607032776e-02 4.313725605607032776e-02 4.313725605607032776e-02 1.000000000000000000e+00 -4.705882444977760315e-02 4.705882444977760315e-02 4.705882444977760315e-02 1.000000000000000000e+00 -5.098039284348487854e-02 5.098039284348487854e-02 5.098039284348487854e-02 1.000000000000000000e+00 -5.490196123719215393e-02 5.490196123719215393e-02 5.490196123719215393e-02 1.000000000000000000e+00 -5.882352963089942932e-02 5.882352963089942932e-02 5.882352963089942932e-02 1.000000000000000000e+00 -6.274510174989700317e-02 6.274510174989700317e-02 6.274510174989700317e-02 1.000000000000000000e+00 -6.666667014360427856e-02 6.666667014360427856e-02 6.666667014360427856e-02 1.000000000000000000e+00 -7.058823853731155396e-02 7.058823853731155396e-02 7.058823853731155396e-02 1.000000000000000000e+00 -7.450980693101882935e-02 7.450980693101882935e-02 7.450980693101882935e-02 1.000000000000000000e+00 -7.843137532472610474e-02 7.843137532472610474e-02 7.843137532472610474e-02 1.000000000000000000e+00 -8.235294371843338013e-02 8.235294371843338013e-02 8.235294371843338013e-02 1.000000000000000000e+00 -8.627451211214065552e-02 8.627451211214065552e-02 8.627451211214065552e-02 1.000000000000000000e+00 -9.019608050584793091e-02 9.019608050584793091e-02 9.019608050584793091e-02 1.000000000000000000e+00 -9.411764889955520630e-02 9.411764889955520630e-02 9.411764889955520630e-02 1.000000000000000000e+00 -9.803921729326248169e-02 9.803921729326248169e-02 9.803921729326248169e-02 1.000000000000000000e+00 -1.019607856869697571e-01 1.019607856869697571e-01 1.019607856869697571e-01 1.000000000000000000e+00 -1.058823540806770325e-01 1.058823540806770325e-01 1.058823540806770325e-01 1.000000000000000000e+00 -1.098039224743843079e-01 1.098039224743843079e-01 1.098039224743843079e-01 1.000000000000000000e+00 -1.137254908680915833e-01 1.137254908680915833e-01 1.137254908680915833e-01 1.000000000000000000e+00 -1.176470592617988586e-01 1.176470592617988586e-01 1.176470592617988586e-01 1.000000000000000000e+00 -1.215686276555061340e-01 1.215686276555061340e-01 1.215686276555061340e-01 1.000000000000000000e+00 -1.254902034997940063e-01 1.254902034997940063e-01 1.254902034997940063e-01 1.000000000000000000e+00 -1.294117718935012817e-01 1.294117718935012817e-01 1.294117718935012817e-01 1.000000000000000000e+00 -1.333333402872085571e-01 1.333333402872085571e-01 1.333333402872085571e-01 1.000000000000000000e+00 -1.372549086809158325e-01 1.372549086809158325e-01 1.372549086809158325e-01 1.000000000000000000e+00 -1.411764770746231079e-01 1.411764770746231079e-01 1.411764770746231079e-01 1.000000000000000000e+00 -1.450980454683303833e-01 1.450980454683303833e-01 1.450980454683303833e-01 1.000000000000000000e+00 -1.490196138620376587e-01 1.490196138620376587e-01 1.490196138620376587e-01 1.000000000000000000e+00 -1.529411822557449341e-01 1.529411822557449341e-01 1.529411822557449341e-01 1.000000000000000000e+00 -1.568627506494522095e-01 1.568627506494522095e-01 1.568627506494522095e-01 1.000000000000000000e+00 -1.607843190431594849e-01 1.607843190431594849e-01 1.607843190431594849e-01 1.000000000000000000e+00 -1.647058874368667603e-01 1.647058874368667603e-01 1.647058874368667603e-01 1.000000000000000000e+00 -1.686274558305740356e-01 1.686274558305740356e-01 1.686274558305740356e-01 1.000000000000000000e+00 -1.725490242242813110e-01 1.725490242242813110e-01 1.725490242242813110e-01 1.000000000000000000e+00 -1.764705926179885864e-01 1.764705926179885864e-01 1.764705926179885864e-01 1.000000000000000000e+00 -1.803921610116958618e-01 1.803921610116958618e-01 1.803921610116958618e-01 1.000000000000000000e+00 -1.843137294054031372e-01 1.843137294054031372e-01 1.843137294054031372e-01 1.000000000000000000e+00 -1.882352977991104126e-01 1.882352977991104126e-01 1.882352977991104126e-01 1.000000000000000000e+00 -1.921568661928176880e-01 1.921568661928176880e-01 1.921568661928176880e-01 1.000000000000000000e+00 -1.960784345865249634e-01 1.960784345865249634e-01 1.960784345865249634e-01 1.000000000000000000e+00 -2.000000029802322388e-01 2.000000029802322388e-01 2.000000029802322388e-01 1.000000000000000000e+00 -2.039215713739395142e-01 2.039215713739395142e-01 2.039215713739395142e-01 1.000000000000000000e+00 -2.078431397676467896e-01 2.078431397676467896e-01 2.078431397676467896e-01 1.000000000000000000e+00 -2.117647081613540649e-01 2.117647081613540649e-01 2.117647081613540649e-01 1.000000000000000000e+00 -2.156862765550613403e-01 2.156862765550613403e-01 2.156862765550613403e-01 1.000000000000000000e+00 -2.196078449487686157e-01 2.196078449487686157e-01 2.196078449487686157e-01 1.000000000000000000e+00 -2.235294133424758911e-01 2.235294133424758911e-01 2.235294133424758911e-01 1.000000000000000000e+00 -2.274509817361831665e-01 2.274509817361831665e-01 2.274509817361831665e-01 1.000000000000000000e+00 -2.313725501298904419e-01 2.313725501298904419e-01 2.313725501298904419e-01 1.000000000000000000e+00 -2.352941185235977173e-01 2.352941185235977173e-01 2.352941185235977173e-01 1.000000000000000000e+00 -2.392156869173049927e-01 2.392156869173049927e-01 2.392156869173049927e-01 1.000000000000000000e+00 -2.431372553110122681e-01 2.431372553110122681e-01 2.431372553110122681e-01 1.000000000000000000e+00 -2.470588237047195435e-01 2.470588237047195435e-01 2.470588237047195435e-01 1.000000000000000000e+00 -2.509804069995880127e-01 2.509804069995880127e-01 2.509804069995880127e-01 1.000000000000000000e+00 -2.549019753932952881e-01 2.549019753932952881e-01 2.549019753932952881e-01 1.000000000000000000e+00 -2.588235437870025635e-01 2.588235437870025635e-01 2.588235437870025635e-01 1.000000000000000000e+00 -2.627451121807098389e-01 2.627451121807098389e-01 2.627451121807098389e-01 1.000000000000000000e+00 -2.666666805744171143e-01 2.666666805744171143e-01 2.666666805744171143e-01 1.000000000000000000e+00 -2.705882489681243896e-01 2.705882489681243896e-01 2.705882489681243896e-01 1.000000000000000000e+00 -2.745098173618316650e-01 2.745098173618316650e-01 2.745098173618316650e-01 1.000000000000000000e+00 -2.784313857555389404e-01 2.784313857555389404e-01 2.784313857555389404e-01 1.000000000000000000e+00 -2.823529541492462158e-01 2.823529541492462158e-01 2.823529541492462158e-01 1.000000000000000000e+00 -2.862745225429534912e-01 2.862745225429534912e-01 2.862745225429534912e-01 1.000000000000000000e+00 -2.901960909366607666e-01 2.901960909366607666e-01 2.901960909366607666e-01 1.000000000000000000e+00 -2.941176593303680420e-01 2.941176593303680420e-01 2.941176593303680420e-01 1.000000000000000000e+00 -2.980392277240753174e-01 2.980392277240753174e-01 2.980392277240753174e-01 1.000000000000000000e+00 -3.019607961177825928e-01 3.019607961177825928e-01 3.019607961177825928e-01 1.000000000000000000e+00 -3.058823645114898682e-01 3.058823645114898682e-01 3.058823645114898682e-01 1.000000000000000000e+00 -3.098039329051971436e-01 3.098039329051971436e-01 3.098039329051971436e-01 1.000000000000000000e+00 -3.137255012989044189e-01 3.137255012989044189e-01 3.137255012989044189e-01 1.000000000000000000e+00 -3.176470696926116943e-01 3.176470696926116943e-01 3.176470696926116943e-01 1.000000000000000000e+00 -3.215686380863189697e-01 3.215686380863189697e-01 3.215686380863189697e-01 1.000000000000000000e+00 -3.254902064800262451e-01 3.254902064800262451e-01 3.254902064800262451e-01 1.000000000000000000e+00 -3.294117748737335205e-01 3.294117748737335205e-01 3.294117748737335205e-01 1.000000000000000000e+00 -3.333333432674407959e-01 3.333333432674407959e-01 3.333333432674407959e-01 1.000000000000000000e+00 -3.372549116611480713e-01 3.372549116611480713e-01 3.372549116611480713e-01 1.000000000000000000e+00 -3.411764800548553467e-01 3.411764800548553467e-01 3.411764800548553467e-01 1.000000000000000000e+00 -3.450980484485626221e-01 3.450980484485626221e-01 3.450980484485626221e-01 1.000000000000000000e+00 -3.490196168422698975e-01 3.490196168422698975e-01 3.490196168422698975e-01 1.000000000000000000e+00 -3.529411852359771729e-01 3.529411852359771729e-01 3.529411852359771729e-01 1.000000000000000000e+00 -3.568627536296844482e-01 3.568627536296844482e-01 3.568627536296844482e-01 1.000000000000000000e+00 -3.607843220233917236e-01 3.607843220233917236e-01 3.607843220233917236e-01 1.000000000000000000e+00 -3.647058904170989990e-01 3.647058904170989990e-01 3.647058904170989990e-01 1.000000000000000000e+00 -3.686274588108062744e-01 3.686274588108062744e-01 3.686274588108062744e-01 1.000000000000000000e+00 -3.725490272045135498e-01 3.725490272045135498e-01 3.725490272045135498e-01 1.000000000000000000e+00 -3.764705955982208252e-01 3.764705955982208252e-01 3.764705955982208252e-01 1.000000000000000000e+00 -3.803921639919281006e-01 3.803921639919281006e-01 3.803921639919281006e-01 1.000000000000000000e+00 -3.843137323856353760e-01 3.843137323856353760e-01 3.843137323856353760e-01 1.000000000000000000e+00 -3.882353007793426514e-01 3.882353007793426514e-01 3.882353007793426514e-01 1.000000000000000000e+00 -3.921568691730499268e-01 3.921568691730499268e-01 3.921568691730499268e-01 1.000000000000000000e+00 -3.960784375667572021e-01 3.960784375667572021e-01 3.960784375667572021e-01 1.000000000000000000e+00 -4.000000059604644775e-01 4.000000059604644775e-01 4.000000059604644775e-01 1.000000000000000000e+00 -4.039215743541717529e-01 4.039215743541717529e-01 4.039215743541717529e-01 1.000000000000000000e+00 -4.078431427478790283e-01 4.078431427478790283e-01 4.078431427478790283e-01 1.000000000000000000e+00 -4.117647111415863037e-01 4.117647111415863037e-01 4.117647111415863037e-01 1.000000000000000000e+00 -4.156862795352935791e-01 4.156862795352935791e-01 4.156862795352935791e-01 1.000000000000000000e+00 -4.196078479290008545e-01 4.196078479290008545e-01 4.196078479290008545e-01 1.000000000000000000e+00 -4.235294163227081299e-01 4.235294163227081299e-01 4.235294163227081299e-01 1.000000000000000000e+00 -4.274509847164154053e-01 4.274509847164154053e-01 4.274509847164154053e-01 1.000000000000000000e+00 -4.313725531101226807e-01 4.313725531101226807e-01 4.313725531101226807e-01 1.000000000000000000e+00 -4.352941215038299561e-01 4.352941215038299561e-01 4.352941215038299561e-01 1.000000000000000000e+00 -4.392156898975372314e-01 4.392156898975372314e-01 4.392156898975372314e-01 1.000000000000000000e+00 -4.431372582912445068e-01 4.431372582912445068e-01 4.431372582912445068e-01 1.000000000000000000e+00 -4.470588266849517822e-01 4.470588266849517822e-01 4.470588266849517822e-01 1.000000000000000000e+00 -4.509803950786590576e-01 4.509803950786590576e-01 4.509803950786590576e-01 1.000000000000000000e+00 -4.549019634723663330e-01 4.549019634723663330e-01 4.549019634723663330e-01 1.000000000000000000e+00 -4.588235318660736084e-01 4.588235318660736084e-01 4.588235318660736084e-01 1.000000000000000000e+00 -4.627451002597808838e-01 4.627451002597808838e-01 4.627451002597808838e-01 1.000000000000000000e+00 -4.666666686534881592e-01 4.666666686534881592e-01 4.666666686534881592e-01 1.000000000000000000e+00 -4.705882370471954346e-01 4.705882370471954346e-01 4.705882370471954346e-01 1.000000000000000000e+00 -4.745098054409027100e-01 4.745098054409027100e-01 4.745098054409027100e-01 1.000000000000000000e+00 -4.784313738346099854e-01 4.784313738346099854e-01 4.784313738346099854e-01 1.000000000000000000e+00 -4.823529422283172607e-01 4.823529422283172607e-01 4.823529422283172607e-01 1.000000000000000000e+00 -4.862745106220245361e-01 4.862745106220245361e-01 4.862745106220245361e-01 1.000000000000000000e+00 -4.901960790157318115e-01 4.901960790157318115e-01 4.901960790157318115e-01 1.000000000000000000e+00 -4.941176474094390869e-01 4.941176474094390869e-01 4.941176474094390869e-01 1.000000000000000000e+00 -4.980392158031463623e-01 4.980392158031463623e-01 4.980392158031463623e-01 1.000000000000000000e+00 -5.019608139991760254e-01 5.019608139991760254e-01 5.019608139991760254e-01 1.000000000000000000e+00 -5.058823823928833008e-01 5.058823823928833008e-01 5.058823823928833008e-01 1.000000000000000000e+00 -5.098039507865905762e-01 5.098039507865905762e-01 5.098039507865905762e-01 1.000000000000000000e+00 -5.137255191802978516e-01 5.137255191802978516e-01 5.137255191802978516e-01 1.000000000000000000e+00 -5.176470875740051270e-01 5.176470875740051270e-01 5.176470875740051270e-01 1.000000000000000000e+00 -5.215686559677124023e-01 5.215686559677124023e-01 5.215686559677124023e-01 1.000000000000000000e+00 -5.254902243614196777e-01 5.254902243614196777e-01 5.254902243614196777e-01 1.000000000000000000e+00 -5.294117927551269531e-01 5.294117927551269531e-01 5.294117927551269531e-01 1.000000000000000000e+00 -5.333333611488342285e-01 5.333333611488342285e-01 5.333333611488342285e-01 1.000000000000000000e+00 -5.372549295425415039e-01 5.372549295425415039e-01 5.372549295425415039e-01 1.000000000000000000e+00 -5.411764979362487793e-01 5.411764979362487793e-01 5.411764979362487793e-01 1.000000000000000000e+00 -5.450980663299560547e-01 5.450980663299560547e-01 5.450980663299560547e-01 1.000000000000000000e+00 -5.490196347236633301e-01 5.490196347236633301e-01 5.490196347236633301e-01 1.000000000000000000e+00 -5.529412031173706055e-01 5.529412031173706055e-01 5.529412031173706055e-01 1.000000000000000000e+00 -5.568627715110778809e-01 5.568627715110778809e-01 5.568627715110778809e-01 1.000000000000000000e+00 -5.607843399047851562e-01 5.607843399047851562e-01 5.607843399047851562e-01 1.000000000000000000e+00 -5.647059082984924316e-01 5.647059082984924316e-01 5.647059082984924316e-01 1.000000000000000000e+00 -5.686274766921997070e-01 5.686274766921997070e-01 5.686274766921997070e-01 1.000000000000000000e+00 -5.725490450859069824e-01 5.725490450859069824e-01 5.725490450859069824e-01 1.000000000000000000e+00 -5.764706134796142578e-01 5.764706134796142578e-01 5.764706134796142578e-01 1.000000000000000000e+00 -5.803921818733215332e-01 5.803921818733215332e-01 5.803921818733215332e-01 1.000000000000000000e+00 -5.843137502670288086e-01 5.843137502670288086e-01 5.843137502670288086e-01 1.000000000000000000e+00 -5.882353186607360840e-01 5.882353186607360840e-01 5.882353186607360840e-01 1.000000000000000000e+00 -5.921568870544433594e-01 5.921568870544433594e-01 5.921568870544433594e-01 1.000000000000000000e+00 -5.960784554481506348e-01 5.960784554481506348e-01 5.960784554481506348e-01 1.000000000000000000e+00 -6.000000238418579102e-01 6.000000238418579102e-01 6.000000238418579102e-01 1.000000000000000000e+00 -6.039215922355651855e-01 6.039215922355651855e-01 6.039215922355651855e-01 1.000000000000000000e+00 -6.078431606292724609e-01 6.078431606292724609e-01 6.078431606292724609e-01 1.000000000000000000e+00 -6.117647290229797363e-01 6.117647290229797363e-01 6.117647290229797363e-01 1.000000000000000000e+00 -6.156862974166870117e-01 6.156862974166870117e-01 6.156862974166870117e-01 1.000000000000000000e+00 -6.196078658103942871e-01 6.196078658103942871e-01 6.196078658103942871e-01 1.000000000000000000e+00 -6.235294342041015625e-01 6.235294342041015625e-01 6.235294342041015625e-01 1.000000000000000000e+00 -6.274510025978088379e-01 6.274510025978088379e-01 6.274510025978088379e-01 1.000000000000000000e+00 -6.313725709915161133e-01 6.313725709915161133e-01 6.313725709915161133e-01 1.000000000000000000e+00 -6.352941393852233887e-01 6.352941393852233887e-01 6.352941393852233887e-01 1.000000000000000000e+00 -6.392157077789306641e-01 6.392157077789306641e-01 6.392157077789306641e-01 1.000000000000000000e+00 -6.431372761726379395e-01 6.431372761726379395e-01 6.431372761726379395e-01 1.000000000000000000e+00 -6.470588445663452148e-01 6.470588445663452148e-01 6.470588445663452148e-01 1.000000000000000000e+00 -6.509804129600524902e-01 6.509804129600524902e-01 6.509804129600524902e-01 1.000000000000000000e+00 -6.549019813537597656e-01 6.549019813537597656e-01 6.549019813537597656e-01 1.000000000000000000e+00 -6.588235497474670410e-01 6.588235497474670410e-01 6.588235497474670410e-01 1.000000000000000000e+00 -6.627451181411743164e-01 6.627451181411743164e-01 6.627451181411743164e-01 1.000000000000000000e+00 -6.666666865348815918e-01 6.666666865348815918e-01 6.666666865348815918e-01 1.000000000000000000e+00 -6.705882549285888672e-01 6.705882549285888672e-01 6.705882549285888672e-01 1.000000000000000000e+00 -6.745098233222961426e-01 6.745098233222961426e-01 6.745098233222961426e-01 1.000000000000000000e+00 -6.784313917160034180e-01 6.784313917160034180e-01 6.784313917160034180e-01 1.000000000000000000e+00 -6.823529601097106934e-01 6.823529601097106934e-01 6.823529601097106934e-01 1.000000000000000000e+00 -6.862745285034179688e-01 6.862745285034179688e-01 6.862745285034179688e-01 1.000000000000000000e+00 -6.901960968971252441e-01 6.901960968971252441e-01 6.901960968971252441e-01 1.000000000000000000e+00 -6.941176652908325195e-01 6.941176652908325195e-01 6.941176652908325195e-01 1.000000000000000000e+00 -6.980392336845397949e-01 6.980392336845397949e-01 6.980392336845397949e-01 1.000000000000000000e+00 -7.019608020782470703e-01 7.019608020782470703e-01 7.019608020782470703e-01 1.000000000000000000e+00 -7.058823704719543457e-01 7.058823704719543457e-01 7.058823704719543457e-01 1.000000000000000000e+00 -7.098039388656616211e-01 7.098039388656616211e-01 7.098039388656616211e-01 1.000000000000000000e+00 -7.137255072593688965e-01 7.137255072593688965e-01 7.137255072593688965e-01 1.000000000000000000e+00 -7.176470756530761719e-01 7.176470756530761719e-01 7.176470756530761719e-01 1.000000000000000000e+00 -7.215686440467834473e-01 7.215686440467834473e-01 7.215686440467834473e-01 1.000000000000000000e+00 -7.254902124404907227e-01 7.254902124404907227e-01 7.254902124404907227e-01 1.000000000000000000e+00 -7.294117808341979980e-01 7.294117808341979980e-01 7.294117808341979980e-01 1.000000000000000000e+00 -7.333333492279052734e-01 7.333333492279052734e-01 7.333333492279052734e-01 1.000000000000000000e+00 -7.372549176216125488e-01 7.372549176216125488e-01 7.372549176216125488e-01 1.000000000000000000e+00 -7.411764860153198242e-01 7.411764860153198242e-01 7.411764860153198242e-01 1.000000000000000000e+00 -7.450980544090270996e-01 7.450980544090270996e-01 7.450980544090270996e-01 1.000000000000000000e+00 -7.490196228027343750e-01 7.490196228027343750e-01 7.490196228027343750e-01 1.000000000000000000e+00 -7.529411911964416504e-01 7.529411911964416504e-01 7.529411911964416504e-01 1.000000000000000000e+00 -7.568627595901489258e-01 7.568627595901489258e-01 7.568627595901489258e-01 1.000000000000000000e+00 -7.607843279838562012e-01 7.607843279838562012e-01 7.607843279838562012e-01 1.000000000000000000e+00 -7.647058963775634766e-01 7.647058963775634766e-01 7.647058963775634766e-01 1.000000000000000000e+00 -7.686274647712707520e-01 7.686274647712707520e-01 7.686274647712707520e-01 1.000000000000000000e+00 -7.725490331649780273e-01 7.725490331649780273e-01 7.725490331649780273e-01 1.000000000000000000e+00 -7.764706015586853027e-01 7.764706015586853027e-01 7.764706015586853027e-01 1.000000000000000000e+00 -7.803921699523925781e-01 7.803921699523925781e-01 7.803921699523925781e-01 1.000000000000000000e+00 -7.843137383460998535e-01 7.843137383460998535e-01 7.843137383460998535e-01 1.000000000000000000e+00 -7.882353067398071289e-01 7.882353067398071289e-01 7.882353067398071289e-01 1.000000000000000000e+00 -7.921568751335144043e-01 7.921568751335144043e-01 7.921568751335144043e-01 1.000000000000000000e+00 -7.960784435272216797e-01 7.960784435272216797e-01 7.960784435272216797e-01 1.000000000000000000e+00 -8.000000119209289551e-01 8.000000119209289551e-01 8.000000119209289551e-01 1.000000000000000000e+00 -8.039215803146362305e-01 8.039215803146362305e-01 8.039215803146362305e-01 1.000000000000000000e+00 -8.078431487083435059e-01 8.078431487083435059e-01 8.078431487083435059e-01 1.000000000000000000e+00 -8.117647171020507812e-01 8.117647171020507812e-01 8.117647171020507812e-01 1.000000000000000000e+00 -8.156862854957580566e-01 8.156862854957580566e-01 8.156862854957580566e-01 1.000000000000000000e+00 -8.196078538894653320e-01 8.196078538894653320e-01 8.196078538894653320e-01 1.000000000000000000e+00 -8.235294222831726074e-01 8.235294222831726074e-01 8.235294222831726074e-01 1.000000000000000000e+00 -8.274509906768798828e-01 8.274509906768798828e-01 8.274509906768798828e-01 1.000000000000000000e+00 -8.313725590705871582e-01 8.313725590705871582e-01 8.313725590705871582e-01 1.000000000000000000e+00 -8.352941274642944336e-01 8.352941274642944336e-01 8.352941274642944336e-01 1.000000000000000000e+00 -8.392156958580017090e-01 8.392156958580017090e-01 8.392156958580017090e-01 1.000000000000000000e+00 -8.431372642517089844e-01 8.431372642517089844e-01 8.431372642517089844e-01 1.000000000000000000e+00 -8.470588326454162598e-01 8.470588326454162598e-01 8.470588326454162598e-01 1.000000000000000000e+00 -8.509804010391235352e-01 8.509804010391235352e-01 8.509804010391235352e-01 1.000000000000000000e+00 -8.549019694328308105e-01 8.549019694328308105e-01 8.549019694328308105e-01 1.000000000000000000e+00 -8.588235378265380859e-01 8.588235378265380859e-01 8.588235378265380859e-01 1.000000000000000000e+00 -8.627451062202453613e-01 8.627451062202453613e-01 8.627451062202453613e-01 1.000000000000000000e+00 -8.666666746139526367e-01 8.666666746139526367e-01 8.666666746139526367e-01 1.000000000000000000e+00 -8.705882430076599121e-01 8.705882430076599121e-01 8.705882430076599121e-01 1.000000000000000000e+00 -8.745098114013671875e-01 8.745098114013671875e-01 8.745098114013671875e-01 1.000000000000000000e+00 -8.784313797950744629e-01 8.784313797950744629e-01 8.784313797950744629e-01 1.000000000000000000e+00 -8.823529481887817383e-01 8.823529481887817383e-01 8.823529481887817383e-01 1.000000000000000000e+00 -8.862745165824890137e-01 8.862745165824890137e-01 8.862745165824890137e-01 1.000000000000000000e+00 -8.901960849761962891e-01 8.901960849761962891e-01 8.901960849761962891e-01 1.000000000000000000e+00 -8.941176533699035645e-01 8.941176533699035645e-01 8.941176533699035645e-01 1.000000000000000000e+00 -8.980392217636108398e-01 8.980392217636108398e-01 8.980392217636108398e-01 1.000000000000000000e+00 -9.019607901573181152e-01 9.019607901573181152e-01 9.019607901573181152e-01 1.000000000000000000e+00 -9.058823585510253906e-01 9.058823585510253906e-01 9.058823585510253906e-01 1.000000000000000000e+00 -9.098039269447326660e-01 9.098039269447326660e-01 9.098039269447326660e-01 1.000000000000000000e+00 -9.137254953384399414e-01 9.137254953384399414e-01 9.137254953384399414e-01 1.000000000000000000e+00 -9.176470637321472168e-01 9.176470637321472168e-01 9.176470637321472168e-01 1.000000000000000000e+00 -9.215686321258544922e-01 9.215686321258544922e-01 9.215686321258544922e-01 1.000000000000000000e+00 -9.254902005195617676e-01 9.254902005195617676e-01 9.254902005195617676e-01 1.000000000000000000e+00 -9.294117689132690430e-01 9.294117689132690430e-01 9.294117689132690430e-01 1.000000000000000000e+00 -9.333333373069763184e-01 9.333333373069763184e-01 9.333333373069763184e-01 1.000000000000000000e+00 -9.372549057006835938e-01 9.372549057006835938e-01 9.372549057006835938e-01 1.000000000000000000e+00 -9.411764740943908691e-01 9.411764740943908691e-01 9.411764740943908691e-01 1.000000000000000000e+00 -9.450980424880981445e-01 9.450980424880981445e-01 9.450980424880981445e-01 1.000000000000000000e+00 -9.490196108818054199e-01 9.490196108818054199e-01 9.490196108818054199e-01 1.000000000000000000e+00 -9.529411792755126953e-01 9.529411792755126953e-01 9.529411792755126953e-01 1.000000000000000000e+00 -9.568627476692199707e-01 9.568627476692199707e-01 9.568627476692199707e-01 1.000000000000000000e+00 -9.607843160629272461e-01 9.607843160629272461e-01 9.607843160629272461e-01 1.000000000000000000e+00 -9.647058844566345215e-01 9.647058844566345215e-01 9.647058844566345215e-01 1.000000000000000000e+00 -9.686274528503417969e-01 9.686274528503417969e-01 9.686274528503417969e-01 1.000000000000000000e+00 -9.725490212440490723e-01 9.725490212440490723e-01 9.725490212440490723e-01 1.000000000000000000e+00 -9.764705896377563477e-01 9.764705896377563477e-01 9.764705896377563477e-01 1.000000000000000000e+00 -9.803921580314636230e-01 9.803921580314636230e-01 9.803921580314636230e-01 1.000000000000000000e+00 -9.843137264251708984e-01 9.843137264251708984e-01 9.843137264251708984e-01 1.000000000000000000e+00 -9.882352948188781738e-01 9.882352948188781738e-01 9.882352948188781738e-01 1.000000000000000000e+00 -9.921568632125854492e-01 9.921568632125854492e-01 9.921568632125854492e-01 1.000000000000000000e+00 -9.960784316062927246e-01 9.960784316062927246e-01 9.960784316062927246e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/hot b/fastplotlib/utils/colormaps/hot deleted file mode 100644 index a85a40219..000000000 --- a/fastplotlib/utils/colormaps/hot +++ /dev/null @@ -1,256 +0,0 @@ -4.160000011324882507e-02 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.189484357833862305e-02 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.218968704342842102e-02 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.248453050851821899e-02 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.277937769889831543e-02 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.307421743869781494e-02 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.033690646290779114e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.136639118194580078e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.239587515592575073e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.342535912990570068e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.445484459400177002e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.548432856798171997e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.651381254196166992e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.754329800605773926e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.857278198003768921e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.960226595401763916e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.063174992799758911e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.166123539209365845e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.269071936607360840e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.372020334005355835e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.474968880414962769e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.577917277812957764e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.680865824222564697e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.783814072608947754e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.886762619018554688e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.989710867404937744e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.092659413814544678e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.195607960224151611e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.298556208610534668e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.401504755020141602e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.504453301429748535e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.607401549816131592e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.710350096225738525e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.813298642635345459e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.916246891021728516e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.019195437431335449e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.122143983840942383e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.225092232227325439e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.328040778636932373e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.430989325046539307e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.533937573432922363e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.636886119842529297e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.739834368228912354e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.842782914638519287e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.945731461048126221e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.048679709434509277e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.151628255844116211e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.254576802253723145e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.357525348663330078e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.460473299026489258e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.563421845436096191e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.666370391845703125e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.769318938255310059e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.872267484664916992e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.975216031074523926e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.078163981437683105e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.181112527847290039e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.284061074256896973e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.387009620666503906e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.489958167076110840e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.592906713485717773e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.695854663848876953e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.798803210258483887e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.901751756668090820e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.004700303077697754e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.107648849487304688e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.210596799850463867e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.313545346260070801e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.416493892669677734e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.519442439079284668e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.622390985488891602e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.725339531898498535e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.828287482261657715e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.931236028671264648e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.034184575080871582e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.137133121490478516e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.240081667900085449e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.343030214309692383e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.445978164672851562e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.548926711082458496e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.651875257492065430e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.754823803901672363e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.857772350311279297e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.960720300674438477e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.063668847084045410e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.166617393493652344e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.269565939903259277e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.372514486312866211e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.475463032722473145e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.578410983085632324e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.681359529495239258e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.784308075904846191e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.887256622314453125e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.990205168724060059e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.314668364822864532e-03 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.960876956582069397e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.990286983549594879e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.019697010517120361e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.049107223749160767e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.078517436981201172e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.107927650213241577e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.137337863445281982e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.166747331619262695e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.019615754485130310e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.122556775808334351e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.225497797131538391e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.328438818454742432e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.431379765272140503e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.534320861101150513e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.637261807918548584e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.740202903747558594e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.843143850564956665e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.946084797382354736e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.049025893211364746e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.151966840028762817e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.254907935857772827e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.357848882675170898e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.460789829492568970e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.563730776309967041e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.666671872138977051e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.769612967967987061e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.872554063796997070e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.975494861602783203e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.078435957431793213e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.181377053260803223e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.284317851066589355e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.387258946895599365e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.490200042724609375e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.593141138553619385e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.696081936359405518e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.799023032188415527e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.901964128017425537e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.004904925823211670e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.107846021652221680e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.210787117481231689e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.313727915287017822e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.416669011116027832e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.519610106945037842e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.622551202774047852e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.725492000579833984e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.828433096408843994e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.931374192237854004e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.034314990043640137e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.137256383895874023e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.240197181701660156e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.343137979507446289e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.446079373359680176e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.549020171165466309e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.651960968971252441e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.754902362823486328e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.857843160629272461e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.960783958435058594e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.063725352287292480e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.166666150093078613e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.269606947898864746e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.372548341751098633e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.475489139556884766e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.578430533409118652e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.681371331214904785e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.784312129020690918e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.887253522872924805e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.990194320678710938e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.093135118484497070e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.196076512336730957e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.299017310142517090e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.401958107948303223e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.504899501800537109e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.607840299606323242e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.710781097412109375e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.813722491264343262e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.916663289070129395e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.019604682922363281e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.122545480728149414e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.225486278533935547e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.328427672386169434e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.431368470191955566e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.534309267997741699e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.637250661849975586e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.740191459655761719e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.843132257461547852e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.946073651313781738e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.049014449119567871e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.151955246925354004e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.254896640777587891e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.357837438583374023e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.460778236389160156e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.563719630241394043e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.666660428047180176e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.769601821899414062e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.872542619705200195e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.975483417510986328e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 1.176371797919273376e-02 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 2.720491029322147369e-02 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 4.264610260725021362e-02 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 5.808729305863380432e-02 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 7.352848351001739502e-02 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 8.896967768669128418e-02 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 1.044108718633651733e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 1.198520585894584656e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 1.352932602167129517e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 1.507344394922256470e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 1.661756336688995361e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 1.816168278455734253e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 1.970580220222473145e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 2.124992161989212036e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 2.279404103755950928e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 2.433815896511077881e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 2.588227987289428711e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 2.742639780044555664e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 2.897051572799682617e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 3.051463663578033447e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 3.205875456333160400e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 3.360287547111511230e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 3.514699339866638184e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 3.669111430644989014e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 3.823523223400115967e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 3.977935016155242920e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 4.132347106933593750e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 4.286758899688720703e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 4.441170990467071533e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 4.595582783222198486e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 4.749994874000549316e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 4.904406666755676270e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 5.058818459510803223e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 5.213230252265930176e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 5.367642641067504883e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 5.522054433822631836e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 5.676466226577758789e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 5.830878019332885742e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 5.985289812088012695e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 6.139702200889587402e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 6.294113993644714355e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 6.448525786399841309e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 6.602937579154968262e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 6.757349967956542969e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 6.911761760711669922e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 7.066173553466796875e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 7.220585346221923828e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 7.374997138977050781e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 7.529409527778625488e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 7.683821320533752441e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 7.838233113288879395e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 7.992644906044006348e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 8.147056698799133301e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 8.301469087600708008e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 8.455880880355834961e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 8.610292673110961914e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 8.764704465866088867e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 8.919116854667663574e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 9.073528647422790527e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 9.227940440177917480e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 9.382352232933044434e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 9.536764025688171387e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 9.691176414489746094e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 9.845588207244873047e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/hsv b/fastplotlib/utils/colormaps/hsv deleted file mode 100644 index 126d76f39..000000000 --- a/fastplotlib/utils/colormaps/hsv +++ /dev/null @@ -1,256 +0,0 @@ -1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.316178753972053528e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.632357507944107056e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.948536634445190430e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.264715015888214111e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.158089414238929749e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.389707326889038086e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.621325165033340454e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.852943003177642822e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.084560841321945190e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.316178828477859497e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.547796666622161865e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.779414653778076172e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.011032342910766602e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.242650330066680908e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.474268317222595215e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.705886006355285645e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.937503993511199951e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.169121682643890381e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.400739669799804688e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.632357656955718994e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.863975346088409424e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.095593333244323730e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.327211022377014160e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.558829307556152344e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.790446996688842773e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.022064685821533203e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.253682971000671387e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.485300660133361816e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.716918349266052246e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.948536634445190430e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.180154323577880859e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.411772012710571289e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.643389701843261719e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.875007987022399902e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.106625676155090332e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.338243365287780762e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.569861650466918945e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.801479339599609375e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.033097028732299805e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.264715313911437988e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.959555864334106445e-01 9.455888867378234863e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.882349967956542969e-01 9.610300660133361816e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.805143475532531738e-01 9.764712452888488770e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.727937579154968262e-01 9.919124245643615723e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.577195644378662109e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.345577359199523926e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.113959670066833496e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.882341980934143066e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.650723695755004883e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.419106006622314453e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.187488317489624023e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.955870032310485840e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.724252343177795410e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.492634654045104980e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.261016964912414551e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.029398679733276367e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.797780990600585938e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.566163301467895508e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.334545016288757324e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.102927327156066895e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.871309638023376465e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.639691352844238281e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.408073663711547852e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.176455974578857422e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.944837987422943115e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.713220000267028809e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.481602013111114502e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.249984323978424072e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.018366336822509766e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.786748349666595459e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.555130660533905029e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.323512673377990723e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.091894984245300293e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.860276997089385986e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.628659009933471680e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.397041171789169312e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.165423333644866943e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.933805495500564575e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.702187657356262207e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.470569670200347900e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.238951832056045532e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.007333919405937195e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.757160812616348267e-02 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.440982058644294739e-02 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.124934434890747070e-02 1.000000000000000000e+00 1.312501353822881356e-06 1.000000000000000000e+00 -2.352874726057052612e-02 1.000000000000000000e+00 1.544250454753637314e-02 1.000000000000000000e+00 -1.580815203487873077e-02 1.000000000000000000e+00 3.088369593024253845e-02 1.000000000000000000e+00 -8.087555877864360809e-03 1.000000000000000000e+00 4.632488638162612915e-02 1.000000000000000000e+00 -3.669599245768040419e-04 1.000000000000000000e+00 6.176608055830001831e-02 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 8.456076681613922119e-02 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 1.077224090695381165e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 1.308840513229370117e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 1.540457010269165039e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 1.772073358297348022e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 2.003689855337142944e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 2.235306203365325928e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 2.466922700405120850e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 2.698538899421691895e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 2.930155396461486816e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 3.161771893501281738e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 3.393388390541076660e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 3.625004589557647705e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 3.856621086597442627e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 4.088237583637237549e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 4.319854080677032471e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 4.551470279693603516e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 4.783086776733398438e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 5.014703273773193359e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 5.246319770812988281e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 5.477936267852783203e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 5.709552764892578125e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 5.941168665885925293e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 6.172785162925720215e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 6.404401659965515137e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 6.636018157005310059e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 6.867634654045104980e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 7.099251151084899902e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 7.330867648124694824e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 7.562484145164489746e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 7.794100046157836914e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 8.025716543197631836e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 8.257333040237426758e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 8.488949537277221680e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 8.720566034317016602e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 8.952182531356811523e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 9.183799028396606445e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 9.415414929389953613e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 9.647031426429748535e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 9.878647923469543457e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.889734983444213867e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 9.658116698265075684e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 9.426499009132385254e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 9.194881319999694824e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 8.963263034820556641e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 8.731645345687866211e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 8.500027656555175781e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 8.268409967422485352e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 8.036791682243347168e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 7.805173993110656738e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 7.573556303977966309e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 7.341938018798828125e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 7.110320329666137695e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 6.878702640533447266e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 6.647084355354309082e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 6.415466666221618652e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 6.183848977088928223e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 5.952230691909790039e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 5.720613002777099609e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 5.488995313644409180e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 5.257377028465270996e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 5.025759339332580566e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 4.794141650199890137e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 4.562523663043975830e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 4.330905675888061523e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 4.099287986755371094e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 3.867669999599456787e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 3.636052012443542480e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 3.404434323310852051e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 3.172816336154937744e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 2.941198348999023438e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 2.709580659866333008e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 2.477962672710418701e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 2.246344834566116333e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 2.014726996421813965e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 1.783109009265899658e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 1.551491171121597290e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 1.319873332977294922e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 1.088255420327186584e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 8.566375821828842163e-02 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 6.250196695327758789e-02 1.000000000000000000e+00 1.000000000000000000e+00 -7.719939574599266052e-03 4.706012085080146790e-02 1.000000000000000000e+00 1.000000000000000000e+00 -1.544053573161363602e-02 3.161893039941787720e-02 1.000000000000000000e+00 1.000000000000000000e+00 -2.316113188862800598e-02 1.617773622274398804e-02 1.000000000000000000e+00 1.000000000000000000e+00 -3.088172711431980133e-02 7.365448400378227234e-04 1.000000000000000000e+00 1.000000000000000000e+00 -5.330697074532508850e-02 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -7.646875828504562378e-02 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -9.963054955005645752e-02 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -1.227923333644866943e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -1.459541171789169312e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -1.691159158945083618e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -1.922776997089385986e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -2.154394835233688354e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -2.386012673377990723e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -2.617630660533905029e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -2.849248349666595459e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -3.080866336822509766e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -3.312484323978424072e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -3.544102013111114502e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -3.775720000267028809e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -4.007337987422943115e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -4.238955676555633545e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -4.470573663711547852e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -4.702191650867462158e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -4.933809340000152588e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -5.165427327156066895e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -5.397045016288757324e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -5.628663301467895508e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -5.860280990600585938e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -6.091898679733276367e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -6.323516964912414551e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -6.555134654045104980e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -6.786752343177795410e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -7.018370032310485840e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -7.249988317489624023e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -7.481606006622314453e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -7.713223695755004883e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -7.944841980934143066e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -8.176459670066833496e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -8.408077359199523926e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -8.639695644378662109e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -8.871313333511352539e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -9.102931022644042969e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -9.334549307823181152e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -9.566166996955871582e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -9.724261760711669922e-01 0.000000000000000000e+00 9.926476478576660156e-01 1.000000000000000000e+00 -9.801467657089233398e-01 0.000000000000000000e+00 9.772064685821533203e-01 1.000000000000000000e+00 -9.878673553466796875e-01 0.000000000000000000e+00 9.617652893066406250e-01 1.000000000000000000e+00 -9.955879449844360352e-01 0.000000000000000000e+00 9.463241100311279297e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 9.275743365287780762e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 9.044125676155090332e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 8.812507987022399902e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 8.580889701843261719e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 8.349272012710571289e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 8.117654323577880859e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 7.886036634445190430e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 7.654418349266052246e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 7.422800660133361816e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 7.191182971000671387e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 6.959564685821533203e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 6.727946996688842773e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 6.496329307556152344e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 6.264711022377014160e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 6.033093333244323730e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 5.801475644111633301e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 5.569857358932495117e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 5.338239669799804688e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 5.106621980667114258e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 4.875003993511199951e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 4.643386006355285645e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 4.411768317222595215e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 4.180150330066680908e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 3.948532342910766602e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 3.716914653778076172e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 3.485296666622161865e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 3.253678679466247559e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 3.022060990333557129e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 2.790443003177642822e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 2.558825016021728516e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 2.327207326889038086e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 2.095589339733123779e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 1.863971501588821411e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 1.632353663444519043e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 1.400735825300216675e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 1.169117912650108337e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 9.375000000000000000e-02 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/inferno b/fastplotlib/utils/colormaps/inferno deleted file mode 100644 index dc84e7b0e..000000000 --- a/fastplotlib/utils/colormaps/inferno +++ /dev/null @@ -1,256 +0,0 @@ -1.461999956518411636e-03 4.659999976865947247e-04 1.386599987745285034e-02 1.000000000000000000e+00 -2.267000032588839531e-03 1.270000007934868336e-03 1.857000030577182770e-02 1.000000000000000000e+00 -3.298999974504113197e-03 2.248999895527958870e-03 2.423899993300437927e-02 1.000000000000000000e+00 -4.546999931335449219e-03 3.391999984160065651e-03 3.090899996459484100e-02 1.000000000000000000e+00 -6.006000097841024399e-03 4.691999871283769608e-03 3.855799883604049683e-02 1.000000000000000000e+00 -7.675999775528907776e-03 6.136000156402587891e-03 4.683599993586540222e-02 1.000000000000000000e+00 -9.561000391840934753e-03 7.712999824434518814e-03 5.514299869537353516e-02 1.000000000000000000e+00 -1.166300009936094284e-02 9.417000226676464081e-03 6.345999985933303833e-02 1.000000000000000000e+00 -1.399500016123056412e-02 1.122500002384185791e-02 7.186199724674224854e-02 1.000000000000000000e+00 -1.656099967658519745e-02 1.313600037246942520e-02 8.028200268745422363e-02 1.000000000000000000e+00 -1.937299966812133789e-02 1.513299997895956039e-02 8.876699954271316528e-02 1.000000000000000000e+00 -2.244699932634830475e-02 1.719900034368038177e-02 9.732700139284133911e-02 1.000000000000000000e+00 -2.579299919307231903e-02 1.933100074529647827e-02 1.059300005435943604e-01 1.000000000000000000e+00 -2.943200059235095978e-02 2.150299958884716034e-02 1.146209985017776489e-01 1.000000000000000000e+00 -3.338500112295150757e-02 2.370199933648109436e-02 1.233970001339912415e-01 1.000000000000000000e+00 -3.766800090670585632e-02 2.592100016772747040e-02 1.322319954633712769e-01 1.000000000000000000e+00 -4.225299879908561707e-02 2.813900075852870941e-02 1.411409974098205566e-01 1.000000000000000000e+00 -4.691499844193458557e-02 3.032400086522102356e-02 1.501639932394027710e-01 1.000000000000000000e+00 -5.164400115609169006e-02 3.247400000691413879e-02 1.592539995908737183e-01 1.000000000000000000e+00 -5.644899979233741760e-02 3.456899896264076233e-02 1.684139966964721680e-01 1.000000000000000000e+00 -6.134000048041343689e-02 3.658999875187873840e-02 1.776420027017593384e-01 1.000000000000000000e+00 -6.633099913597106934e-02 3.850400075316429138e-02 1.869619935750961304e-01 1.000000000000000000e+00 -7.142899930477142334e-02 4.029399901628494263e-02 1.963540017604827881e-01 1.000000000000000000e+00 -7.663699984550476074e-02 4.190500080585479736e-02 2.057989984750747681e-01 1.000000000000000000e+00 -8.196199685335159302e-02 4.332799836993217468e-02 2.152889966964721680e-01 1.000000000000000000e+00 -8.741100132465362549e-02 4.455599933862686157e-02 2.248129993677139282e-01 1.000000000000000000e+00 -9.299000352621078491e-02 4.558299854397773743e-02 2.343579977750778198e-01 1.000000000000000000e+00 -9.870199859142303467e-02 4.640199989080429077e-02 2.439039945602416992e-01 1.000000000000000000e+00 -1.045510023832321167e-01 4.700800031423568726e-02 2.534300088882446289e-01 1.000000000000000000e+00 -1.105360016226768494e-01 4.739899933338165283e-02 2.629120051860809326e-01 1.000000000000000000e+00 -1.166559979319572449e-01 4.757399857044219971e-02 2.723209857940673828e-01 1.000000000000000000e+00 -1.229080036282539368e-01 4.753600060939788818e-02 2.816239893436431885e-01 1.000000000000000000e+00 -1.292849928140640259e-01 4.729299992322921753e-02 2.907879948616027832e-01 1.000000000000000000e+00 -1.357779949903488159e-01 4.685600101947784424e-02 2.997759878635406494e-01 1.000000000000000000e+00 -1.423780024051666260e-01 4.624199867248535156e-02 3.085530102252960205e-01 1.000000000000000000e+00 -1.490730047225952148e-01 4.546799883246421814e-02 3.170849978923797607e-01 1.000000000000000000e+00 -1.558499932289123535e-01 4.455899819731712341e-02 3.253380060195922852e-01 1.000000000000000000e+00 -1.626890003681182861e-01 4.355400055646896362e-02 3.332769870758056641e-01 1.000000000000000000e+00 -1.695750057697296143e-01 4.248899966478347778e-02 3.408739864826202393e-01 1.000000000000000000e+00 -1.764930039644241333e-01 4.140200093388557434e-02 3.481110036373138428e-01 1.000000000000000000e+00 -1.834290027618408203e-01 4.032900184392929077e-02 3.549709916114807129e-01 1.000000000000000000e+00 -1.903669983148574829e-01 3.930899873375892639e-02 3.614470064640045166e-01 1.000000000000000000e+00 -1.972970068454742432e-01 3.840000182390213013e-02 3.675349950790405273e-01 1.000000000000000000e+00 -2.042089998722076416e-01 3.763199970126152039e-02 3.732379972934722900e-01 1.000000000000000000e+00 -2.110950052738189697e-01 3.703000023961067200e-02 3.785629868507385254e-01 1.000000000000000000e+00 -2.179490029811859131e-01 3.661499917507171631e-02 3.835220038890838623e-01 1.000000000000000000e+00 -2.247630059719085693e-01 3.640500083565711975e-02 3.881289958953857422e-01 1.000000000000000000e+00 -2.315379977226257324e-01 3.640500083565711975e-02 3.923999965190887451e-01 1.000000000000000000e+00 -2.382729947566986084e-01 3.662100061774253845e-02 3.963530063629150391e-01 1.000000000000000000e+00 -2.449669986963272095e-01 3.705500066280364990e-02 4.000070095062255859e-01 1.000000000000000000e+00 -2.516199946403503418e-01 3.770500048995018005e-02 4.033780097961425781e-01 1.000000000000000000e+00 -2.582339942455291748e-01 3.857100009918212891e-02 4.064849913120269775e-01 1.000000000000000000e+00 -2.648099958896636963e-01 3.964700177311897278e-02 4.093450009822845459e-01 1.000000000000000000e+00 -2.713469862937927246e-01 4.092200100421905518e-02 4.119760096073150635e-01 1.000000000000000000e+00 -2.778500020503997803e-01 4.235300049185752869e-02 4.143919944763183594e-01 1.000000000000000000e+00 -2.843210101127624512e-01 4.393300041556358337e-02 4.166080057621002197e-01 1.000000000000000000e+00 -2.907629907131195068e-01 4.564400017261505127e-02 4.186370074748992920e-01 1.000000000000000000e+00 -2.971780002117156982e-01 4.746999964118003845e-02 4.204910099506378174e-01 1.000000000000000000e+00 -3.035680055618286133e-01 4.939600080251693726e-02 4.221819937229156494e-01 1.000000000000000000e+00 -3.099350035190582275e-01 5.140699818730354309e-02 4.237209856510162354e-01 1.000000000000000000e+00 -3.162820041179656982e-01 5.349000170826911926e-02 4.251160025596618652e-01 1.000000000000000000e+00 -3.226099908351898193e-01 5.563399940729141235e-02 4.263769984245300293e-01 1.000000000000000000e+00 -3.289209902286529541e-01 5.782699957489967346e-02 4.275110065937042236e-01 1.000000000000000000e+00 -3.352169990539550781e-01 6.005999818444252014e-02 4.285239875316619873e-01 1.000000000000000000e+00 -3.415000140666961670e-01 6.232500076293945312e-02 4.294250011444091797e-01 1.000000000000000000e+00 -3.477709889411926270e-01 6.461600214242935181e-02 4.302169978618621826e-01 1.000000000000000000e+00 -3.540320098400115967e-01 6.692499667406082153e-02 4.309059977531433105e-01 1.000000000000000000e+00 -3.602840006351470947e-01 6.924699991941452026e-02 4.314970076084136963e-01 1.000000000000000000e+00 -3.665289878845214844e-01 7.157900184392929077e-02 4.319939911365509033e-01 1.000000000000000000e+00 -3.727680146694183350e-01 7.391499727964401245e-02 4.323999881744384766e-01 1.000000000000000000e+00 -3.790009915828704834e-01 7.625299692153930664e-02 4.327189922332763672e-01 1.000000000000000000e+00 -3.852280080318450928e-01 7.859099656343460083e-02 4.329549968242645264e-01 1.000000000000000000e+00 -3.914529979228973389e-01 8.092699944972991943e-02 4.331089854240417480e-01 1.000000000000000000e+00 -3.976739943027496338e-01 8.325699716806411743e-02 4.331830143928527832e-01 1.000000000000000000e+00 -4.038940072059631348e-01 8.557999879121780396e-02 4.331789910793304443e-01 1.000000000000000000e+00 -4.101130068302154541e-01 8.789599686861038208e-02 4.330979883670806885e-01 1.000000000000000000e+00 -4.163309931755065918e-01 9.020300209522247314e-02 4.329429864883422852e-01 1.000000000000000000e+00 -4.225490093231201172e-01 9.250099956989288330e-02 4.327139854431152344e-01 1.000000000000000000e+00 -4.287680089473724365e-01 9.478999674320220947e-02 4.324119985103607178e-01 1.000000000000000000e+00 -4.349870085716247559e-01 9.706900268793106079e-02 4.320389926433563232e-01 1.000000000000000000e+00 -4.412069916725158691e-01 9.933800250291824341e-02 4.315940141677856445e-01 1.000000000000000000e+00 -4.474279880523681641e-01 1.015970036387443542e-01 4.310800135135650635e-01 1.000000000000000000e+00 -4.536510109901428223e-01 1.038480028510093689e-01 4.304980039596557617e-01 1.000000000000000000e+00 -4.598749876022338867e-01 1.060890033841133118e-01 4.298459887504577637e-01 1.000000000000000000e+00 -4.661000072956085205e-01 1.083220019936561584e-01 4.291250109672546387e-01 1.000000000000000000e+00 -4.723280072212219238e-01 1.105469986796379089e-01 4.283339977264404297e-01 1.000000000000000000e+00 -4.785580039024353027e-01 1.127640008926391602e-01 4.274750053882598877e-01 1.000000000000000000e+00 -4.847890138626098633e-01 1.149739995598793030e-01 4.265480041503906250e-01 1.000000000000000000e+00 -4.910219907760620117e-01 1.171789988875389099e-01 4.255520105361938477e-01 1.000000000000000000e+00 -4.972569942474365234e-01 1.193789988756179810e-01 4.244880080223083496e-01 1.000000000000000000e+00 -5.034930109977722168e-01 1.215749979019165039e-01 4.233559966087341309e-01 1.000000000000000000e+00 -5.097299814224243164e-01 1.237690001726150513e-01 4.221560060977935791e-01 1.000000000000000000e+00 -5.159670114517211914e-01 1.259600073099136353e-01 4.208869934082031250e-01 1.000000000000000000e+00 -5.222060084342956543e-01 1.281500011682510376e-01 4.195489883422851562e-01 1.000000000000000000e+00 -5.284439921379089355e-01 1.303409934043884277e-01 4.181419909000396729e-01 1.000000000000000000e+00 -5.346829891204833984e-01 1.325339972972869873e-01 4.166670143604278564e-01 1.000000000000000000e+00 -5.409200191497802734e-01 1.347289979457855225e-01 4.151229858398437500e-01 1.000000000000000000e+00 -5.471569895744323730e-01 1.369290053844451904e-01 4.135110080242156982e-01 1.000000000000000000e+00 -5.533919930458068848e-01 1.391340047121047974e-01 4.118289947509765625e-01 1.000000000000000000e+00 -5.596240162849426270e-01 1.413459926843643188e-01 4.100779891014099121e-01 1.000000000000000000e+00 -5.658540129661560059e-01 1.435669958591461182e-01 4.082579910755157471e-01 1.000000000000000000e+00 -5.720810294151306152e-01 1.457969993352890015e-01 4.063690006732940674e-01 1.000000000000000000e+00 -5.783039927482604980e-01 1.480389982461929321e-01 4.044109880924224854e-01 1.000000000000000000e+00 -5.845209956169128418e-01 1.502940058708190918e-01 4.023849964141845703e-01 1.000000000000000000e+00 -5.907340049743652344e-01 1.525630056858062744e-01 4.002900123596191406e-01 1.000000000000000000e+00 -5.969399809837341309e-01 1.548479944467544556e-01 3.981249928474426270e-01 1.000000000000000000e+00 -6.031389832496643066e-01 1.571509987115859985e-01 3.958910107612609863e-01 1.000000000000000000e+00 -6.093299984931945801e-01 1.594740003347396851e-01 3.935889899730682373e-01 1.000000000000000000e+00 -6.155130267143249512e-01 1.618169993162155151e-01 3.912189900875091553e-01 1.000000000000000000e+00 -6.216850280761718750e-01 1.641840040683746338e-01 3.887810111045837402e-01 1.000000000000000000e+00 -6.278470158576965332e-01 1.665749996900558472e-01 3.862760066986083984e-01 1.000000000000000000e+00 -6.339979767799377441e-01 1.689919978380203247e-01 3.837040066719055176e-01 1.000000000000000000e+00 -6.401349902153015137e-01 1.714379936456680298e-01 3.810650110244750977e-01 1.000000000000000000e+00 -6.462600231170654297e-01 1.739140003919601440e-01 3.783589899539947510e-01 1.000000000000000000e+00 -6.523690223693847656e-01 1.764210015535354614e-01 3.755860030651092529e-01 1.000000000000000000e+00 -6.584630012512207031e-01 1.789620071649551392e-01 3.727479875087738037e-01 1.000000000000000000e+00 -6.645399928092956543e-01 1.815389990806579590e-01 3.698459863662719727e-01 1.000000000000000000e+00 -6.705989837646484375e-01 1.841530054807662964e-01 3.668789863586425781e-01 1.000000000000000000e+00 -6.766380071640014648e-01 1.868070065975189209e-01 3.638490140438079834e-01 1.000000000000000000e+00 -6.826559901237487793e-01 1.895010024309158325e-01 3.607569932937622070e-01 1.000000000000000000e+00 -6.886529922485351562e-01 1.922390013933181763e-01 3.576030135154724121e-01 1.000000000000000000e+00 -6.946269869804382324e-01 1.950210034847259521e-01 3.543879985809326172e-01 1.000000000000000000e+00 -7.005760073661804199e-01 1.978510022163391113e-01 3.511129915714263916e-01 1.000000000000000000e+00 -7.064999938011169434e-01 2.007279992103576660e-01 3.477770090103149414e-01 1.000000000000000000e+00 -7.123960256576538086e-01 2.036560028791427612e-01 3.443830013275146484e-01 1.000000000000000000e+00 -7.182639837265014648e-01 2.066359966993331909e-01 3.409309983253479004e-01 1.000000000000000000e+00 -7.241029739379882812e-01 2.096700072288513184e-01 3.374240100383758545e-01 1.000000000000000000e+00 -7.299090027809143066e-01 2.127590030431747437e-01 3.338609933853149414e-01 1.000000000000000000e+00 -7.356830239295959473e-01 2.159059941768646240e-01 3.302449882030487061e-01 1.000000000000000000e+00 -7.414230108261108398e-01 2.191119939088821411e-01 3.265759944915771484e-01 1.000000000000000000e+00 -7.471269965171813965e-01 2.223780006170272827e-01 3.228560090065002441e-01 1.000000000000000000e+00 -7.527940273284912109e-01 2.257059961557388306e-01 3.190850019454956055e-01 1.000000000000000000e+00 -7.584220170974731445e-01 2.290969938039779663e-01 3.152660131454467773e-01 1.000000000000000000e+00 -7.640100121498107910e-01 2.325540035963058472e-01 3.113990128040313721e-01 1.000000000000000000e+00 -7.695559859275817871e-01 2.360769957304000854e-01 3.074850142002105713e-01 1.000000000000000000e+00 -7.750589847564697266e-01 2.396669983863830566e-01 3.035260140895843506e-01 1.000000000000000000e+00 -7.805169820785522461e-01 2.433270066976547241e-01 2.995229959487915039e-01 1.000000000000000000e+00 -7.859290242195129395e-01 2.470560073852539062e-01 2.954770028591156006e-01 1.000000000000000000e+00 -7.912930250167846680e-01 2.508560121059417725e-01 2.913900017738342285e-01 1.000000000000000000e+00 -7.966070175170898438e-01 2.547279894351959229e-01 2.872639894485473633e-01 1.000000000000000000e+00 -8.018710017204284668e-01 2.586739957332611084e-01 2.830989956855773926e-01 1.000000000000000000e+00 -8.070819973945617676e-01 2.626920044422149658e-01 2.788980007171630859e-01 1.000000000000000000e+00 -8.122389912605285645e-01 2.667860090732574463e-01 2.746610045433044434e-01 1.000000000000000000e+00 -8.173410296440124512e-01 2.709540128707885742e-01 2.703900039196014404e-01 1.000000000000000000e+00 -8.223860263824462891e-01 2.751969993114471436e-01 2.660849988460540771e-01 1.000000000000000000e+00 -8.273720145225524902e-01 2.795169949531555176e-01 2.617500126361846924e-01 1.000000000000000000e+00 -8.322989940643310547e-01 2.839129865169525146e-01 2.573829889297485352e-01 1.000000000000000000e+00 -8.371649980545043945e-01 2.883850038051605225e-01 2.529880106449127197e-01 1.000000000000000000e+00 -8.419690132141113281e-01 2.929329872131347656e-01 2.485640048980712891e-01 1.000000000000000000e+00 -8.467090129852294922e-01 2.975589931011199951e-01 2.441129982471466064e-01 1.000000000000000000e+00 -8.513839840888977051e-01 3.022600114345550537e-01 2.396360039710998535e-01 1.000000000000000000e+00 -8.559920191764831543e-01 3.070380091667175293e-01 2.351330071687698364e-01 1.000000000000000000e+00 -8.605329990386962891e-01 3.118920028209686279e-01 2.306060045957565308e-01 1.000000000000000000e+00 -8.650060296058654785e-01 3.168219923973083496e-01 2.260549962520599365e-01 1.000000000000000000e+00 -8.694090247154235840e-01 3.218269944190979004e-01 2.214819937944412231e-01 1.000000000000000000e+00 -8.737409710884094238e-01 3.269059956073760986e-01 2.168859988451004028e-01 1.000000000000000000e+00 -8.780009746551513672e-01 3.320600092411041260e-01 2.122679948806762695e-01 1.000000000000000000e+00 -8.821880221366882324e-01 3.372870087623596191e-01 2.076279968023300171e-01 1.000000000000000000e+00 -8.863019943237304688e-01 3.425860106945037842e-01 2.029680013656616211e-01 1.000000000000000000e+00 -8.903409838676452637e-01 3.479569852352142334e-01 1.982859969139099121e-01 1.000000000000000000e+00 -8.943049907684326172e-01 3.533990085124969482e-01 1.935839951038360596e-01 1.000000000000000000e+00 -8.981919884681701660e-01 3.589110076427459717e-01 1.888599991798400879e-01 1.000000000000000000e+00 -9.020029902458190918e-01 3.644919991493225098e-01 1.841160058975219727e-01 1.000000000000000000e+00 -9.057350158691406250e-01 3.701399862766265869e-01 1.793500036001205444e-01 1.000000000000000000e+00 -9.093899726867675781e-01 3.758560121059417725e-01 1.745630055665969849e-01 1.000000000000000000e+00 -9.129660129547119141e-01 3.816359937191009521e-01 1.697549968957901001e-01 1.000000000000000000e+00 -9.164620041847229004e-01 3.874810039997100830e-01 1.649239957332611084e-01 1.000000000000000000e+00 -9.198790192604064941e-01 3.933889865875244141e-01 1.600700020790100098e-01 1.000000000000000000e+00 -9.232149720191955566e-01 3.993589878082275391e-01 1.551930010318756104e-01 1.000000000000000000e+00 -9.264699816703796387e-01 4.053890109062194824e-01 1.502919942140579224e-01 1.000000000000000000e+00 -9.296439886093139648e-01 4.114789962768554688e-01 1.453669965267181396e-01 1.000000000000000000e+00 -9.327369928359985352e-01 4.176270067691802979e-01 1.404169946908950806e-01 1.000000000000000000e+00 -9.357470273971557617e-01 4.238309860229492188e-01 1.354400068521499634e-01 1.000000000000000000e+00 -9.386749863624572754e-01 4.300909936428070068e-01 1.304379999637603760e-01 1.000000000000000000e+00 -9.415209889411926270e-01 4.364050030708312988e-01 1.254090070724487305e-01 1.000000000000000000e+00 -9.442849755287170410e-01 4.427720010280609131e-01 1.203539967536926270e-01 1.000000000000000000e+00 -9.469649791717529297e-01 4.491910040378570557e-01 1.152720004320144653e-01 1.000000000000000000e+00 -9.495620131492614746e-01 4.556599855422973633e-01 1.101640015840530396e-01 1.000000000000000000e+00 -9.520750045776367188e-01 4.621779918670654297e-01 1.050309985876083374e-01 1.000000000000000000e+00 -9.545059800148010254e-01 4.687440097332000732e-01 9.987399727106094360e-02 1.000000000000000000e+00 -9.568520188331604004e-01 4.753560125827789307e-01 9.469500184059143066e-02 1.000000000000000000e+00 -9.591140151023864746e-01 4.820140004158020020e-01 8.949899673461914062e-02 1.000000000000000000e+00 -9.612929821014404297e-01 4.887160062789916992e-01 8.428899943828582764e-02 1.000000000000000000e+00 -9.633870124816894531e-01 4.954620003700256348e-01 7.907299697399139404e-02 1.000000000000000000e+00 -9.653970003128051758e-01 5.022490024566650391e-01 7.385899871587753296e-02 1.000000000000000000e+00 -9.673219919204711914e-01 5.090780258178710938e-01 6.865900009870529175e-02 1.000000000000000000e+00 -9.691630005836486816e-01 5.159459710121154785e-01 6.348799914121627808e-02 1.000000000000000000e+00 -9.709190130233764648e-01 5.228530168533325195e-01 5.836699903011322021e-02 1.000000000000000000e+00 -9.725900292396545410e-01 5.297979712486267090e-01 5.332399904727935791e-02 1.000000000000000000e+00 -9.741759896278381348e-01 5.367799997329711914e-01 4.839200153946876526e-02 1.000000000000000000e+00 -9.756770133972167969e-01 5.437980294227600098e-01 4.361800104379653931e-02 1.000000000000000000e+00 -9.770920276641845703e-01 5.508499741554260254e-01 3.905000165104866028e-02 1.000000000000000000e+00 -9.784219861030578613e-01 5.579370260238647461e-01 3.493100032210350037e-02 1.000000000000000000e+00 -9.796659946441650391e-01 5.650569796562194824e-01 3.140899911522865295e-02 1.000000000000000000e+00 -9.808239936828613281e-01 5.722090005874633789e-01 2.850800007581710815e-02 1.000000000000000000e+00 -9.818950295448303223e-01 5.793920159339904785e-01 2.624999918043613434e-02 1.000000000000000000e+00 -9.828810095787048340e-01 5.866060256958007812e-01 2.466100081801414490e-02 1.000000000000000000e+00 -9.837790131568908691e-01 5.938490033149719238e-01 2.377000078558921814e-02 1.000000000000000000e+00 -9.845910072326660156e-01 6.011220216751098633e-01 2.360600046813488007e-02 1.000000000000000000e+00 -9.853150248527526855e-01 6.084219813346862793e-01 2.420200034976005554e-02 1.000000000000000000e+00 -9.859520196914672852e-01 6.157500147819519043e-01 2.559199929237365723e-02 1.000000000000000000e+00 -9.865019917488098145e-01 6.231049895286560059e-01 2.781400084495544434e-02 1.000000000000000000e+00 -9.869639873504638672e-01 6.304849982261657715e-01 3.090799972414970398e-02 1.000000000000000000e+00 -9.873369932174682617e-01 6.378899812698364258e-01 3.491599857807159424e-02 1.000000000000000000e+00 -9.876220226287841797e-01 6.453199982643127441e-01 3.988600149750709534e-02 1.000000000000000000e+00 -9.878190159797668457e-01 6.527730226516723633e-01 4.558100178837776184e-02 1.000000000000000000e+00 -9.879260063171386719e-01 6.602500081062316895e-01 5.175000056624412537e-02 1.000000000000000000e+00 -9.879450201988220215e-01 6.677479743957519531e-01 5.832900106906890869e-02 1.000000000000000000e+00 -9.878739714622497559e-01 6.752669811248779297e-01 6.525699794292449951e-02 1.000000000000000000e+00 -9.877139925956726074e-01 6.828070282936096191e-01 7.248900085687637329e-02 1.000000000000000000e+00 -9.874640107154846191e-01 6.903660297393798828e-01 7.998999953269958496e-02 1.000000000000000000e+00 -9.871240258216857910e-01 6.979439854621887207e-01 8.773099631071090698e-02 1.000000000000000000e+00 -9.866939783096313477e-01 7.055400013923645020e-01 9.569399803876876831e-02 1.000000000000000000e+00 -9.861750006675720215e-01 7.131530046463012695e-01 1.038630008697509766e-01 1.000000000000000000e+00 -9.855660200119018555e-01 7.207819819450378418e-01 1.122289970517158508e-01 1.000000000000000000e+00 -9.848650097846984863e-01 7.284269928932189941e-01 1.207849979400634766e-01 1.000000000000000000e+00 -9.840750098228454590e-01 7.360870242118835449e-01 1.295270025730133057e-01 1.000000000000000000e+00 -9.831960201263427734e-01 7.437580227851867676e-01 1.384530067443847656e-01 1.000000000000000000e+00 -9.822279810905456543e-01 7.514420151710510254e-01 1.475650072097778320e-01 1.000000000000000000e+00 -9.811729788780212402e-01 7.591350078582763672e-01 1.568630039691925049e-01 1.000000000000000000e+00 -9.800320267677307129e-01 7.668370008468627930e-01 1.663530021905899048e-01 1.000000000000000000e+00 -9.788060188293457031e-01 7.745450139045715332e-01 1.760369986295700073e-01 1.000000000000000000e+00 -9.774969816207885742e-01 7.822579741477966309e-01 1.859229952096939087e-01 1.000000000000000000e+00 -9.761080145835876465e-01 7.899739742279052734e-01 1.960179954767227173e-01 1.000000000000000000e+00 -9.746379852294921875e-01 7.976920008659362793e-01 2.063319981098175049e-01 1.000000000000000000e+00 -9.730880260467529297e-01 8.054090142250061035e-01 2.168769985437393188e-01 1.000000000000000000e+00 -9.714679718017578125e-01 8.131219744682312012e-01 2.276580035686492920e-01 1.000000000000000000e+00 -9.697830080986022949e-01 8.208249807357788086e-01 2.386859953403472900e-01 1.000000000000000000e+00 -9.680410027503967285e-01 8.285149931907653809e-01 2.499720007181167603e-01 1.000000000000000000e+00 -9.662430286407470703e-01 8.361909985542297363e-01 2.615340054035186768e-01 1.000000000000000000e+00 -9.643939733505249023e-01 8.438479900360107422e-01 2.733910083770751953e-01 1.000000000000000000e+00 -9.625170230865478516e-01 8.514760136604309082e-01 2.855460047721862793e-01 1.000000000000000000e+00 -9.606260061264038086e-01 8.590689897537231445e-01 2.980099916458129883e-01 1.000000000000000000e+00 -9.587200284004211426e-01 8.666239976882934570e-01 3.108200132846832275e-01 1.000000000000000000e+00 -9.568340182304382324e-01 8.741289973258972168e-01 3.239740133285522461e-01 1.000000000000000000e+00 -9.549970030784606934e-01 8.815690279006958008e-01 3.374750018119812012e-01 1.000000000000000000e+00 -9.532150030136108398e-01 8.889420032501220703e-01 3.513689935207366943e-01 1.000000000000000000e+00 -9.515460133552551270e-01 8.962259888648986816e-01 3.656269907951354980e-01 1.000000000000000000e+00 -9.500179886817932129e-01 9.034090042114257812e-01 3.802709877490997314e-01 1.000000000000000000e+00 -9.486830234527587891e-01 9.104729890823364258e-01 3.952890038490295410e-01 1.000000000000000000e+00 -9.475939869880676270e-01 9.173989892005920410e-01 4.106650054454803467e-01 1.000000000000000000e+00 -9.468089938163757324e-01 9.241679906845092773e-01 4.263730049133300781e-01 1.000000000000000000e+00 -9.463919997215270996e-01 9.307609796524047852e-01 4.423669874668121338e-01 1.000000000000000000e+00 -9.464030265808105469e-01 9.371590018272399902e-01 4.585919976234436035e-01 1.000000000000000000e+00 -9.469029903411865234e-01 9.433479905128479004e-01 4.749700129032135010e-01 1.000000000000000000e+00 -9.479370117187500000e-01 9.493179917335510254e-01 4.914259910583496094e-01 1.000000000000000000e+00 -9.495450258255004883e-01 9.550629854202270508e-01 5.078600049018859863e-01 1.000000000000000000e+00 -9.517400264739990234e-01 9.605870246887207031e-01 5.242030024528503418e-01 1.000000000000000000e+00 -9.545289874076843262e-01 9.658960103988647461e-01 5.403609871864318848e-01 1.000000000000000000e+00 -9.578959941864013672e-01 9.710029959678649902e-01 5.562750101089477539e-01 1.000000000000000000e+00 -9.618120193481445312e-01 9.759240150451660156e-01 5.719249844551086426e-01 1.000000000000000000e+00 -9.662489891052246094e-01 9.806780219078063965e-01 5.872060060501098633e-01 1.000000000000000000e+00 -9.711620211601257324e-01 9.852820038795471191e-01 6.021540164947509766e-01 1.000000000000000000e+00 -9.765110015869140625e-01 9.897530078887939453e-01 6.167600154876708984e-01 1.000000000000000000e+00 -9.822570085525512695e-01 9.941089749336242676e-01 6.310170292854309082e-01 1.000000000000000000e+00 -9.883620142936706543e-01 9.983639717102050781e-01 6.449239850044250488e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/jet b/fastplotlib/utils/colormaps/jet deleted file mode 100644 index 9ca10bb71..000000000 --- a/fastplotlib/utils/colormaps/jet +++ /dev/null @@ -1,256 +0,0 @@ -0.000000000000000000e+00 0.000000000000000000e+00 5.000000000000000000e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 5.178253054618835449e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 5.356506109237670898e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 5.534759163856506348e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 5.713012218475341797e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 5.891265869140625000e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 6.069518923759460449e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 6.247771978378295898e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 6.426025032997131348e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 6.604278087615966797e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 6.782531142234802246e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 6.960784196853637695e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 7.139037251472473145e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 7.317290306091308594e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 7.495543956756591797e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 7.673797011375427246e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 7.852050065994262695e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 8.030303120613098145e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 8.208556175231933594e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 8.386809229850769043e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 8.565062284469604492e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 8.743315339088439941e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 8.921568393707275391e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 9.099822044372558594e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 9.278075098991394043e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 9.456328153610229492e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 9.634581208229064941e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 9.812834262847900391e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 9.991087317466735840e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 1.960784429684281349e-03 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 1.764705963432788849e-02 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 3.333333507180213928e-02 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 4.901960864663124084e-02 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 6.470588594675064087e-02 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 8.039215952157974243e-02 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 9.607843309640884399e-02 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 1.117647066712379456e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 1.274509876966476440e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 1.431372612714767456e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 1.588235348463058472e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 1.745098084211349487e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 1.901960819959640503e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 2.058823555707931519e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 2.215686291456222534e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 2.372549027204513550e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 2.529411911964416504e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 2.686274647712707520e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 2.843137383460998535e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 3.000000119209289551e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 3.156862854957580566e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 3.313725590705871582e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 3.470588326454162598e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 3.627451062202453613e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 3.784313797950744629e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 3.941176533699035645e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 4.098039269447326660e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 4.254902005195617676e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 4.411764740943908691e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 4.568627476692199707e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 4.725490212440490723e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 4.882352948188781738e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 5.039215683937072754e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 5.196078419685363770e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 5.352941155433654785e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 5.509803891181945801e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 5.666666626930236816e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 5.823529362678527832e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 5.980392098426818848e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 6.137254834175109863e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 6.294117569923400879e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 6.450980305671691895e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 6.607843041419982910e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 6.764705777168273926e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 6.921568512916564941e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 7.078431248664855957e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 7.235293984413146973e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 7.392156720161437988e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 7.549019455909729004e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 7.705882191658020020e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 7.862744927406311035e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 8.019607663154602051e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 8.176470398902893066e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 8.333333134651184082e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 8.490195870399475098e-01 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 8.647058606147766113e-01 9.962049126625061035e-01 1.000000000000000000e+00 -0.000000000000000000e+00 8.803921341896057129e-01 9.835547208786010742e-01 1.000000000000000000e+00 -0.000000000000000000e+00 8.960784077644348145e-01 9.709044694900512695e-01 1.000000000000000000e+00 -9.487666189670562744e-03 9.117646813392639160e-01 9.582542777061462402e-01 1.000000000000000000e+00 -2.213788777589797974e-02 9.274509549140930176e-01 9.456040263175964355e-01 1.000000000000000000e+00 -3.478810936212539673e-02 9.431372284889221191e-01 9.329538345336914062e-01 1.000000000000000000e+00 -4.743833094835281372e-02 9.588235020637512207e-01 9.203035831451416016e-01 1.000000000000000000e+00 -6.008855253458023071e-02 9.745097756385803223e-01 9.076533913612365723e-01 1.000000000000000000e+00 -7.273877412080764771e-02 9.901960492134094238e-01 8.950031399726867676e-01 1.000000000000000000e+00 -8.538899570703506470e-02 1.000000000000000000e+00 8.823529481887817383e-01 1.000000000000000000e+00 -9.803921729326248169e-02 1.000000000000000000e+00 8.697026968002319336e-01 1.000000000000000000e+00 -1.106894388794898987e-01 1.000000000000000000e+00 8.570525050163269043e-01 1.000000000000000000e+00 -1.233396604657173157e-01 1.000000000000000000e+00 8.444022536277770996e-01 1.000000000000000000e+00 -1.359898746013641357e-01 1.000000000000000000e+00 8.317520618438720703e-01 1.000000000000000000e+00 -1.486400961875915527e-01 1.000000000000000000e+00 8.191018104553222656e-01 1.000000000000000000e+00 -1.612903177738189697e-01 1.000000000000000000e+00 8.064516186714172363e-01 1.000000000000000000e+00 -1.739405393600463867e-01 1.000000000000000000e+00 7.938013672828674316e-01 1.000000000000000000e+00 -1.865907609462738037e-01 1.000000000000000000e+00 7.811511754989624023e-01 1.000000000000000000e+00 -1.992409825325012207e-01 1.000000000000000000e+00 7.685009241104125977e-01 1.000000000000000000e+00 -2.118912041187286377e-01 1.000000000000000000e+00 7.558507323265075684e-01 1.000000000000000000e+00 -2.245414257049560547e-01 1.000000000000000000e+00 7.432004809379577637e-01 1.000000000000000000e+00 -2.371916472911834717e-01 1.000000000000000000e+00 7.305502891540527344e-01 1.000000000000000000e+00 -2.498418688774108887e-01 1.000000000000000000e+00 7.179000377655029297e-01 1.000000000000000000e+00 -2.624920904636383057e-01 1.000000000000000000e+00 7.052498459815979004e-01 1.000000000000000000e+00 -2.751423120498657227e-01 1.000000000000000000e+00 6.925995945930480957e-01 1.000000000000000000e+00 -2.877925336360931396e-01 1.000000000000000000e+00 6.799494028091430664e-01 1.000000000000000000e+00 -3.004427552223205566e-01 1.000000000000000000e+00 6.672991514205932617e-01 1.000000000000000000e+00 -3.130929768085479736e-01 1.000000000000000000e+00 6.546489596366882324e-01 1.000000000000000000e+00 -3.257431983947753906e-01 1.000000000000000000e+00 6.419987082481384277e-01 1.000000000000000000e+00 -3.383934199810028076e-01 1.000000000000000000e+00 6.293485164642333984e-01 1.000000000000000000e+00 -3.510436415672302246e-01 1.000000000000000000e+00 6.166982650756835938e-01 1.000000000000000000e+00 -3.636938631534576416e-01 1.000000000000000000e+00 6.040480732917785645e-01 1.000000000000000000e+00 -3.763440847396850586e-01 1.000000000000000000e+00 5.913978219032287598e-01 1.000000000000000000e+00 -3.889943063259124756e-01 1.000000000000000000e+00 5.787476301193237305e-01 1.000000000000000000e+00 -4.016445279121398926e-01 1.000000000000000000e+00 5.660973787307739258e-01 1.000000000000000000e+00 -4.142947494983673096e-01 1.000000000000000000e+00 5.534471869468688965e-01 1.000000000000000000e+00 -4.269449710845947266e-01 1.000000000000000000e+00 5.407969355583190918e-01 1.000000000000000000e+00 -4.395951926708221436e-01 1.000000000000000000e+00 5.281467437744140625e-01 1.000000000000000000e+00 -4.522454142570495605e-01 1.000000000000000000e+00 5.154964923858642578e-01 1.000000000000000000e+00 -4.648956358432769775e-01 1.000000000000000000e+00 5.028463006019592285e-01 1.000000000000000000e+00 -4.775458574295043945e-01 1.000000000000000000e+00 4.901960790157318115e-01 1.000000000000000000e+00 -4.901960790157318115e-01 1.000000000000000000e+00 4.775458574295043945e-01 1.000000000000000000e+00 -5.028463006019592285e-01 1.000000000000000000e+00 4.648956358432769775e-01 1.000000000000000000e+00 -5.154964923858642578e-01 1.000000000000000000e+00 4.522454142570495605e-01 1.000000000000000000e+00 -5.281467437744140625e-01 1.000000000000000000e+00 4.395951926708221436e-01 1.000000000000000000e+00 -5.407969355583190918e-01 1.000000000000000000e+00 4.269449710845947266e-01 1.000000000000000000e+00 -5.534471869468688965e-01 1.000000000000000000e+00 4.142947494983673096e-01 1.000000000000000000e+00 -5.660973787307739258e-01 1.000000000000000000e+00 4.016445279121398926e-01 1.000000000000000000e+00 -5.787476301193237305e-01 1.000000000000000000e+00 3.889943063259124756e-01 1.000000000000000000e+00 -5.913978219032287598e-01 1.000000000000000000e+00 3.763440847396850586e-01 1.000000000000000000e+00 -6.040480732917785645e-01 1.000000000000000000e+00 3.636938631534576416e-01 1.000000000000000000e+00 -6.166982650756835938e-01 1.000000000000000000e+00 3.510436415672302246e-01 1.000000000000000000e+00 -6.293485164642333984e-01 1.000000000000000000e+00 3.383934199810028076e-01 1.000000000000000000e+00 -6.419987082481384277e-01 1.000000000000000000e+00 3.257431983947753906e-01 1.000000000000000000e+00 -6.546489596366882324e-01 1.000000000000000000e+00 3.130929768085479736e-01 1.000000000000000000e+00 -6.672991514205932617e-01 1.000000000000000000e+00 3.004427552223205566e-01 1.000000000000000000e+00 -6.799494028091430664e-01 1.000000000000000000e+00 2.877925336360931396e-01 1.000000000000000000e+00 -6.925995945930480957e-01 1.000000000000000000e+00 2.751423120498657227e-01 1.000000000000000000e+00 -7.052498459815979004e-01 1.000000000000000000e+00 2.624920904636383057e-01 1.000000000000000000e+00 -7.179000377655029297e-01 1.000000000000000000e+00 2.498418688774108887e-01 1.000000000000000000e+00 -7.305502891540527344e-01 1.000000000000000000e+00 2.371916472911834717e-01 1.000000000000000000e+00 -7.432004809379577637e-01 1.000000000000000000e+00 2.245414257049560547e-01 1.000000000000000000e+00 -7.558507323265075684e-01 1.000000000000000000e+00 2.118912041187286377e-01 1.000000000000000000e+00 -7.685009241104125977e-01 1.000000000000000000e+00 1.992409825325012207e-01 1.000000000000000000e+00 -7.811511754989624023e-01 1.000000000000000000e+00 1.865907609462738037e-01 1.000000000000000000e+00 -7.938013672828674316e-01 1.000000000000000000e+00 1.739405393600463867e-01 1.000000000000000000e+00 -8.064516186714172363e-01 1.000000000000000000e+00 1.612903177738189697e-01 1.000000000000000000e+00 -8.191018104553222656e-01 1.000000000000000000e+00 1.486400961875915527e-01 1.000000000000000000e+00 -8.317520618438720703e-01 1.000000000000000000e+00 1.359898746013641357e-01 1.000000000000000000e+00 -8.444022536277770996e-01 1.000000000000000000e+00 1.233396604657173157e-01 1.000000000000000000e+00 -8.570525050163269043e-01 1.000000000000000000e+00 1.106894388794898987e-01 1.000000000000000000e+00 -8.697026968002319336e-01 1.000000000000000000e+00 9.803921729326248169e-02 1.000000000000000000e+00 -8.823529481887817383e-01 1.000000000000000000e+00 8.538899570703506470e-02 1.000000000000000000e+00 -8.950031399726867676e-01 1.000000000000000000e+00 7.273877412080764771e-02 1.000000000000000000e+00 -9.076533913612365723e-01 1.000000000000000000e+00 6.008855253458023071e-02 1.000000000000000000e+00 -9.203035831451416016e-01 1.000000000000000000e+00 4.743833094835281372e-02 1.000000000000000000e+00 -9.329538345336914062e-01 1.000000000000000000e+00 3.478810936212539673e-02 1.000000000000000000e+00 -9.456040263175964355e-01 9.883805513381958008e-01 2.213788777589797974e-02 1.000000000000000000e+00 -9.582542777061462402e-01 9.738562107086181641e-01 9.487666189670562744e-03 1.000000000000000000e+00 -9.709044694900512695e-01 9.593318700790405273e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.835547208786010742e-01 9.448075294494628906e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.962049126625061035e-01 9.302832484245300293e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.157589077949523926e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.012345671653747559e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.867102265357971191e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.721858859062194824e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.576616048812866211e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.431372642517089844e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.286129236221313477e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.140885829925537109e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.995642423629760742e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.850399613380432129e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.705156207084655762e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.559912800788879395e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.414669394493103027e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.269426584243774414e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.124183177947998047e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.978939771652221680e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.833696365356445312e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.688452959060668945e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.543210148811340332e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.397966742515563965e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.252723336219787598e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.107479929924011230e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.962236523628234863e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.816993713378906250e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.671750307083129883e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.526506900787353516e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.381263494491577148e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.236020088195800781e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.090777277946472168e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.945533871650695801e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.800290465354919434e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.655047059059143066e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.509803950786590576e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.364560544490814209e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.219317436218261719e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.074074029922485352e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.928830921649932861e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.783587515354156494e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.638344109058380127e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.493101000785827637e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.347857594490051270e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.202614486217498779e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.057371079921722412e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.912127673625946045e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.766884565353393555e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.621641159057617188e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.476397901773452759e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.331154644489288330e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.185911387205123901e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.040668129920959473e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.895424872636795044e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.750181615352630615e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.604938209056854248e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.459694951772689819e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.314451694488525391e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.169208437204360962e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.023965105414390564e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.787218481302261353e-02 0.000000000000000000e+00 1.000000000000000000e+00 -9.991087317466735840e-01 7.334785908460617065e-02 0.000000000000000000e+00 1.000000000000000000e+00 -9.812834262847900391e-01 5.882352963089942932e-02 0.000000000000000000e+00 1.000000000000000000e+00 -9.634581208229064941e-01 4.429920017719268799e-02 0.000000000000000000e+00 1.000000000000000000e+00 -9.456328153610229492e-01 2.977487258613109589e-02 0.000000000000000000e+00 1.000000000000000000e+00 -9.278075098991394043e-01 1.525054499506950378e-02 0.000000000000000000e+00 1.000000000000000000e+00 -9.099822044372558594e-01 7.262164144776761532e-04 0.000000000000000000e+00 1.000000000000000000e+00 -8.921568393707275391e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.743315339088439941e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.565062284469604492e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.386809229850769043e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.208556175231933594e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.030303120613098145e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.852050065994262695e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.673797011375427246e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.495543956756591797e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.317290306091308594e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.139037251472473145e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.960784196853637695e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.782531142234802246e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.604278087615966797e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.426025032997131348e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.247771978378295898e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.069518923759460449e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.891265869140625000e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.713012218475341797e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.534759163856506348e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.356506109237670898e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.178253054618835449e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.000000000000000000e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/magma b/fastplotlib/utils/colormaps/magma deleted file mode 100644 index 674bb9963..000000000 --- a/fastplotlib/utils/colormaps/magma +++ /dev/null @@ -1,256 +0,0 @@ -1.461999956518411636e-03 4.659999976865947247e-04 1.386599987745285034e-02 1.000000000000000000e+00 -2.257999964058399200e-03 1.294999965466558933e-03 1.833100058138370514e-02 1.000000000000000000e+00 -3.279000055044889450e-03 2.305000089108943939e-03 2.370800077915191650e-02 1.000000000000000000e+00 -4.511999897658824921e-03 3.490000031888484955e-03 2.996500022709369659e-02 1.000000000000000000e+00 -5.950000137090682983e-03 4.842999856919050217e-03 3.712999820709228516e-02 1.000000000000000000e+00 -7.588000036776065826e-03 6.355999968945980072e-03 4.497300088405609131e-02 1.000000000000000000e+00 -9.425999596714973450e-03 8.022000081837177277e-03 5.284399911761283875e-02 1.000000000000000000e+00 -1.146499998867511749e-02 9.828000329434871674e-03 6.075000017881393433e-02 1.000000000000000000e+00 -1.370800007134675980e-02 1.177099999040365219e-02 6.866700202226638794e-02 1.000000000000000000e+00 -1.615599915385246277e-02 1.384000014513731003e-02 7.660300284624099731e-02 1.000000000000000000e+00 -1.881499961018562317e-02 1.602599956095218658e-02 8.458399772644042969e-02 1.000000000000000000e+00 -2.169200032949447632e-02 1.831999979913234711e-02 9.261000156402587891e-02 1.000000000000000000e+00 -2.479200065135955811e-02 2.071500010788440704e-02 1.006760001182556152e-01 1.000000000000000000e+00 -2.812300063669681549e-02 2.320099994540214539e-02 1.087870001792907715e-01 1.000000000000000000e+00 -3.169599920511245728e-02 2.576499991118907928e-02 1.169650033116340637e-01 1.000000000000000000e+00 -3.551999852061271667e-02 2.839699946343898773e-02 1.252090036869049072e-01 1.000000000000000000e+00 -3.960800170898437500e-02 3.109000064432621002e-02 1.335150003433227539e-01 1.000000000000000000e+00 -4.382999986410140991e-02 3.382999822497367859e-02 1.418859958648681641e-01 1.000000000000000000e+00 -4.806200042366981506e-02 3.660700097680091858e-02 1.503269970417022705e-01 1.000000000000000000e+00 -5.231999978423118591e-02 3.940699994564056396e-02 1.588409990072250366e-01 1.000000000000000000e+00 -5.661499872803688049e-02 4.216000065207481384e-02 1.674460023641586304e-01 1.000000000000000000e+00 -6.094900146126747131e-02 4.479400068521499634e-02 1.761289983987808228e-01 1.000000000000000000e+00 -6.532999873161315918e-02 4.731800034642219543e-02 1.848919987678527832e-01 1.000000000000000000e+00 -6.976400315761566162e-02 4.972599819302558899e-02 1.937350034713745117e-01 1.000000000000000000e+00 -7.425700128078460693e-02 5.201699957251548767e-02 2.026599943637847900e-01 1.000000000000000000e+00 -7.881499826908111572e-02 5.418400093913078308e-02 2.116670012474060059e-01 1.000000000000000000e+00 -8.344600349664688110e-02 5.622500181198120117e-02 2.207549959421157837e-01 1.000000000000000000e+00 -8.815500140190124512e-02 5.813299864530563354e-02 2.299219965934753418e-01 1.000000000000000000e+00 -9.294900298118591309e-02 5.990400165319442749e-02 2.391639947891235352e-01 1.000000000000000000e+00 -9.783300012350082397e-02 6.153099983930587769e-02 2.484769970178604126e-01 1.000000000000000000e+00 -1.028150022029876709e-01 6.300999969244003296e-02 2.578540146350860596e-01 1.000000000000000000e+00 -1.078990027308464050e-01 6.433500349521636963e-02 2.672890126705169678e-01 1.000000000000000000e+00 -1.130940020084381104e-01 6.549199670553207397e-02 2.767840027809143066e-01 1.000000000000000000e+00 -1.184049993753433228e-01 6.647899746894836426e-02 2.863210141658782959e-01 1.000000000000000000e+00 -1.238330006599426270e-01 6.729499995708465576e-02 2.958790063858032227e-01 1.000000000000000000e+00 -1.293800026178359985e-01 6.793499737977981567e-02 3.054429888725280762e-01 1.000000000000000000e+00 -1.350529938936233521e-01 6.839100271463394165e-02 3.149999976158142090e-01 1.000000000000000000e+00 -1.408579945564270020e-01 6.865400075912475586e-02 3.245379924774169922e-01 1.000000000000000000e+00 -1.467850059270858765e-01 6.873799860477447510e-02 3.340109884738922119e-01 1.000000000000000000e+00 -1.528390049934387207e-01 6.863699853420257568e-02 3.434039950370788574e-01 1.000000000000000000e+00 -1.590179949998855591e-01 6.835400313138961792e-02 3.526880145072937012e-01 1.000000000000000000e+00 -1.653079986572265625e-01 6.791099905967712402e-02 3.618159890174865723e-01 1.000000000000000000e+00 -1.717129945755004883e-01 6.730499863624572754e-02 3.707709908485412598e-01 1.000000000000000000e+00 -1.782120019197463989e-01 6.657599657773971558e-02 3.794969916343688965e-01 1.000000000000000000e+00 -1.848009973764419556e-01 6.573200225830078125e-02 3.879730105400085449e-01 1.000000000000000000e+00 -1.914599984884262085e-01 6.481800228357315063e-02 3.961519896984100342e-01 1.000000000000000000e+00 -1.981769949197769165e-01 6.386200338602066040e-02 4.040090143680572510e-01 1.000000000000000000e+00 -2.049349993467330933e-01 6.290700286626815796e-02 4.115140140056610107e-01 1.000000000000000000e+00 -2.117179930210113525e-01 6.199200078845024109e-02 4.186469912528991699e-01 1.000000000000000000e+00 -2.185119986534118652e-01 6.115800142288208008e-02 4.253920018672943115e-01 1.000000000000000000e+00 -2.253019958734512329e-01 6.044499948620796204e-02 4.317420125007629395e-01 1.000000000000000000e+00 -2.320770025253295898e-01 5.988899990916252136e-02 4.376949965953826904e-01 1.000000000000000000e+00 -2.388260066509246826e-01 5.951699987053871155e-02 4.432559907436370850e-01 1.000000000000000000e+00 -2.455430030822753906e-01 5.935199931263923645e-02 4.484359920024871826e-01 1.000000000000000000e+00 -2.522200047969818115e-01 5.941500142216682434e-02 4.532479941844940186e-01 1.000000000000000000e+00 -2.588570117950439453e-01 5.970599874854087830e-02 4.577099978923797607e-01 1.000000000000000000e+00 -2.654469907283782959e-01 6.023700162768363953e-02 4.618400037288665771e-01 1.000000000000000000e+00 -2.719939947128295898e-01 6.099399924278259277e-02 4.656600058078765869e-01 1.000000000000000000e+00 -2.784929871559143066e-01 6.197800114750862122e-02 4.691900014877319336e-01 1.000000000000000000e+00 -2.849510014057159424e-01 6.316799670457839966e-02 4.724510014057159424e-01 1.000000000000000000e+00 -2.913660109043121338e-01 6.455300003290176392e-02 4.754619896411895752e-01 1.000000000000000000e+00 -2.977400124073028564e-01 6.611700356006622314e-02 4.782429933547973633e-01 1.000000000000000000e+00 -3.040809929370880127e-01 6.783500313758850098e-02 4.808120131492614746e-01 1.000000000000000000e+00 -3.103820085525512695e-01 6.970199942588806152e-02 4.831860065460205078e-01 1.000000000000000000e+00 -3.166539967060089111e-01 7.169000059366226196e-02 4.853799939155578613e-01 1.000000000000000000e+00 -3.228990137577056885e-01 7.378199696540832520e-02 4.874080121517181396e-01 1.000000000000000000e+00 -3.291139900684356689e-01 7.597199827432632446e-02 4.892869889736175537e-01 1.000000000000000000e+00 -3.353079855442047119e-01 7.823599874973297119e-02 4.910239875316619873e-01 1.000000000000000000e+00 -3.414820134639739990e-01 8.056399971246719360e-02 4.926309883594512939e-01 1.000000000000000000e+00 -3.476360142230987549e-01 8.294600248336791992e-02 4.941209852695465088e-01 1.000000000000000000e+00 -3.537729978561401367e-01 8.537299931049346924e-02 4.955010116100311279e-01 1.000000000000000000e+00 -3.598980009555816650e-01 8.783099800348281860e-02 4.967780113220214844e-01 1.000000000000000000e+00 -3.660120069980621338e-01 9.031400084495544434e-02 4.979600012302398682e-01 1.000000000000000000e+00 -3.721159994602203369e-01 9.281600266695022583e-02 4.990530014038085938e-01 1.000000000000000000e+00 -3.782109916210174561e-01 9.533199667930603027e-02 5.000669956207275391e-01 1.000000000000000000e+00 -3.842990100383758545e-01 9.785500168800354004e-02 5.010020136833190918e-01 1.000000000000000000e+00 -3.903839886188507080e-01 1.003789976239204407e-01 5.018640160560607910e-01 1.000000000000000000e+00 -3.964670002460479736e-01 1.029020026326179504e-01 5.026580095291137695e-01 1.000000000000000000e+00 -4.025479853153228760e-01 1.054200008511543274e-01 5.033860206604003906e-01 1.000000000000000000e+00 -4.086290001869201660e-01 1.079299971461296082e-01 5.040519833564758301e-01 1.000000000000000000e+00 -4.147090017795562744e-01 1.104310005903244019e-01 5.046619772911071777e-01 1.000000000000000000e+00 -4.207910001277923584e-01 1.129200011491775513e-01 5.052149891853332520e-01 1.000000000000000000e+00 -4.268769919872283936e-01 1.153950020670890808e-01 5.057139992713928223e-01 1.000000000000000000e+00 -4.329670071601867676e-01 1.178549975156784058e-01 5.061600208282470703e-01 1.000000000000000000e+00 -4.390619993209838867e-01 1.202979981899261475e-01 5.065550208091735840e-01 1.000000000000000000e+00 -4.451630115509033203e-01 1.227239966392517090e-01 5.069010257720947266e-01 1.000000000000000000e+00 -4.512709975242614746e-01 1.251319944858551025e-01 5.071979761123657227e-01 1.000000000000000000e+00 -4.573859870433807373e-01 1.275220066308975220e-01 5.074480175971984863e-01 1.000000000000000000e+00 -4.635080099105834961e-01 1.298930048942565918e-01 5.076519846916198730e-01 1.000000000000000000e+00 -4.696399867534637451e-01 1.322450041770935059e-01 5.078089833259582520e-01 1.000000000000000000e+00 -4.757800102233886719e-01 1.345770061016082764e-01 5.079209804534912109e-01 1.000000000000000000e+00 -4.819290041923522949e-01 1.368910074234008789e-01 5.079889893531799316e-01 1.000000000000000000e+00 -4.880880117416381836e-01 1.391859948635101318e-01 5.080109834671020508e-01 1.000000000000000000e+00 -4.942579865455627441e-01 1.414619982242584229e-01 5.079879760742187500e-01 1.000000000000000000e+00 -5.004379749298095703e-01 1.437190026044845581e-01 5.079200267791748047e-01 1.000000000000000000e+00 -5.066289901733398438e-01 1.459580063819885254e-01 5.078060030937194824e-01 1.000000000000000000e+00 -5.128309726715087891e-01 1.481789946556091309e-01 5.076479911804199219e-01 1.000000000000000000e+00 -5.190449953079223633e-01 1.503829956054687500e-01 5.074430108070373535e-01 1.000000000000000000e+00 -5.252699851989746094e-01 1.525689959526062012e-01 5.071920156478881836e-01 1.000000000000000000e+00 -5.315070152282714844e-01 1.547390073537826538e-01 5.068950057029724121e-01 1.000000000000000000e+00 -5.377550125122070312e-01 1.568939983844757080e-01 5.065510272979736328e-01 1.000000000000000000e+00 -5.440149903297424316e-01 1.590330004692077637e-01 5.061590075492858887e-01 1.000000000000000000e+00 -5.502870082855224609e-01 1.611579954624176025e-01 5.057190060615539551e-01 1.000000000000000000e+00 -5.565710067749023438e-01 1.632689982652664185e-01 5.052300095558166504e-01 1.000000000000000000e+00 -5.628659725189208984e-01 1.653680056333541870e-01 5.046920180320739746e-01 1.000000000000000000e+00 -5.691720247268676758e-01 1.674540042877197266e-01 5.041049718856811523e-01 1.000000000000000000e+00 -5.754899978637695312e-01 1.695300042629241943e-01 5.034660100936889648e-01 1.000000000000000000e+00 -5.818189978599548340e-01 1.715960055589675903e-01 5.027769804000854492e-01 1.000000000000000000e+00 -5.881580114364624023e-01 1.736519932746887207e-01 5.020350217819213867e-01 1.000000000000000000e+00 -5.945079922676086426e-01 1.757010072469711304e-01 5.012410283088684082e-01 1.000000000000000000e+00 -6.008679866790771484e-01 1.777430027723312378e-01 5.003939867019653320e-01 1.000000000000000000e+00 -6.072379946708679199e-01 1.797789931297302246e-01 4.994919896125793457e-01 1.000000000000000000e+00 -6.136170029640197754e-01 1.818110048770904541e-01 4.985359907150268555e-01 1.000000000000000000e+00 -6.200050115585327148e-01 1.838400065898895264e-01 4.975239932537078857e-01 1.000000000000000000e+00 -6.264010071754455566e-01 1.858669966459274292e-01 4.964559972286224365e-01 1.000000000000000000e+00 -6.328049898147583008e-01 1.878930032253265381e-01 4.953320026397705078e-01 1.000000000000000000e+00 -6.392160058021545410e-01 1.899210065603256226e-01 4.941500127315521240e-01 1.000000000000000000e+00 -6.456329822540283203e-01 1.919520050287246704e-01 4.929099977016448975e-01 1.000000000000000000e+00 -6.520559787750244141e-01 1.939859986305236816e-01 4.916110038757324219e-01 1.000000000000000000e+00 -6.584830284118652344e-01 1.960269957780838013e-01 4.902530014514923096e-01 1.000000000000000000e+00 -6.649150252342224121e-01 1.980749964714050293e-01 4.888359904289245605e-01 1.000000000000000000e+00 -6.713489890098571777e-01 2.001329958438873291e-01 4.873580038547515869e-01 1.000000000000000000e+00 -6.777859926223754883e-01 2.022030055522918701e-01 4.858190119266510010e-01 1.000000000000000000e+00 -6.842240095138549805e-01 2.042859941720962524e-01 4.842190146446228027e-01 1.000000000000000000e+00 -6.906610131263732910e-01 2.063840031623840332e-01 4.825580120086669922e-01 1.000000000000000000e+00 -6.970980167388916016e-01 2.085009962320327759e-01 4.808349907398223877e-01 1.000000000000000000e+00 -7.035319805145263672e-01 2.106380015611648560e-01 4.790489971637725830e-01 1.000000000000000000e+00 -7.099620103836059570e-01 2.127970010042190552e-01 4.772010147571563721e-01 1.000000000000000000e+00 -7.163869738578796387e-01 2.149820029735565186e-01 4.752900004386901855e-01 1.000000000000000000e+00 -7.228050231933593750e-01 2.171940058469772339e-01 4.733160138130187988e-01 1.000000000000000000e+00 -7.292159795761108398e-01 2.194370031356811523e-01 4.712789952754974365e-01 1.000000000000000000e+00 -7.356160283088684082e-01 2.217130064964294434e-01 4.691799879074096680e-01 1.000000000000000000e+00 -7.420039772987365723e-01 2.240249961614608765e-01 4.670180082321166992e-01 1.000000000000000000e+00 -7.483779788017272949e-01 2.263769954442977905e-01 4.647940099239349365e-01 1.000000000000000000e+00 -7.547370195388793945e-01 2.287719994783401489e-01 4.625090062618255615e-01 1.000000000000000000e+00 -7.610769867897033691e-01 2.312140017747879028e-01 4.601620137691497803e-01 1.000000000000000000e+00 -7.673979997634887695e-01 2.337049990892410278e-01 4.577549993991851807e-01 1.000000000000000000e+00 -7.736949920654296875e-01 2.362489998340606689e-01 4.552890062332153320e-01 1.000000000000000000e+00 -7.799680233001708984e-01 2.388509958982467651e-01 4.527649879455566406e-01 1.000000000000000000e+00 -7.862120270729064941e-01 2.415139973163604736e-01 4.501839876174926758e-01 1.000000000000000000e+00 -7.924270033836364746e-01 2.442419975996017456e-01 4.475429952144622803e-01 1.000000000000000000e+00 -7.986080050468444824e-01 2.470400035381317139e-01 4.448480010032653809e-01 1.000000000000000000e+00 -8.047519922256469727e-01 2.499109953641891479e-01 4.421019852161407471e-01 1.000000000000000000e+00 -8.108549714088439941e-01 2.528609931468963623e-01 4.393050074577331543e-01 1.000000000000000000e+00 -8.169140219688415527e-01 2.558949887752532959e-01 4.364610016345977783e-01 1.000000000000000000e+00 -8.229259848594665527e-01 2.590160071849822998e-01 4.335730075836181641e-01 1.000000000000000000e+00 -8.288859724998474121e-01 2.622289955615997314e-01 4.306440055370330811e-01 1.000000000000000000e+00 -8.347910046577453613e-01 2.655400037765502930e-01 4.276709854602813721e-01 1.000000000000000000e+00 -8.406360149383544922e-01 2.689529955387115479e-01 4.246659874916076660e-01 1.000000000000000000e+00 -8.464159965515136719e-01 2.724730074405670166e-01 4.216310083866119385e-01 1.000000000000000000e+00 -8.521260023117065430e-01 2.761059999465942383e-01 4.185729920864105225e-01 1.000000000000000000e+00 -8.577629923820495605e-01 2.798570096492767334e-01 4.154959917068481445e-01 1.000000000000000000e+00 -8.633199930191040039e-01 2.837289869785308838e-01 4.124029874801635742e-01 1.000000000000000000e+00 -8.687930107116699219e-01 2.877280116081237793e-01 4.093030095100402832e-01 1.000000000000000000e+00 -8.741760253906250000e-01 2.918590009212493896e-01 4.062049984931945801e-01 1.000000000000000000e+00 -8.794639706611633301e-01 2.961249947547912598e-01 4.031180143356323242e-01 1.000000000000000000e+00 -8.846510052680969238e-01 3.005299866199493408e-01 4.000470042228698730e-01 1.000000000000000000e+00 -8.897309899330139160e-01 3.050790131092071533e-01 3.970020115375518799e-01 1.000000000000000000e+00 -8.946999907493591309e-01 3.097729980945587158e-01 3.939949870109558105e-01 1.000000000000000000e+00 -8.995519876480102539e-01 3.146159946918487549e-01 3.910369873046875000e-01 1.000000000000000000e+00 -9.042810201644897461e-01 3.196099996566772461e-01 3.881370127201080322e-01 1.000000000000000000e+00 -9.088839888572692871e-01 3.247550129890441895e-01 3.853079974651336670e-01 1.000000000000000000e+00 -9.133539795875549316e-01 3.300519883632659912e-01 3.825629949569702148e-01 1.000000000000000000e+00 -9.176890254020690918e-01 3.355000019073486328e-01 3.799149990081787109e-01 1.000000000000000000e+00 -9.218840003013610840e-01 3.410980105400085449e-01 3.773759901523590088e-01 1.000000000000000000e+00 -9.259369969367980957e-01 3.468439877033233643e-01 3.749589920043945312e-01 1.000000000000000000e+00 -9.298449754714965820e-01 3.527339994907379150e-01 3.726769983768463135e-01 1.000000000000000000e+00 -9.336060285568237305e-01 3.587639927864074707e-01 3.705410063266754150e-01 1.000000000000000000e+00 -9.372209906578063965e-01 3.649289906024932861e-01 3.685669898986816406e-01 1.000000000000000000e+00 -9.406870007514953613e-01 3.712239861488342285e-01 3.667620122432708740e-01 1.000000000000000000e+00 -9.440060257911682129e-01 3.776429891586303711e-01 3.651359975337982178e-01 1.000000000000000000e+00 -9.471799731254577637e-01 3.841780126094818115e-01 3.637009859085083008e-01 1.000000000000000000e+00 -9.502099752426147461e-01 3.908199965953826904e-01 3.624680042266845703e-01 1.000000000000000000e+00 -9.530990123748779297e-01 3.975630104541778564e-01 3.614380061626434326e-01 1.000000000000000000e+00 -9.558489918708801270e-01 4.043999910354614258e-01 3.606190085411071777e-01 1.000000000000000000e+00 -9.584640264511108398e-01 4.113239943981170654e-01 3.600139915943145752e-01 1.000000000000000000e+00 -9.609490036964416504e-01 4.183230102062225342e-01 3.596299886703491211e-01 1.000000000000000000e+00 -9.633100032806396484e-01 4.253900051116943359e-01 3.594689965248107910e-01 1.000000000000000000e+00 -9.655489921569824219e-01 4.325189888477325439e-01 3.595289885997772217e-01 1.000000000000000000e+00 -9.676709771156311035e-01 4.397029876708984375e-01 3.598099946975708008e-01 1.000000000000000000e+00 -9.696800112724304199e-01 4.469360113143920898e-01 3.603110015392303467e-01 1.000000000000000000e+00 -9.715819954872131348e-01 4.542100131511688232e-01 3.610300123691558838e-01 1.000000000000000000e+00 -9.733809828758239746e-01 4.615199863910675049e-01 3.619650006294250488e-01 1.000000000000000000e+00 -9.750819802284240723e-01 4.688610136508941650e-01 3.631109893321990967e-01 1.000000000000000000e+00 -9.766899943351745605e-01 4.762260019779205322e-01 3.644660115242004395e-01 1.000000000000000000e+00 -9.782099723815917969e-01 4.836120009422302246e-01 3.660250008106231689e-01 1.000000000000000000e+00 -9.796450138092041016e-01 4.910140037536621094e-01 3.677830100059509277e-01 1.000000000000000000e+00 -9.810000061988830566e-01 4.984279870986938477e-01 3.697339892387390137e-01 1.000000000000000000e+00 -9.822790026664733887e-01 5.058509707450866699e-01 3.718740046024322510e-01 1.000000000000000000e+00 -9.834849834442138672e-01 5.132799744606018066e-01 3.741979897022247314e-01 1.000000000000000000e+00 -9.846220016479492188e-01 5.207129716873168945e-01 3.766979873180389404e-01 1.000000000000000000e+00 -9.856929779052734375e-01 5.281479954719543457e-01 3.793709874153137207e-01 1.000000000000000000e+00 -9.866999983787536621e-01 5.355820059776306152e-01 3.822099864482879639e-01 1.000000000000000000e+00 -9.876459836959838867e-01 5.430150032043457031e-01 3.852100074291229248e-01 1.000000000000000000e+00 -9.885330200195312500e-01 5.504459738731384277e-01 3.883650004863739014e-01 1.000000000000000000e+00 -9.893630146980285645e-01 5.578730106353759766e-01 3.916710019111633301e-01 1.000000000000000000e+00 -9.901379942893981934e-01 5.652959942817687988e-01 3.951219916343688965e-01 1.000000000000000000e+00 -9.908710122108459473e-01 5.727059841156005859e-01 3.987140059471130371e-01 1.000000000000000000e+00 -9.915580153465270996e-01 5.801069736480712891e-01 4.024409949779510498e-01 1.000000000000000000e+00 -9.921960234642028809e-01 5.875020027160644531e-01 4.062989950180053711e-01 1.000000000000000000e+00 -9.927849769592285156e-01 5.948910117149353027e-01 4.102829992771148682e-01 1.000000000000000000e+00 -9.933260083198547363e-01 6.022750139236450195e-01 4.143899977207183838e-01 1.000000000000000000e+00 -9.938340187072753906e-01 6.096439957618713379e-01 4.186129868030548096e-01 1.000000000000000000e+00 -9.943090081214904785e-01 6.169989705085754395e-01 4.229499995708465576e-01 1.000000000000000000e+00 -9.947379827499389648e-01 6.243500113487243652e-01 4.273970127105712891e-01 1.000000000000000000e+00 -9.951220154762268066e-01 6.316959857940673828e-01 4.319509863853454590e-01 1.000000000000000000e+00 -9.954800009727478027e-01 6.390269994735717773e-01 4.366070032119750977e-01 1.000000000000000000e+00 -9.958099722862243652e-01 6.463440060615539551e-01 4.413610100746154785e-01 1.000000000000000000e+00 -9.960960149765014648e-01 6.536589860916137695e-01 4.462130069732666016e-01 1.000000000000000000e+00 -9.963409900665283203e-01 6.609690189361572266e-01 4.511600136756896973e-01 1.000000000000000000e+00 -9.965800046920776367e-01 6.682559847831726074e-01 4.561919867992401123e-01 1.000000000000000000e+00 -9.967749714851379395e-01 6.755409836769104004e-01 4.613139927387237549e-01 1.000000000000000000e+00 -9.969249963760375977e-01 6.828280091285705566e-01 4.665260016918182373e-01 1.000000000000000000e+00 -9.970769882202148438e-01 6.900879740715026855e-01 4.718109965324401855e-01 1.000000000000000000e+00 -9.971860051155090332e-01 6.973490118980407715e-01 4.771820008754730225e-01 1.000000000000000000e+00 -9.972540140151977539e-01 7.046110033988952637e-01 4.826349914073944092e-01 1.000000000000000000e+00 -9.973250031471252441e-01 7.118480205535888672e-01 4.881539940834045410e-01 1.000000000000000000e+00 -9.973509907722473145e-01 7.190889716148376465e-01 4.937550127506256104e-01 1.000000000000000000e+00 -9.973509907722473145e-01 7.263240218162536621e-01 4.994280040264129639e-01 1.000000000000000000e+00 -9.973409771919250488e-01 7.335450053215026855e-01 5.051670074462890625e-01 1.000000000000000000e+00 -9.972850084304809570e-01 7.407720088958740234e-01 5.109829902648925781e-01 1.000000000000000000e+00 -9.972280263900756836e-01 7.479810118675231934e-01 5.168589949607849121e-01 1.000000000000000000e+00 -9.971380233764648438e-01 7.551900148391723633e-01 5.228059887886047363e-01 1.000000000000000000e+00 -9.970189929008483887e-01 7.623980045318603516e-01 5.288209915161132812e-01 1.000000000000000000e+00 -9.968979954719543457e-01 7.695909738540649414e-01 5.348920226097106934e-01 1.000000000000000000e+00 -9.967269897460937500e-01 7.767950296401977539e-01 5.410389900207519531e-01 1.000000000000000000e+00 -9.965710043907165527e-01 7.839769721031188965e-01 5.472329854965209961e-01 1.000000000000000000e+00 -9.963690042495727539e-01 7.911670207977294922e-01 5.534989833831787109e-01 1.000000000000000000e+00 -9.961619973182678223e-01 7.983480095863342285e-01 5.598199963569641113e-01 1.000000000000000000e+00 -9.959319829940795898e-01 8.055269718170166016e-01 5.662019848823547363e-01 1.000000000000000000e+00 -9.956799745559692383e-01 8.127059936523437500e-01 5.726450085639953613e-01 1.000000000000000000e+00 -9.954239726066589355e-01 8.198750019073486328e-01 5.791400074958801270e-01 1.000000000000000000e+00 -9.951310157775878906e-01 8.270519971847534180e-01 5.857009887695312500e-01 1.000000000000000000e+00 -9.948509931564331055e-01 8.342130184173583984e-01 5.923069715499877930e-01 1.000000000000000000e+00 -9.945240020751953125e-01 8.413869738578796387e-01 5.989829897880554199e-01 1.000000000000000000e+00 -9.942219853401184082e-01 8.485400080680847168e-01 6.056960225105285645e-01 1.000000000000000000e+00 -9.938660264015197754e-01 8.557109832763671875e-01 6.124820113182067871e-01 1.000000000000000000e+00 -9.935449957847595215e-01 8.628590106964111328e-01 6.192989945411682129e-01 1.000000000000000000e+00 -9.931700229644775391e-01 8.700240254402160645e-01 6.261889934539794922e-01 1.000000000000000000e+00 -9.928309917449951172e-01 8.771679997444152832e-01 6.331089735031127930e-01 1.000000000000000000e+00 -9.924399852752685547e-01 8.843299746513366699e-01 6.400989890098571777e-01 1.000000000000000000e+00 -9.920889735221862793e-01 8.914700150489807129e-01 6.471160054206848145e-01 1.000000000000000000e+00 -9.916880130767822266e-01 8.986269831657409668e-01 6.542019844055175781e-01 1.000000000000000000e+00 -9.913319945335388184e-01 9.057629704475402832e-01 6.613090038299560547e-01 1.000000000000000000e+00 -9.909300208091735840e-01 9.129149913787841797e-01 6.684809923171997070e-01 1.000000000000000000e+00 -9.905700087547302246e-01 9.200490117073059082e-01 6.756749749183654785e-01 1.000000000000000000e+00 -9.901750087738037109e-01 9.271960258483886719e-01 6.829259991645812988e-01 1.000000000000000000e+00 -9.898149967193603516e-01 9.343289732933044434e-01 6.901980042457580566e-01 1.000000000000000000e+00 -9.894340038299560547e-01 9.414700269699096680e-01 6.975190043449401855e-01 1.000000000000000000e+00 -9.890769720077514648e-01 9.486039876937866211e-01 7.048630118370056152e-01 1.000000000000000000e+00 -9.887170195579528809e-01 9.557420015335083008e-01 7.122420072555541992e-01 1.000000000000000000e+00 -9.883670210838317871e-01 9.628779888153076172e-01 7.196490168571472168e-01 1.000000000000000000e+00 -9.880329966545104980e-01 9.700120091438293457e-01 7.270770072937011719e-01 1.000000000000000000e+00 -9.876909852027893066e-01 9.771540164947509766e-01 7.345359921455383301e-01 1.000000000000000000e+00 -9.873870015144348145e-01 9.842879772186279297e-01 7.420020103454589844e-01 1.000000000000000000e+00 -9.870529770851135254e-01 9.914379715919494629e-01 7.495040297508239746e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/nipy_spectral b/fastplotlib/utils/colormaps/nipy_spectral deleted file mode 100644 index ff914a0fe..000000000 --- a/fastplotlib/utils/colormaps/nipy_spectral +++ /dev/null @@ -1,256 +0,0 @@ -0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.660392016172409058e-02 0.000000000000000000e+00 4.182745143771171570e-02 1.000000000000000000e+00 -7.320784032344818115e-02 0.000000000000000000e+00 8.365490287542343140e-02 1.000000000000000000e+00 -1.098117679357528687e-01 0.000000000000000000e+00 1.254823505878448486e-01 1.000000000000000000e+00 -1.464156806468963623e-01 0.000000000000000000e+00 1.673098057508468628e-01 1.000000000000000000e+00 -1.830196082592010498e-01 0.000000000000000000e+00 2.091372609138488770e-01 1.000000000000000000e+00 -2.196235358715057373e-01 0.000000000000000000e+00 2.509647011756896973e-01 1.000000000000000000e+00 -2.562274634838104248e-01 0.000000000000000000e+00 2.927921712398529053e-01 1.000000000000000000e+00 -2.928313612937927246e-01 0.000000000000000000e+00 3.346196115016937256e-01 1.000000000000000000e+00 -3.294352889060974121e-01 0.000000000000000000e+00 3.764470517635345459e-01 1.000000000000000000e+00 -3.660392165184020996e-01 0.000000000000000000e+00 4.182745218276977539e-01 1.000000000000000000e+00 -4.026431441307067871e-01 0.000000000000000000e+00 4.601019620895385742e-01 1.000000000000000000e+00 -4.392470717430114746e-01 0.000000000000000000e+00 5.019294023513793945e-01 1.000000000000000000e+00 -4.680058956146240234e-01 0.000000000000000000e+00 5.346078276634216309e-01 1.000000000000000000e+00 -4.732294082641601562e-01 0.000000000000000000e+00 5.398392081260681152e-01 1.000000000000000000e+00 -4.784529507160186768e-01 0.000000000000000000e+00 5.450705885887145996e-01 1.000000000000000000e+00 -4.836764633655548096e-01 0.000000000000000000e+00 5.503019690513610840e-01 1.000000000000000000e+00 -4.889000058174133301e-01 0.000000000000000000e+00 5.555333495140075684e-01 1.000000000000000000e+00 -4.941235184669494629e-01 0.000000000000000000e+00 5.607647299766540527e-01 1.000000000000000000e+00 -4.993470609188079834e-01 0.000000000000000000e+00 5.659960508346557617e-01 1.000000000000000000e+00 -5.045706033706665039e-01 0.000000000000000000e+00 5.712274312973022461e-01 1.000000000000000000e+00 -5.097941160202026367e-01 0.000000000000000000e+00 5.764588117599487305e-01 1.000000000000000000e+00 -5.150176286697387695e-01 0.000000000000000000e+00 5.816901922225952148e-01 1.000000000000000000e+00 -5.202412009239196777e-01 0.000000000000000000e+00 5.869215726852416992e-01 1.000000000000000000e+00 -5.254647135734558105e-01 0.000000000000000000e+00 5.921529531478881836e-01 1.000000000000000000e+00 -5.306882262229919434e-01 0.000000000000000000e+00 5.973843336105346680e-01 1.000000000000000000e+00 -5.123862624168395996e-01 0.000000000000000000e+00 6.026157140731811523e-01 1.000000000000000000e+00 -4.705588221549987793e-01 0.000000000000000000e+00 6.078470349311828613e-01 1.000000000000000000e+00 -4.287313818931579590e-01 0.000000000000000000e+00 6.130784153938293457e-01 1.000000000000000000e+00 -3.869039118289947510e-01 0.000000000000000000e+00 6.183097958564758301e-01 1.000000000000000000e+00 -3.450764715671539307e-01 0.000000000000000000e+00 6.235411763191223145e-01 1.000000000000000000e+00 -3.032490313053131104e-01 0.000000000000000000e+00 6.287725567817687988e-01 1.000000000000000000e+00 -2.614215612411499023e-01 0.000000000000000000e+00 6.340039372444152832e-01 1.000000000000000000e+00 -2.195941209793090820e-01 0.000000000000000000e+00 6.392353177070617676e-01 1.000000000000000000e+00 -1.777666658163070679e-01 0.000000000000000000e+00 6.444666385650634766e-01 1.000000000000000000e+00 -1.359392106533050537e-01 0.000000000000000000e+00 6.496980190277099609e-01 1.000000000000000000e+00 -9.411176294088363647e-02 0.000000000000000000e+00 6.549293994903564453e-01 1.000000000000000000e+00 -5.228431522846221924e-02 0.000000000000000000e+00 6.601607799530029297e-01 1.000000000000000000e+00 -1.045686285942792892e-02 0.000000000000000000e+00 6.653921604156494141e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 6.784647107124328613e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 6.941509842872619629e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 7.098372578620910645e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 7.255235314369201660e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 7.412098050117492676e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 7.568960785865783691e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 7.725823521614074707e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 7.882686257362365723e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 8.039548993110656738e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 8.196411728858947754e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 8.353274464607238770e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 8.510137200355529785e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 8.666999936103820801e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.660392016172409058e-02 8.666999936103820801e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.320784032344818115e-02 8.666999936103820801e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.098117679357528687e-01 8.666999936103820801e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.464156806468963623e-01 8.666999936103820801e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.830196082592010498e-01 8.666999936103820801e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.196235358715057373e-01 8.666999936103820801e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.562274634838104248e-01 8.666999936103820801e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.928313612937927246e-01 8.666999936103820801e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.294352889060974121e-01 8.666999936103820801e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.660392165184020996e-01 8.666999936103820801e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.026431441307067871e-01 8.666999936103820801e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.392470717430114746e-01 8.666999936103820801e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.693137109279632568e-01 8.666999936103820801e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.797686338424682617e-01 8.666999936103820801e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.902235269546508789e-01 8.666999936103820801e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.006784200668334961e-01 8.666999936103820801e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.111333131790161133e-01 8.666999936103820801e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.215882062911987305e-01 8.666999936103820801e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.320431590080261230e-01 8.666999936103820801e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.424980521202087402e-01 8.666999936103820801e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.529529452323913574e-01 8.666999936103820801e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.634078383445739746e-01 8.666999936103820801e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.738627314567565918e-01 8.666999936103820801e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.843176245689392090e-01 8.666999936103820801e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.947725772857666016e-01 8.666999936103820801e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.026157140731811523e-01 8.588568568229675293e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.078470349311828613e-01 8.431705832481384277e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.130784153938293457e-01 8.274843096733093262e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.183097958564758301e-01 8.117980360984802246e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.235411763191223145e-01 7.961117625236511230e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.287725567817687988e-01 7.804254889488220215e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.340039372444152832e-01 7.647392153739929199e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.392353177070617676e-01 7.490529417991638184e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.444666385650634766e-01 7.333666682243347168e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.496980190277099609e-01 7.176803946495056152e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.549293994903564453e-01 7.019941210746765137e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.601607799530029297e-01 6.863078474998474121e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.653921604156494141e-01 6.706215739250183105e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.667000055313110352e-01 6.588529348373413086e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.667000055313110352e-01 6.483901739120483398e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.667000055313110352e-01 6.379274725914001465e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.667000055313110352e-01 6.274647116661071777e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.667000055313110352e-01 6.170019507408142090e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.667000055313110352e-01 6.065391898155212402e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.667000055313110352e-01 5.960764884948730469e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.667000055313110352e-01 5.856137275695800781e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.667000055313110352e-01 5.751509666442871094e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.667000055313110352e-01 5.646882057189941406e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.667000055313110352e-01 5.542255043983459473e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.667000055313110352e-01 5.437627434730529785e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.667000055313110352e-01 5.332999825477600098e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.614686250686645508e-01 4.914725422859191895e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.562372446060180664e-01 4.496451020240783691e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.510058641433715820e-01 4.078176617622375488e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.457744836807250977e-01 3.659901916980743408e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.405431628227233887e-01 3.241627514362335205e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.353117823600769043e-01 2.823352813720703125e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.300804018974304199e-01 2.405078411102294922e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.248490214347839355e-01 1.986803859472274780e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.196176409721374512e-01 1.568529456853866577e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.143862605094909668e-01 1.150254905223846436e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.091548800468444824e-01 7.319804280996322632e-02 1.000000000000000000e+00 -0.000000000000000000e+00 6.039235591888427734e-01 3.137058764696121216e-02 1.000000000000000000e+00 -0.000000000000000000e+00 6.026137471199035645e-01 0.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 6.130686402320861816e-01 0.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 6.235235333442687988e-01 0.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 6.339784264564514160e-01 0.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 6.444333195686340332e-01 0.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 6.548882126808166504e-01 0.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 6.653431653976440430e-01 0.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 6.757980585098266602e-01 0.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 6.862529516220092773e-01 0.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 6.967078447341918945e-01 0.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 7.071627378463745117e-01 0.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 7.176176309585571289e-01 0.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 7.280725240707397461e-01 0.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 7.385313510894775391e-01 0.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 7.489941120147705078e-01 0.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 7.594568729400634766e-01 0.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 7.699196338653564453e-01 0.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 7.803823351860046387e-01 0.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 7.908450961112976074e-01 0.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 8.013078570365905762e-01 0.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 8.117706179618835449e-01 0.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 8.222333192825317383e-01 0.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 8.326960802078247070e-01 0.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 8.431588411331176758e-01 0.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 8.536215424537658691e-01 0.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 8.640843033790588379e-01 0.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 8.745411634445190430e-01 0.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 8.849960565567016602e-01 0.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 8.954510092735290527e-01 0.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 9.059059023857116699e-01 0.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 9.163607954978942871e-01 0.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 9.268156886100769043e-01 0.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 9.372705817222595215e-01 0.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 9.477254748344421387e-01 0.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 9.581803679466247559e-01 0.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 9.686353206634521484e-01 0.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 9.790902137756347656e-01 0.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 9.895451068878173828e-01 0.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.751372501254081726e-02 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.150274500250816345e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.725411713123321533e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.300549000501632690e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.875686287879943848e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.450823426246643066e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.025960862636566162e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.601098001003265381e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.176235437393188477e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.751372575759887695e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.326509714126586914e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.901646852493286133e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.372215390205383301e-01 9.986921548843383789e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.529078722000122070e-01 9.934607744216918945e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.685941457748413086e-01 9.882293939590454102e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.842804193496704102e-01 9.829980134963989258e-01 0.000000000000000000e+00 1.000000000000000000e+00 -7.999666929244995117e-01 9.777666926383972168e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.156529664993286133e-01 9.725353121757507324e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.313392400741577148e-01 9.673039317131042480e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.470255136489868164e-01 9.620725512504577637e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.627117872238159180e-01 9.568411707878112793e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.783980607986450195e-01 9.516097903251647949e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.940843343734741211e-01 9.463784098625183105e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.097706079483032227e-01 9.411470293998718262e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.254568815231323242e-01 9.359157085418701172e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.359157085418701172e-01 9.280725717544555664e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.411470293998718262e-01 9.176176190376281738e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.463784098625183105e-01 9.071627259254455566e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.516097903251647949e-01 8.967078328132629395e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.568411707878112793e-01 8.862529397010803223e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.620725512504577637e-01 8.757980465888977051e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.673039317131042480e-01 8.653431534767150879e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.725353121757507324e-01 8.548882603645324707e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.777666926383972168e-01 8.444333076477050781e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.829980134963989258e-01 8.339784145355224609e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.882293939590454102e-01 8.235235214233398438e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.934607744216918945e-01 8.130686283111572266e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.986921548843383789e-01 8.026137351989746094e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.882353067398071289e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.725490331649780273e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.568627595901489258e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.411764860153198242e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.254902124404907227e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.098039388656616211e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.941176652908325195e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.784313917160034180e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.627451181411743164e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.470588445663452148e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.313725709915161133e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.156862974166870117e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.000000238418579102e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.529412031173706055e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.058823823928833008e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.588235318660736084e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.117647111415863037e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.647058904170989990e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.176470696926116943e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.705882489681243896e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.235294133424758911e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.764705926179885864e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.294117718935012817e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.235294371843338013e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.529411926865577698e-02 0.000000000000000000e+00 1.000000000000000000e+00 -9.973862767219543457e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.869313836097717285e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.764764904975891113e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.660215973854064941e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.555666446685791016e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.451117515563964844e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.346568584442138672e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.242019653320312500e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.137470722198486328e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.032921791076660156e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.928372263908386230e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.823823332786560059e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.719274401664733887e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.640843033790588379e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.588529229164123535e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.536215424537658691e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.483902215957641602e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.431588411331176758e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.379274606704711914e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.326960802078247070e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.274646997451782227e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.222333192825317383e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.170019388198852539e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.117706179618835449e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.065392374992370605e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.013078570365905762e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.000000119209289551e-01 4.705882444977760315e-02 4.705882444977760315e-02 1.000000000000000000e+00 -8.000000119209289551e-01 1.098039224743843079e-01 1.098039224743843079e-01 1.000000000000000000e+00 -8.000000119209289551e-01 1.725490242242813110e-01 1.725490242242813110e-01 1.000000000000000000e+00 -8.000000119209289551e-01 2.352941185235977173e-01 2.352941185235977173e-01 1.000000000000000000e+00 -8.000000119209289551e-01 2.980392277240753174e-01 2.980392277240753174e-01 1.000000000000000000e+00 -8.000000119209289551e-01 3.607843220233917236e-01 3.607843220233917236e-01 1.000000000000000000e+00 -8.000000119209289551e-01 4.235294163227081299e-01 4.235294163227081299e-01 1.000000000000000000e+00 -8.000000119209289551e-01 4.862745106220245361e-01 4.862745106220245361e-01 1.000000000000000000e+00 -8.000000119209289551e-01 5.490196347236633301e-01 5.490196347236633301e-01 1.000000000000000000e+00 -8.000000119209289551e-01 6.117647290229797363e-01 6.117647290229797363e-01 1.000000000000000000e+00 -8.000000119209289551e-01 6.745098233222961426e-01 6.745098233222961426e-01 1.000000000000000000e+00 -8.000000119209289551e-01 7.372549176216125488e-01 7.372549176216125488e-01 1.000000000000000000e+00 -8.000000119209289551e-01 8.000000119209289551e-01 8.000000119209289551e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/ocean b/fastplotlib/utils/colormaps/ocean deleted file mode 100644 index e42719b48..000000000 --- a/fastplotlib/utils/colormaps/ocean +++ /dev/null @@ -1,256 +0,0 @@ -0.000000000000000000e+00 5.000000000000000000e-01 0.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 4.941176474094390869e-01 3.921568859368562698e-03 1.000000000000000000e+00 -0.000000000000000000e+00 4.882352948188781738e-01 7.843137718737125397e-03 1.000000000000000000e+00 -0.000000000000000000e+00 4.823529422283172607e-01 1.176470611244440079e-02 1.000000000000000000e+00 -0.000000000000000000e+00 4.764705896377563477e-01 1.568627543747425079e-02 1.000000000000000000e+00 -0.000000000000000000e+00 4.705882370471954346e-01 1.960784383118152618e-02 1.000000000000000000e+00 -0.000000000000000000e+00 4.647058844566345215e-01 2.352941222488880157e-02 1.000000000000000000e+00 -0.000000000000000000e+00 4.588235318660736084e-01 2.745098061859607697e-02 1.000000000000000000e+00 -0.000000000000000000e+00 4.529411792755126953e-01 3.137255087494850159e-02 1.000000000000000000e+00 -0.000000000000000000e+00 4.470588266849517822e-01 3.529411926865577698e-02 1.000000000000000000e+00 -0.000000000000000000e+00 4.411764740943908691e-01 3.921568766236305237e-02 1.000000000000000000e+00 -0.000000000000000000e+00 4.352941215038299561e-01 4.313725605607032776e-02 1.000000000000000000e+00 -0.000000000000000000e+00 4.294117689132690430e-01 4.705882444977760315e-02 1.000000000000000000e+00 -0.000000000000000000e+00 4.235294163227081299e-01 5.098039284348487854e-02 1.000000000000000000e+00 -0.000000000000000000e+00 4.176470637321472168e-01 5.490196123719215393e-02 1.000000000000000000e+00 -0.000000000000000000e+00 4.117647111415863037e-01 5.882352963089942932e-02 1.000000000000000000e+00 -0.000000000000000000e+00 4.058823585510253906e-01 6.274510174989700317e-02 1.000000000000000000e+00 -0.000000000000000000e+00 4.000000059604644775e-01 6.666667014360427856e-02 1.000000000000000000e+00 -0.000000000000000000e+00 3.941176533699035645e-01 7.058823853731155396e-02 1.000000000000000000e+00 -0.000000000000000000e+00 3.882353007793426514e-01 7.450980693101882935e-02 1.000000000000000000e+00 -0.000000000000000000e+00 3.823529481887817383e-01 7.843137532472610474e-02 1.000000000000000000e+00 -0.000000000000000000e+00 3.764705955982208252e-01 8.235294371843338013e-02 1.000000000000000000e+00 -0.000000000000000000e+00 3.705882430076599121e-01 8.627451211214065552e-02 1.000000000000000000e+00 -0.000000000000000000e+00 3.647058904170989990e-01 9.019608050584793091e-02 1.000000000000000000e+00 -0.000000000000000000e+00 3.588235378265380859e-01 9.411764889955520630e-02 1.000000000000000000e+00 -0.000000000000000000e+00 3.529411852359771729e-01 9.803921729326248169e-02 1.000000000000000000e+00 -0.000000000000000000e+00 3.470588326454162598e-01 1.019607856869697571e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.411764800548553467e-01 1.058823540806770325e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.352941274642944336e-01 1.098039224743843079e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.294117748737335205e-01 1.137254908680915833e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.235294222831726074e-01 1.176470592617988586e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.176470696926116943e-01 1.215686276555061340e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.117647171020507812e-01 1.254902034997940063e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.058823645114898682e-01 1.294117718935012817e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.000000119209289551e-01 1.333333402872085571e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.941176593303680420e-01 1.372549086809158325e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.882353067398071289e-01 1.411764770746231079e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.823529541492462158e-01 1.450980454683303833e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.764706015586853027e-01 1.490196138620376587e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.705882489681243896e-01 1.529411822557449341e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.647058963775634766e-01 1.568627506494522095e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.588235437870025635e-01 1.607843190431594849e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.529411911964416504e-01 1.647058874368667603e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.470588237047195435e-01 1.686274558305740356e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.411764711141586304e-01 1.725490242242813110e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.352941185235977173e-01 1.764705926179885864e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.294117659330368042e-01 1.803921610116958618e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.235294133424758911e-01 1.843137294054031372e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.176470607519149780e-01 1.882352977991104126e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.117647081613540649e-01 1.921568661928176880e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.058823555707931519e-01 1.960784345865249634e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.000000029802322388e-01 2.000000029802322388e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.941176503896713257e-01 2.039215713739395142e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.882352977991104126e-01 2.078431397676467896e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.823529452085494995e-01 2.117647081613540649e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.764705926179885864e-01 2.156862765550613403e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.705882400274276733e-01 2.196078449487686157e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.647058874368667603e-01 2.235294133424758911e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.588235348463058472e-01 2.274509817361831665e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.529411822557449341e-01 2.313725501298904419e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.470588296651840210e-01 2.352941185235977173e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.411764770746231079e-01 2.392156869173049927e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.352941244840621948e-01 2.431372553110122681e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.294117718935012817e-01 2.470588237047195435e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.235294118523597717e-01 2.509804069995880127e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.176470592617988586e-01 2.549019753932952881e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.117647066712379456e-01 2.588235437870025635e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.058823540806770325e-01 2.627451121807098389e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000014901161194e-01 2.666666805744171143e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.411764889955520630e-02 2.705882489681243896e-01 1.000000000000000000e+00 -0.000000000000000000e+00 8.823529630899429321e-02 2.745098173618316650e-01 1.000000000000000000e+00 -0.000000000000000000e+00 8.235294371843338013e-02 2.784313857555389404e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.647059112787246704e-02 2.823529541492462158e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.058823853731155396e-02 2.862745225429534912e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.470588594675064087e-02 2.901960909366607666e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.882352963089942932e-02 2.941176593303680420e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.294117704033851624e-02 2.980392277240753174e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.705882444977760315e-02 3.019607961177825928e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.117647185921669006e-02 3.058823645114898682e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.529411926865577698e-02 3.098039329051971436e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.941176481544971466e-02 3.137255012989044189e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.352941222488880157e-02 3.176470696926116943e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.764705963432788849e-02 3.215686380863189697e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.176470611244440079e-02 3.254902064800262451e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.882353056222200394e-03 3.294117748737335205e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 3.333333432674407959e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.882353056222200394e-03 3.372549116611480713e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.176470611244440079e-02 3.411764800548553467e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.764705963432788849e-02 3.450980484485626221e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.352941222488880157e-02 3.490196168422698975e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.941176481544971466e-02 3.529411852359771729e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.529411926865577698e-02 3.568627536296844482e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.117647185921669006e-02 3.607843220233917236e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.705882444977760315e-02 3.647058904170989990e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.294117704033851624e-02 3.686274588108062744e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.882352963089942932e-02 3.725490272045135498e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.470588594675064087e-02 3.764705955982208252e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.058823853731155396e-02 3.803921639919281006e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.647059112787246704e-02 3.843137323856353760e-01 1.000000000000000000e+00 -0.000000000000000000e+00 8.235294371843338013e-02 3.882353007793426514e-01 1.000000000000000000e+00 -0.000000000000000000e+00 8.823529630899429321e-02 3.921568691730499268e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.411764889955520630e-02 3.960784375667572021e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000014901161194e-01 4.000000059604644775e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.058823540806770325e-01 4.039215743541717529e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.117647066712379456e-01 4.078431427478790283e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.176470592617988586e-01 4.117647111415863037e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.235294118523597717e-01 4.156862795352935791e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.294117718935012817e-01 4.196078479290008545e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.352941244840621948e-01 4.235294163227081299e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.411764770746231079e-01 4.274509847164154053e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.470588296651840210e-01 4.313725531101226807e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.529411822557449341e-01 4.352941215038299561e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.588235348463058472e-01 4.392156898975372314e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.647058874368667603e-01 4.431372582912445068e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.705882400274276733e-01 4.470588266849517822e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.764705926179885864e-01 4.509803950786590576e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.823529452085494995e-01 4.549019634723663330e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.882352977991104126e-01 4.588235318660736084e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.941176503896713257e-01 4.627451002597808838e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.000000029802322388e-01 4.666666686534881592e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.058823555707931519e-01 4.705882370471954346e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.117647081613540649e-01 4.745098054409027100e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.176470607519149780e-01 4.784313738346099854e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.235294133424758911e-01 4.823529422283172607e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.294117659330368042e-01 4.862745106220245361e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.352941185235977173e-01 4.901960790157318115e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.411764711141586304e-01 4.941176474094390869e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.470588237047195435e-01 4.980392158031463623e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.529411911964416504e-01 5.019608139991760254e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.588235437870025635e-01 5.058823823928833008e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.647058963775634766e-01 5.098039507865905762e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.705882489681243896e-01 5.137255191802978516e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.764706015586853027e-01 5.176470875740051270e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.823529541492462158e-01 5.215686559677124023e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.882353067398071289e-01 5.254902243614196777e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.941176593303680420e-01 5.294117927551269531e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.000000119209289551e-01 5.333333611488342285e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.058823645114898682e-01 5.372549295425415039e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.117647171020507812e-01 5.411764979362487793e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.176470696926116943e-01 5.450980663299560547e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.235294222831726074e-01 5.490196347236633301e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.294117748737335205e-01 5.529412031173706055e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.352941274642944336e-01 5.568627715110778809e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.411764800548553467e-01 5.607843399047851562e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.470588326454162598e-01 5.647059082984924316e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.529411852359771729e-01 5.686274766921997070e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.588235378265380859e-01 5.725490450859069824e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.647058904170989990e-01 5.764706134796142578e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.705882430076599121e-01 5.803921818733215332e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.764705955982208252e-01 5.843137502670288086e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.823529481887817383e-01 5.882353186607360840e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.882353007793426514e-01 5.921568870544433594e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.941176533699035645e-01 5.960784554481506348e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.000000059604644775e-01 6.000000238418579102e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.058823585510253906e-01 6.039215922355651855e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.117647111415863037e-01 6.078431606292724609e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.176470637321472168e-01 6.117647290229797363e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.235294163227081299e-01 6.156862974166870117e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.294117689132690430e-01 6.196078658103942871e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.352941215038299561e-01 6.235294342041015625e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.411764740943908691e-01 6.274510025978088379e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.470588266849517822e-01 6.313725709915161133e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.529411792755126953e-01 6.352941393852233887e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.588235318660736084e-01 6.392157077789306641e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.647058844566345215e-01 6.431372761726379395e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.705882370471954346e-01 6.470588445663452148e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.764705896377563477e-01 6.509804129600524902e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.823529422283172607e-01 6.549019813537597656e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.882352948188781738e-01 6.588235497474670410e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.941176474094390869e-01 6.627451181411743164e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.000000000000000000e-01 6.666666865348815918e-01 1.000000000000000000e+00 -1.176470611244440079e-02 5.058823823928833008e-01 6.705882549285888672e-01 1.000000000000000000e+00 -2.352941222488880157e-02 5.117647051811218262e-01 6.745098233222961426e-01 1.000000000000000000e+00 -3.529411926865577698e-02 5.176470875740051270e-01 6.784313917160034180e-01 1.000000000000000000e+00 -4.705882444977760315e-02 5.235294103622436523e-01 6.823529601097106934e-01 1.000000000000000000e+00 -5.882352963089942932e-02 5.294117927551269531e-01 6.862745285034179688e-01 1.000000000000000000e+00 -7.058823853731155396e-02 5.352941155433654785e-01 6.901960968971252441e-01 1.000000000000000000e+00 -8.235294371843338013e-02 5.411764979362487793e-01 6.941176652908325195e-01 1.000000000000000000e+00 -9.411764889955520630e-02 5.470588207244873047e-01 6.980392336845397949e-01 1.000000000000000000e+00 -1.058823540806770325e-01 5.529412031173706055e-01 7.019608020782470703e-01 1.000000000000000000e+00 -1.176470592617988586e-01 5.588235259056091309e-01 7.058823704719543457e-01 1.000000000000000000e+00 -1.294117718935012817e-01 5.647059082984924316e-01 7.098039388656616211e-01 1.000000000000000000e+00 -1.411764770746231079e-01 5.705882310867309570e-01 7.137255072593688965e-01 1.000000000000000000e+00 -1.529411822557449341e-01 5.764706134796142578e-01 7.176470756530761719e-01 1.000000000000000000e+00 -1.647058874368667603e-01 5.823529362678527832e-01 7.215686440467834473e-01 1.000000000000000000e+00 -1.764705926179885864e-01 5.882353186607360840e-01 7.254902124404907227e-01 1.000000000000000000e+00 -1.882352977991104126e-01 5.941176414489746094e-01 7.294117808341979980e-01 1.000000000000000000e+00 -2.000000029802322388e-01 6.000000238418579102e-01 7.333333492279052734e-01 1.000000000000000000e+00 -2.117647081613540649e-01 6.058823466300964355e-01 7.372549176216125488e-01 1.000000000000000000e+00 -2.235294133424758911e-01 6.117647290229797363e-01 7.411764860153198242e-01 1.000000000000000000e+00 -2.352941185235977173e-01 6.176470518112182617e-01 7.450980544090270996e-01 1.000000000000000000e+00 -2.470588237047195435e-01 6.235294342041015625e-01 7.490196228027343750e-01 1.000000000000000000e+00 -2.588235437870025635e-01 6.294117569923400879e-01 7.529411911964416504e-01 1.000000000000000000e+00 -2.705882489681243896e-01 6.352941393852233887e-01 7.568627595901489258e-01 1.000000000000000000e+00 -2.823529541492462158e-01 6.411764621734619141e-01 7.607843279838562012e-01 1.000000000000000000e+00 -2.941176593303680420e-01 6.470588445663452148e-01 7.647058963775634766e-01 1.000000000000000000e+00 -3.058823645114898682e-01 6.529411673545837402e-01 7.686274647712707520e-01 1.000000000000000000e+00 -3.176470696926116943e-01 6.588235497474670410e-01 7.725490331649780273e-01 1.000000000000000000e+00 -3.294117748737335205e-01 6.647058725357055664e-01 7.764706015586853027e-01 1.000000000000000000e+00 -3.411764800548553467e-01 6.705882549285888672e-01 7.803921699523925781e-01 1.000000000000000000e+00 -3.529411852359771729e-01 6.764705777168273926e-01 7.843137383460998535e-01 1.000000000000000000e+00 -3.647058904170989990e-01 6.823529601097106934e-01 7.882353067398071289e-01 1.000000000000000000e+00 -3.764705955982208252e-01 6.882352828979492188e-01 7.921568751335144043e-01 1.000000000000000000e+00 -3.882353007793426514e-01 6.941176652908325195e-01 7.960784435272216797e-01 1.000000000000000000e+00 -4.000000059604644775e-01 6.999999880790710449e-01 8.000000119209289551e-01 1.000000000000000000e+00 -4.117647111415863037e-01 7.058823704719543457e-01 8.039215803146362305e-01 1.000000000000000000e+00 -4.235294163227081299e-01 7.117646932601928711e-01 8.078431487083435059e-01 1.000000000000000000e+00 -4.352941215038299561e-01 7.176470756530761719e-01 8.117647171020507812e-01 1.000000000000000000e+00 -4.470588266849517822e-01 7.235293984413146973e-01 8.156862854957580566e-01 1.000000000000000000e+00 -4.588235318660736084e-01 7.294117808341979980e-01 8.196078538894653320e-01 1.000000000000000000e+00 -4.705882370471954346e-01 7.352941036224365234e-01 8.235294222831726074e-01 1.000000000000000000e+00 -4.823529422283172607e-01 7.411764860153198242e-01 8.274509906768798828e-01 1.000000000000000000e+00 -4.941176474094390869e-01 7.470588088035583496e-01 8.313725590705871582e-01 1.000000000000000000e+00 -5.058823823928833008e-01 7.529411911964416504e-01 8.352941274642944336e-01 1.000000000000000000e+00 -5.176470875740051270e-01 7.588235139846801758e-01 8.392156958580017090e-01 1.000000000000000000e+00 -5.294117927551269531e-01 7.647058963775634766e-01 8.431372642517089844e-01 1.000000000000000000e+00 -5.411764979362487793e-01 7.705882191658020020e-01 8.470588326454162598e-01 1.000000000000000000e+00 -5.529412031173706055e-01 7.764706015586853027e-01 8.509804010391235352e-01 1.000000000000000000e+00 -5.647059082984924316e-01 7.823529243469238281e-01 8.549019694328308105e-01 1.000000000000000000e+00 -5.764706134796142578e-01 7.882353067398071289e-01 8.588235378265380859e-01 1.000000000000000000e+00 -5.882353186607360840e-01 7.941176295280456543e-01 8.627451062202453613e-01 1.000000000000000000e+00 -6.000000238418579102e-01 8.000000119209289551e-01 8.666666746139526367e-01 1.000000000000000000e+00 -6.117647290229797363e-01 8.058823347091674805e-01 8.705882430076599121e-01 1.000000000000000000e+00 -6.235294342041015625e-01 8.117647171020507812e-01 8.745098114013671875e-01 1.000000000000000000e+00 -6.352941393852233887e-01 8.176470398902893066e-01 8.784313797950744629e-01 1.000000000000000000e+00 -6.470588445663452148e-01 8.235294222831726074e-01 8.823529481887817383e-01 1.000000000000000000e+00 -6.588235497474670410e-01 8.294117450714111328e-01 8.862745165824890137e-01 1.000000000000000000e+00 -6.705882549285888672e-01 8.352941274642944336e-01 8.901960849761962891e-01 1.000000000000000000e+00 -6.823529601097106934e-01 8.411764502525329590e-01 8.941176533699035645e-01 1.000000000000000000e+00 -6.941176652908325195e-01 8.470588326454162598e-01 8.980392217636108398e-01 1.000000000000000000e+00 -7.058823704719543457e-01 8.529411554336547852e-01 9.019607901573181152e-01 1.000000000000000000e+00 -7.176470756530761719e-01 8.588235378265380859e-01 9.058823585510253906e-01 1.000000000000000000e+00 -7.294117808341979980e-01 8.647058606147766113e-01 9.098039269447326660e-01 1.000000000000000000e+00 -7.411764860153198242e-01 8.705882430076599121e-01 9.137254953384399414e-01 1.000000000000000000e+00 -7.529411911964416504e-01 8.764705657958984375e-01 9.176470637321472168e-01 1.000000000000000000e+00 -7.647058963775634766e-01 8.823529481887817383e-01 9.215686321258544922e-01 1.000000000000000000e+00 -7.764706015586853027e-01 8.882352709770202637e-01 9.254902005195617676e-01 1.000000000000000000e+00 -7.882353067398071289e-01 8.941176533699035645e-01 9.294117689132690430e-01 1.000000000000000000e+00 -8.000000119209289551e-01 8.999999761581420898e-01 9.333333373069763184e-01 1.000000000000000000e+00 -8.117647171020507812e-01 9.058823585510253906e-01 9.372549057006835938e-01 1.000000000000000000e+00 -8.235294222831726074e-01 9.117646813392639160e-01 9.411764740943908691e-01 1.000000000000000000e+00 -8.352941274642944336e-01 9.176470637321472168e-01 9.450980424880981445e-01 1.000000000000000000e+00 -8.470588326454162598e-01 9.235293865203857422e-01 9.490196108818054199e-01 1.000000000000000000e+00 -8.588235378265380859e-01 9.294117689132690430e-01 9.529411792755126953e-01 1.000000000000000000e+00 -8.705882430076599121e-01 9.352940917015075684e-01 9.568627476692199707e-01 1.000000000000000000e+00 -8.823529481887817383e-01 9.411764740943908691e-01 9.607843160629272461e-01 1.000000000000000000e+00 -8.941176533699035645e-01 9.470587968826293945e-01 9.647058844566345215e-01 1.000000000000000000e+00 -9.058823585510253906e-01 9.529411792755126953e-01 9.686274528503417969e-01 1.000000000000000000e+00 -9.176470637321472168e-01 9.588235020637512207e-01 9.725490212440490723e-01 1.000000000000000000e+00 -9.294117689132690430e-01 9.647058844566345215e-01 9.764705896377563477e-01 1.000000000000000000e+00 -9.411764740943908691e-01 9.705882072448730469e-01 9.803921580314636230e-01 1.000000000000000000e+00 -9.529411792755126953e-01 9.764705896377563477e-01 9.843137264251708984e-01 1.000000000000000000e+00 -9.647058844566345215e-01 9.823529124259948730e-01 9.882352948188781738e-01 1.000000000000000000e+00 -9.764705896377563477e-01 9.882352948188781738e-01 9.921568632125854492e-01 1.000000000000000000e+00 -9.882352948188781738e-01 9.941176176071166992e-01 9.960784316062927246e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/pink b/fastplotlib/utils/colormaps/pink deleted file mode 100644 index bf4d1310f..000000000 --- a/fastplotlib/utils/colormaps/pink +++ /dev/null @@ -1,256 +0,0 @@ -1.177999973297119141e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.370846927165985107e-01 2.541472017765045166e-02 2.541472017765045166e-02 1.000000000000000000e+00 -1.563693732023239136e-01 5.082944035530090332e-02 5.082944035530090332e-02 1.000000000000000000e+00 -1.756540685892105103e-01 7.624416053295135498e-02 7.624416053295135498e-02 1.000000000000000000e+00 -1.949387639760971069e-01 1.016588807106018066e-01 1.016588807106018066e-01 1.000000000000000000e+00 -2.087521255016326904e-01 1.128949373960494995e-01 1.128949373960494995e-01 1.000000000000000000e+00 -2.222919464111328125e-01 1.234221234917640686e-01 1.234221234917640686e-01 1.000000000000000000e+00 -2.358317822217941284e-01 1.339493095874786377e-01 1.339493095874786377e-01 1.000000000000000000e+00 -2.493716031312942505e-01 1.444765031337738037e-01 1.444765031337738037e-01 1.000000000000000000e+00 -2.606767714023590088e-01 1.527873724699020386e-01 1.527873724699020386e-01 1.000000000000000000e+00 -2.717467546463012695e-01 1.608649641275405884e-01 1.608649641275405884e-01 1.000000000000000000e+00 -2.828167378902435303e-01 1.689425557851791382e-01 1.689425557851791382e-01 1.000000000000000000e+00 -2.938867211341857910e-01 1.770201623439788818e-01 1.770201623439788818e-01 1.000000000000000000e+00 -3.036964535713195801e-01 1.840111762285232544e-01 1.840111762285232544e-01 1.000000000000000000e+00 -3.132961690425872803e-01 1.908211112022399902e-01 1.908211112022399902e-01 1.000000000000000000e+00 -3.228959143161773682e-01 1.976310461759567261e-01 1.976310461759567261e-01 1.000000000000000000e+00 -3.324956297874450684e-01 2.044409811496734619e-01 2.044409811496734619e-01 1.000000000000000000e+00 -3.412817418575286865e-01 2.105949074029922485e-01 2.105949074029922485e-01 1.000000000000000000e+00 -3.498764336109161377e-01 2.165944874286651611e-01 2.165944874286651611e-01 1.000000000000000000e+00 -3.584711253643035889e-01 2.225940674543380737e-01 2.225940674543380737e-01 1.000000000000000000e+00 -3.670658171176910400e-01 2.285936474800109863e-01 2.285936474800109863e-01 1.000000000000000000e+00 -3.750942945480346680e-01 2.341546267271041870e-01 2.341546267271041870e-01 1.000000000000000000e+00 -3.829458057880401611e-01 2.395785599946975708e-01 2.395785599946975708e-01 1.000000000000000000e+00 -3.907973468303680420e-01 2.450025081634521484e-01 2.450025081634521484e-01 1.000000000000000000e+00 -3.986488878726959229e-01 2.504264414310455322e-01 2.504264414310455322e-01 1.000000000000000000e+00 -4.060873091220855713e-01 2.555390596389770508e-01 2.555390596389770508e-01 1.000000000000000000e+00 -4.133604764938354492e-01 2.605271935462951660e-01 2.605271935462951660e-01 1.000000000000000000e+00 -4.206336438655853271e-01 2.655152976512908936e-01 2.655152976512908936e-01 1.000000000000000000e+00 -4.279068112373352051e-01 2.705034315586090088e-01 2.705034315586090088e-01 1.000000000000000000e+00 -4.348690211772918701e-01 2.752611339092254639e-01 2.752611339092254639e-01 1.000000000000000000e+00 -4.416757225990295410e-01 2.799036204814910889e-01 2.799036204814910889e-01 1.000000000000000000e+00 -4.484824538230895996e-01 2.845461070537567139e-01 2.845461070537567139e-01 1.000000000000000000e+00 -4.552891850471496582e-01 2.891885936260223389e-01 2.891885936260223389e-01 1.000000000000000000e+00 -4.618563950061798096e-01 2.936565577983856201e-01 2.936565577983856201e-01 1.000000000000000000e+00 -4.682762324810028076e-01 2.980171442031860352e-01 2.980171442031860352e-01 1.000000000000000000e+00 -4.746960401535034180e-01 3.023777306079864502e-01 3.023777306079864502e-01 1.000000000000000000e+00 -4.811158776283264160e-01 3.067383468151092529e-01 3.067383468151092529e-01 1.000000000000000000e+00 -4.873483479022979736e-01 3.109638094902038574e-01 3.109638094902038574e-01 1.000000000000000000e+00 -4.934403300285339355e-01 3.150879740715026855e-01 3.150879740715026855e-01 1.000000000000000000e+00 -4.995323121547698975e-01 3.192121386528015137e-01 3.192121386528015137e-01 1.000000000000000000e+00 -5.056242942810058594e-01 3.233362734317779541e-01 3.233362734317779541e-01 1.000000000000000000e+00 -5.115686058998107910e-01 3.273549675941467285e-01 3.273549675941467285e-01 1.000000000000000000e+00 -5.173786878585815430e-01 3.312777578830718994e-01 3.312777578830718994e-01 1.000000000000000000e+00 -5.231887698173522949e-01 3.352005779743194580e-01 3.352005779743194580e-01 1.000000000000000000e+00 -5.289988517761230469e-01 3.391233682632446289e-01 3.391233682632446289e-01 1.000000000000000000e+00 -5.346917510032653809e-01 3.429628610610961914e-01 3.429628610610961914e-01 1.000000000000000000e+00 -5.402557849884033203e-01 3.467107713222503662e-01 3.467107713222503662e-01 1.000000000000000000e+00 -5.458198189735412598e-01 3.504586517810821533e-01 3.504586517810821533e-01 1.000000000000000000e+00 -5.513838529586791992e-01 3.542065322399139404e-01 3.542065322399139404e-01 1.000000000000000000e+00 -5.568546652793884277e-01 3.578888773918151855e-01 3.578888773918151855e-01 1.000000000000000000e+00 -5.622012615203857422e-01 3.614838123321533203e-01 3.614838123321533203e-01 1.000000000000000000e+00 -5.675478577613830566e-01 3.650787770748138428e-01 3.650787770748138428e-01 1.000000000000000000e+00 -5.728944540023803711e-01 3.686737418174743652e-01 3.686737418174743652e-01 1.000000000000000000e+00 -5.781672596931457520e-01 3.722169101238250732e-01 3.722169101238250732e-01 1.000000000000000000e+00 -5.833202004432678223e-01 3.756760060787200928e-01 3.756760060787200928e-01 1.000000000000000000e+00 -5.884730815887451172e-01 3.791350722312927246e-01 3.791350722312927246e-01 1.000000000000000000e+00 -5.936260223388671875e-01 3.825941383838653564e-01 3.825941383838653564e-01 1.000000000000000000e+00 -5.987208485603332520e-01 3.860127925872802734e-01 3.860127925872802734e-01 1.000000000000000000e+00 -6.036995649337768555e-01 3.893505334854125977e-01 3.893505334854125977e-01 1.000000000000000000e+00 -6.086783409118652344e-01 3.926883041858673096e-01 3.926883041858673096e-01 1.000000000000000000e+00 -6.136570572853088379e-01 3.960260748863220215e-01 3.960260748863220215e-01 1.000000000000000000e+00 -6.185908913612365723e-01 3.993324935436248779e-01 3.993324935436248779e-01 1.000000000000000000e+00 -6.234124898910522461e-01 4.025605618953704834e-01 4.025605618953704834e-01 1.000000000000000000e+00 -6.282340884208679199e-01 4.057886600494384766e-01 4.057886600494384766e-01 1.000000000000000000e+00 -6.330556869506835938e-01 4.090167284011840820e-01 4.090167284011840820e-01 1.000000000000000000e+00 -6.378430724143981934e-01 4.122210741043090820e-01 4.122210741043090820e-01 1.000000000000000000e+00 -6.425209045410156250e-01 4.153496026992797852e-01 4.153496026992797852e-01 1.000000000000000000e+00 -6.471986770629882812e-01 4.184781014919281006e-01 4.184781014919281006e-01 1.000000000000000000e+00 -6.518765091896057129e-01 4.216066002845764160e-01 4.216066002845764160e-01 1.000000000000000000e+00 -6.565293669700622559e-01 4.247179031372070312e-01 4.247179031372070312e-01 1.000000000000000000e+00 -6.610759496688842773e-01 4.277559816837310791e-01 4.277559816837310791e-01 1.000000000000000000e+00 -6.656225919723510742e-01 4.307940602302551270e-01 4.307940602302551270e-01 1.000000000000000000e+00 -6.701692342758178711e-01 4.338321387767791748e-01 4.338321387767791748e-01 1.000000000000000000e+00 -6.746985912322998047e-01 4.368582963943481445e-01 4.368582963943481445e-01 1.000000000000000000e+00 -6.791244149208068848e-01 4.398128688335418701e-01 4.398128688335418701e-01 1.000000000000000000e+00 -6.835502386093139648e-01 4.427674710750579834e-01 4.427674710750579834e-01 1.000000000000000000e+00 -6.879760026931762695e-01 4.457220435142517090e-01 4.457220435142517090e-01 1.000000000000000000e+00 -6.923911571502685547e-01 4.486693143844604492e-01 4.486693143844604492e-01 1.000000000000000000e+00 -6.967051029205322266e-01 4.515472948551177979e-01 4.515472948551177979e-01 1.000000000000000000e+00 -7.010189890861511230e-01 4.544253051280975342e-01 4.544253051280975342e-01 1.000000000000000000e+00 -7.053328752517700195e-01 4.573032855987548828e-01 4.573032855987548828e-01 1.000000000000000000e+00 -7.096418142318725586e-01 4.601778984069824219e-01 4.601778984069824219e-01 1.000000000000000000e+00 -7.138522267341613770e-01 4.629847109317779541e-01 4.629847109317779541e-01 1.000000000000000000e+00 -7.180625796318054199e-01 4.657915532588958740e-01 4.657915532588958740e-01 1.000000000000000000e+00 -7.222729921340942383e-01 4.685983955860137939e-01 4.685983955860137939e-01 1.000000000000000000e+00 -7.264833450317382812e-01 4.714052379131317139e-01 4.714052379131317139e-01 1.000000000000000000e+00 -7.305971384048461914e-01 4.741458594799041748e-01 4.741458594799041748e-01 1.000000000000000000e+00 -7.347109317779541016e-01 4.768864810466766357e-01 4.768864810466766357e-01 1.000000000000000000e+00 -7.388246655464172363e-01 4.796271026134490967e-01 4.796271026134490967e-01 1.000000000000000000e+00 -7.429384589195251465e-01 4.823677241802215576e-01 4.823677241802215576e-01 1.000000000000000000e+00 -7.469666004180908203e-01 4.850497841835021973e-01 4.850497841835021973e-01 1.000000000000000000e+00 -7.509904503822326660e-01 4.877288937568664551e-01 4.877288937568664551e-01 1.000000000000000000e+00 -7.550143003463745117e-01 4.904079735279083252e-01 4.904079735279083252e-01 1.000000000000000000e+00 -7.590381503105163574e-01 4.930870831012725830e-01 4.930870831012725830e-01 1.000000000000000000e+00 -7.609713077545166016e-01 4.987535476684570312e-01 4.957141280174255371e-01 1.000000000000000000e+00 -7.626847028732299805e-01 5.047340989112854004e-01 4.983356595039367676e-01 1.000000000000000000e+00 -7.643980383872985840e-01 5.107146501541137695e-01 5.009571909904479980e-01 1.000000000000000000e+00 -7.661113739013671875e-01 5.166952013969421387e-01 5.035787224769592285e-01 1.000000000000000000e+00 -7.678115963935852051e-01 5.224466323852539062e-01 5.061537027359008789e-01 1.000000000000000000e+00 -7.695096731185913086e-01 5.281598567962646484e-01 5.087208747863769531e-01 1.000000000000000000e+00 -7.712076902389526367e-01 5.338730812072753906e-01 5.112881064414978027e-01 1.000000000000000000e+00 -7.729057073593139648e-01 5.395863652229309082e-01 5.138552784919738770e-01 1.000000000000000000e+00 -7.745917439460754395e-01 5.451095700263977051e-01 5.163816809654235840e-01 1.000000000000000000e+00 -7.762749791145324707e-01 5.505880713462829590e-01 5.188984274864196777e-01 1.000000000000000000e+00 -7.779582142829895020e-01 5.560666322708129883e-01 5.214152336120605469e-01 1.000000000000000000e+00 -7.796413898468017578e-01 5.615451931953430176e-01 5.239320397377014160e-01 1.000000000000000000e+00 -7.813135385513305664e-01 5.668653845787048340e-01 5.264118909835815430e-01 1.000000000000000000e+00 -7.829821705818176270e-01 5.721361041069030762e-01 5.288802981376647949e-01 1.000000000000000000e+00 -7.846508026123046875e-01 5.774068832397460938e-01 5.313486456871032715e-01 1.000000000000000000e+00 -7.863194346427917480e-01 5.826776623725891113e-01 5.338169932365417480e-01 1.000000000000000000e+00 -7.879778146743774414e-01 5.878157019615173340e-01 5.362532734870910645e-01 1.000000000000000000e+00 -7.896321415901184082e-01 5.929006338119506836e-01 5.386766791343688965e-01 1.000000000000000000e+00 -7.912864685058593750e-01 5.979856252670288086e-01 5.411000847816467285e-01 1.000000000000000000e+00 -7.929407358169555664e-01 6.030705571174621582e-01 5.435234904289245605e-01 1.000000000000000000e+00 -7.945858240127563477e-01 6.080438494682312012e-01 5.459182262420654297e-01 1.000000000000000000e+00 -7.962263226509094238e-01 6.129613518714904785e-01 5.482985973358154297e-01 1.000000000000000000e+00 -7.978667616844177246e-01 6.178787946701049805e-01 5.506790280342102051e-01 1.000000000000000000e+00 -7.995072603225708008e-01 6.227962374687194824e-01 5.530594587326049805e-01 1.000000000000000000e+00 -8.011394739151000977e-01 6.276196241378784180e-01 5.554146170616149902e-01 1.000000000000000000e+00 -8.027666211128234863e-01 6.323851943016052246e-01 5.577542781829833984e-01 1.000000000000000000e+00 -8.043937087059020996e-01 6.371507048606872559e-01 5.600939393043518066e-01 1.000000000000000000e+00 -8.060208559036254883e-01 6.419162154197692871e-01 5.624336004257202148e-01 1.000000000000000000e+00 -8.076403737068176270e-01 6.466026306152343750e-01 5.647512078285217285e-01 1.000000000000000000e+00 -8.092541694641113281e-01 6.512297987937927246e-01 5.670523047447204590e-01 1.000000000000000000e+00 -8.108679652214050293e-01 6.558570265769958496e-01 5.693534016609191895e-01 1.000000000000000000e+00 -8.124817013740539551e-01 6.604841947555541992e-01 5.716544985771179199e-01 1.000000000000000000e+00 -8.140887618064880371e-01 6.650443673133850098e-01 5.739361643791198730e-01 1.000000000000000000e+00 -8.156895637512207031e-01 6.695438027381896973e-01 5.762000679969787598e-01 1.000000000000000000e+00 -8.172904253005981445e-01 6.740431785583496094e-01 5.784639716148376465e-01 1.000000000000000000e+00 -8.188912868499755859e-01 6.785426139831542969e-01 5.807278752326965332e-01 1.000000000000000000e+00 -8.204862475395202637e-01 6.829862594604492188e-01 5.829752683639526367e-01 1.000000000000000000e+00 -8.220748901367187500e-01 6.873686313629150391e-01 5.852044820785522461e-01 1.000000000000000000e+00 -8.236634731292724609e-01 6.917509436607360840e-01 5.874336957931518555e-01 1.000000000000000000e+00 -8.252520561218261719e-01 6.961332559585571289e-01 5.896629095077514648e-01 1.000000000000000000e+00 -8.268352150917053223e-01 7.004691362380981445e-01 5.918776988983154297e-01 1.000000000000000000e+00 -8.284112215042114258e-01 7.047430276870727539e-01 5.940732955932617188e-01 1.000000000000000000e+00 -8.299872279167175293e-01 7.090169191360473633e-01 5.962689518928527832e-01 1.000000000000000000e+00 -8.315631747245788574e-01 7.132907509803771973e-01 5.984645485877990723e-01 1.000000000000000000e+00 -8.331346511840820312e-01 7.175262570381164551e-01 6.006479263305664062e-01 1.000000000000000000e+00 -8.346987962722778320e-01 7.216993570327758789e-01 6.028114557266235352e-01 1.000000000000000000e+00 -8.362629413604736328e-01 7.258723974227905273e-01 6.049749255180358887e-01 1.000000000000000000e+00 -8.378270864486694336e-01 7.300454974174499512e-01 6.071383953094482422e-01 1.000000000000000000e+00 -8.393873572349548340e-01 7.341871857643127441e-01 6.092916131019592285e-01 1.000000000000000000e+00 -8.409398794174194336e-01 7.382661104202270508e-01 6.114242672920227051e-01 1.000000000000000000e+00 -8.424923419952392578e-01 7.423450946807861328e-01 6.135568618774414062e-01 1.000000000000000000e+00 -8.440448641777038574e-01 7.464240193367004395e-01 6.156894564628601074e-01 1.000000000000000000e+00 -8.455941677093505859e-01 7.504779100418090820e-01 6.178137660026550293e-01 1.000000000000000000e+00 -8.471353054046630859e-01 7.544691562652587891e-01 6.199172139167785645e-01 1.000000000000000000e+00 -8.486764430999755859e-01 7.584604024887084961e-01 6.220206618309020996e-01 1.000000000000000000e+00 -8.502176403999328613e-01 7.624516487121582031e-01 6.241241693496704102e-01 1.000000000000000000e+00 -8.517560958862304688e-01 7.664231657981872559e-01 6.262208223342895508e-01 1.000000000000000000e+00 -8.532858490943908691e-01 7.703316807746887207e-01 6.282958984375000000e-01 1.000000000000000000e+00 -8.548156619071960449e-01 7.742401361465454102e-01 6.303709149360656738e-01 1.000000000000000000e+00 -8.563454151153564453e-01 7.781485915184020996e-01 6.324459910392761230e-01 1.000000000000000000e+00 -8.578731417655944824e-01 7.820423245429992676e-01 6.345158815383911133e-01 1.000000000000000000e+00 -8.593918085098266602e-01 7.858732342720031738e-01 6.365640163421630859e-01 1.000000000000000000e+00 -8.609104752540588379e-01 7.897040843963623047e-01 6.386121511459350586e-01 1.000000000000000000e+00 -8.624291419982910156e-01 7.935349941253662109e-01 6.406602859497070312e-01 1.000000000000000000e+00 -8.639463186264038086e-01 7.973554730415344238e-01 6.427046656608581543e-01 1.000000000000000000e+00 -8.654543161392211914e-01 8.011132478713989258e-01 6.447265744209289551e-01 1.000000000000000000e+00 -8.669623732566833496e-01 8.048710227012634277e-01 6.467485427856445312e-01 1.000000000000000000e+00 -8.684704303741455078e-01 8.086287379264831543e-01 6.487704515457153320e-01 1.000000000000000000e+00 -8.699774742126464844e-01 8.123799562454223633e-01 6.507899761199951172e-01 1.000000000000000000e+00 -8.714751601219177246e-01 8.160688281059265137e-01 6.527867317199707031e-01 1.000000000000000000e+00 -8.729728460311889648e-01 8.197576403617858887e-01 6.547834277153015137e-01 1.000000000000000000e+00 -8.744705319404602051e-01 8.234465122222900391e-01 6.567801833152770996e-01 1.000000000000000000e+00 -8.759676814079284668e-01 8.271322250366210938e-01 6.587757468223571777e-01 1.000000000000000000e+00 -8.774549961090087891e-01 8.307553529739379883e-01 6.607485413551330566e-01 1.000000000000000000e+00 -8.789423108100891113e-01 8.343784213066101074e-01 6.627212762832641602e-01 1.000000000000000000e+00 -8.804295659065246582e-01 8.380015492439270020e-01 6.646940708160400391e-01 1.000000000000000000e+00 -8.819168806076049805e-01 8.416246771812438965e-01 6.666668057441711426e-01 1.000000000000000000e+00 -8.833940625190734863e-01 8.451860547065734863e-01 6.686158776283264160e-01 1.000000000000000000e+00 -8.848711848258972168e-01 8.487474322319030762e-01 6.705649495124816895e-01 1.000000000000000000e+00 -8.863483667373657227e-01 8.523087501525878906e-01 6.725139617919921875e-01 1.000000000000000000e+00 -8.878255486488342285e-01 8.558701276779174805e-01 6.744630336761474609e-01 1.000000000000000000e+00 -8.892933130264282227e-01 8.593752384185791016e-01 6.763908863067626953e-01 1.000000000000000000e+00 -8.907606005668640137e-01 8.628775477409362793e-01 6.783177256584167480e-01 1.000000000000000000e+00 -8.922278881072998047e-01 8.663798570632934570e-01 6.802445054054260254e-01 1.000000000000000000e+00 -8.936951160430908203e-01 8.698821663856506348e-01 6.821713447570800781e-01 1.000000000000000000e+00 -8.951537013053894043e-01 8.733335137367248535e-01 6.840785145759582520e-01 1.000000000000000000e+00 -8.966113328933715820e-01 8.767794966697692871e-01 6.859835386276245117e-01 1.000000000000000000e+00 -8.980690240859985352e-01 8.802254796028137207e-01 6.878886222839355469e-01 1.000000000000000000e+00 -8.995266556739807129e-01 8.836714625358581543e-01 6.897937059402465820e-01 1.000000000000000000e+00 -9.009762406349182129e-01 8.870717287063598633e-01 6.916805505752563477e-01 1.000000000000000000e+00 -9.024245142936706543e-01 8.904643058776855469e-01 6.935644149780273438e-01 1.000000000000000000e+00 -9.038727879524230957e-01 8.938569426536560059e-01 6.954482197761535645e-01 1.000000000000000000e+00 -9.053210616111755371e-01 8.972495794296264648e-01 6.973320245742797852e-01 1.000000000000000000e+00 -9.067617058753967285e-01 9.006007909774780273e-01 6.991994976997375488e-01 1.000000000000000000e+00 -9.082005620002746582e-01 9.039422273635864258e-01 7.010630369186401367e-01 1.000000000000000000e+00 -9.096394181251525879e-01 9.072837233543395996e-01 7.029266357421875000e-01 1.000000000000000000e+00 -9.110783338546752930e-01 9.106252193450927734e-01 7.047901749610900879e-01 1.000000000000000000e+00 -9.125102162361145020e-01 9.125102162361145020e-01 7.093620300292968750e-01 1.000000000000000000e+00 -9.139399528503417969e-01 9.139399528503417969e-01 7.147805094718933105e-01 1.000000000000000000e+00 -9.153696894645690918e-01 9.153696894645690918e-01 7.201990485191345215e-01 1.000000000000000000e+00 -9.167994260787963867e-01 9.167994260787963867e-01 7.256175279617309570e-01 1.000000000000000000e+00 -9.182226061820983887e-01 9.182226061820983887e-01 7.309225797653198242e-01 1.000000000000000000e+00 -9.196432232856750488e-01 9.196432232856750488e-01 7.361822128295898438e-01 1.000000000000000000e+00 -9.210637807846069336e-01 9.210637807846069336e-01 7.414418458938598633e-01 1.000000000000000000e+00 -9.224843978881835938e-01 9.224843978881835938e-01 7.467014789581298828e-01 1.000000000000000000e+00 -9.238992333412170410e-01 9.238992333412170410e-01 7.518643140792846680e-01 1.000000000000000000e+00 -9.253111481666564941e-01 9.253111481666564941e-01 7.569786310195922852e-01 1.000000000000000000e+00 -9.267231225967407227e-01 9.267231225967407227e-01 7.620930075645446777e-01 1.000000000000000000e+00 -9.281350374221801758e-01 9.281350374221801758e-01 7.672073841094970703e-01 1.000000000000000000e+00 -9.295416474342346191e-01 9.295416474342346191e-01 7.722387313842773438e-01 1.000000000000000000e+00 -9.309449195861816406e-01 9.309449195861816406e-01 7.772189378738403320e-01 1.000000000000000000e+00 -9.323482513427734375e-01 9.323482513427734375e-01 7.821991443634033203e-01 1.000000000000000000e+00 -9.337515234947204590e-01 9.337515234947204590e-01 7.871793508529663086e-01 1.000000000000000000e+00 -9.351500272750854492e-01 9.351500272750854492e-01 7.920885682106018066e-01 1.000000000000000000e+00 -9.365448951721191406e-01 9.365448951721191406e-01 7.969445586204528809e-01 1.000000000000000000e+00 -9.379398226737976074e-01 9.379398226737976074e-01 8.018004894256591797e-01 1.000000000000000000e+00 -9.393346905708312988e-01 9.393346905708312988e-01 8.066564202308654785e-01 1.000000000000000000e+00 -9.407252073287963867e-01 9.407252073287963867e-01 8.114522099494934082e-01 1.000000000000000000e+00 -9.421116709709167480e-01 9.421116709709167480e-01 8.161932826042175293e-01 1.000000000000000000e+00 -9.434981942176818848e-01 9.434981942176818848e-01 8.209343552589416504e-01 1.000000000000000000e+00 -9.448846578598022461e-01 9.448846578598022461e-01 8.256753683090209961e-01 1.000000000000000000e+00 -9.462673068046569824e-01 9.462673068046569824e-01 8.303651809692382812e-01 1.000000000000000000e+00 -9.476456642150878906e-01 9.476456642150878906e-01 8.349984884262084961e-01 1.000000000000000000e+00 -9.490239620208740234e-01 9.490239620208740234e-01 8.396318554878234863e-01 1.000000000000000000e+00 -9.504023194313049316e-01 9.504023194313049316e-01 8.442652225494384766e-01 1.000000000000000000e+00 -9.517771601676940918e-01 9.517771601676940918e-01 8.488555550575256348e-01 1.000000000000000000e+00 -9.531473517417907715e-01 9.531473517417907715e-01 8.533886075019836426e-01 1.000000000000000000e+00 -9.545175433158874512e-01 9.545175433158874512e-01 8.579216599464416504e-01 1.000000000000000000e+00 -9.558877348899841309e-01 9.558877348899841309e-01 8.624547123908996582e-01 1.000000000000000000e+00 -9.572549462318420410e-01 9.572549462318420410e-01 8.669519424438476562e-01 1.000000000000000000e+00 -9.586172103881835938e-01 9.586172103881835938e-01 8.713911175727844238e-01 1.000000000000000000e+00 -9.599794745445251465e-01 9.599794745445251465e-01 8.758302927017211914e-01 1.000000000000000000e+00 -9.613417983055114746e-01 9.613417983055114746e-01 8.802694082260131836e-01 1.000000000000000000e+00 -9.627014994621276855e-01 9.627014994621276855e-01 8.846789598464965820e-01 1.000000000000000000e+00 -9.640561342239379883e-01 9.640561342239379883e-01 8.890291452407836914e-01 1.000000000000000000e+00 -9.654107689857482910e-01 9.654107689857482910e-01 8.933793902397155762e-01 1.000000000000000000e+00 -9.667654037475585938e-01 9.667654037475585938e-01 8.977295756340026855e-01 1.000000000000000000e+00 -9.681178331375122070e-01 9.681178331375122070e-01 9.020560979843139648e-01 1.000000000000000000e+00 -9.694647789001464844e-01 9.694647789001464844e-01 9.063233137130737305e-01 1.000000000000000000e+00 -9.708117246627807617e-01 9.708117246627807617e-01 9.105905294418334961e-01 1.000000000000000000e+00 -9.721587300300598145e-01 9.721587300300598145e-01 9.148576855659484863e-01 1.000000000000000000e+00 -9.735038876533508301e-01 9.735038876533508301e-01 9.191060662269592285e-01 1.000000000000000000e+00 -9.748434424400329590e-01 9.748434424400329590e-01 9.232942461967468262e-01 1.000000000000000000e+00 -9.761829972267150879e-01 9.761829972267150879e-01 9.274823665618896484e-01 1.000000000000000000e+00 -9.775225520133972168e-01 9.775225520133972168e-01 9.316705465316772461e-01 1.000000000000000000e+00 -9.788607358932495117e-01 9.788607358932495117e-01 9.358444809913635254e-01 1.000000000000000000e+00 -9.801928400993347168e-01 9.801928400993347168e-01 9.399579763412475586e-01 1.000000000000000000e+00 -9.815250039100646973e-01 9.815250039100646973e-01 9.440715312957763672e-01 1.000000000000000000e+00 -9.828571677207946777e-01 9.828571677207946777e-01 9.481850862503051758e-01 1.000000000000000000e+00 -9.841882586479187012e-01 9.841882586479187012e-01 9.522885084152221680e-01 1.000000000000000000e+00 -9.855129718780517578e-01 9.855129718780517578e-01 9.563313722610473633e-01 1.000000000000000000e+00 -9.868376851081848145e-01 9.868376851081848145e-01 9.603742361068725586e-01 1.000000000000000000e+00 -9.881623983383178711e-01 9.881623983383178711e-01 9.644170999526977539e-01 1.000000000000000000e+00 -9.894865155220031738e-01 9.894865155220031738e-01 9.684535861015319824e-01 1.000000000000000000e+00 -9.908043146133422852e-01 9.908043146133422852e-01 9.724292755126953125e-01 1.000000000000000000e+00 -9.921221137046813965e-01 9.921221137046813965e-01 9.764049649238586426e-01 1.000000000000000000e+00 -9.934399127960205078e-01 9.934399127960205078e-01 9.803806543350219727e-01 1.000000000000000000e+00 -9.947574138641357422e-01 9.947574138641357422e-01 9.843532443046569824e-01 1.000000000000000000e+00 -9.960680603981018066e-01 9.960680603981018066e-01 9.882649183273315430e-01 1.000000000000000000e+00 -9.973787069320678711e-01 9.973787069320678711e-01 9.921766519546508789e-01 1.000000000000000000e+00 -9.986893534660339355e-01 9.986893534660339355e-01 9.960883259773254395e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/plasma b/fastplotlib/utils/colormaps/plasma deleted file mode 100644 index 3675e89ce..000000000 --- a/fastplotlib/utils/colormaps/plasma +++ /dev/null @@ -1,256 +0,0 @@ -5.038300156593322754e-02 2.980300039052963257e-02 5.279750227928161621e-01 1.000000000000000000e+00 -6.353600323200225830e-02 2.842600084841251373e-02 5.331240296363830566e-01 1.000000000000000000e+00 -7.535299658775329590e-02 2.720599994063377380e-02 5.380070209503173828e-01 1.000000000000000000e+00 -8.622200042009353638e-02 2.612500078976154327e-02 5.426579713821411133e-01 1.000000000000000000e+00 -9.637899696826934814e-02 2.516500093042850494e-02 5.471029877662658691e-01 1.000000000000000000e+00 -1.059800013899803162e-01 2.430900000035762787e-02 5.513679981231689453e-01 1.000000000000000000e+00 -1.151240020990371704e-01 2.355599962174892426e-02 5.554680228233337402e-01 1.000000000000000000e+00 -1.239029988646507263e-01 2.287800051271915436e-02 5.594230294227600098e-01 1.000000000000000000e+00 -1.323810070753097534e-01 2.225800044834613800e-02 5.632500052452087402e-01 1.000000000000000000e+00 -1.406030058860778809e-01 2.168699912726879120e-02 5.669590234756469727e-01 1.000000000000000000e+00 -1.486070007085800171e-01 2.115399949252605438e-02 5.705620050430297852e-01 1.000000000000000000e+00 -1.564210057258605957e-01 2.065099962055683136e-02 5.740650296211242676e-01 1.000000000000000000e+00 -1.640699952840805054e-01 2.017099969089031219e-02 5.774779915809631348e-01 1.000000000000000000e+00 -1.715739965438842773e-01 1.970599964261054993e-02 5.808060169219970703e-01 1.000000000000000000e+00 -1.789499968290328979e-01 1.925200037658214569e-02 5.840539932250976562e-01 1.000000000000000000e+00 -1.862130016088485718e-01 1.880300045013427734e-02 5.872280001640319824e-01 1.000000000000000000e+00 -1.933739930391311646e-01 1.835400052368640900e-02 5.903300046920776367e-01 1.000000000000000000e+00 -2.004449963569641113e-01 1.790199987590312958e-02 5.933640003204345703e-01 1.000000000000000000e+00 -2.074349969625473022e-01 1.744199916720390320e-02 5.963330268859863281e-01 1.000000000000000000e+00 -2.143500000238418579e-01 1.697300001978874207e-02 5.992389917373657227e-01 1.000000000000000000e+00 -2.211969941854476929e-01 1.649699918925762177e-02 6.020830273628234863e-01 1.000000000000000000e+00 -2.279829978942871094e-01 1.600700058043003082e-02 6.048669815063476562e-01 1.000000000000000000e+00 -2.347149997949600220e-01 1.550200022757053375e-02 6.075919866561889648e-01 1.000000000000000000e+00 -2.413959950208663940e-01 1.497900020331144333e-02 6.102589964866638184e-01 1.000000000000000000e+00 -2.480320036411285400e-01 1.443899981677532196e-02 6.128680109977722168e-01 1.000000000000000000e+00 -2.546269893646240234e-01 1.388199999928474426e-02 6.154189705848693848e-01 1.000000000000000000e+00 -2.611829936504364014e-01 1.330799981951713562e-02 6.179109811782836914e-01 1.000000000000000000e+00 -2.677029967308044434e-01 1.271599996834993362e-02 6.203460097312927246e-01 1.000000000000000000e+00 -2.741909921169281006e-01 1.210900023579597473e-02 6.227220296859741211e-01 1.000000000000000000e+00 -2.806479930877685547e-01 1.148799993097782135e-02 6.250380277633666992e-01 1.000000000000000000e+00 -2.870759963989257812e-01 1.085499953478574753e-02 6.272950172424316406e-01 1.000000000000000000e+00 -2.934780120849609375e-01 1.021299976855516434e-02 6.294900178909301758e-01 1.000000000000000000e+00 -2.998549938201904297e-01 9.561000391840934753e-03 6.316239833831787109e-01 1.000000000000000000e+00 -3.062100112438201904e-01 8.902000263333320618e-03 6.336939930915832520e-01 1.000000000000000000e+00 -3.125430047512054443e-01 8.239000104367733002e-03 6.356999874114990234e-01 1.000000000000000000e+00 -3.188560009002685547e-01 7.575999945402145386e-03 6.376399993896484375e-01 1.000000000000000000e+00 -3.251500129699707031e-01 6.914999801665544510e-03 6.395120024681091309e-01 1.000000000000000000e+00 -3.314259946346282959e-01 6.260999944061040878e-03 6.413159966468811035e-01 1.000000000000000000e+00 -3.376829922199249268e-01 5.617999937385320663e-03 6.430490016937255859e-01 1.000000000000000000e+00 -3.439249992370605469e-01 4.991000052541494370e-03 6.447100043296813965e-01 1.000000000000000000e+00 -3.501499891281127930e-01 4.381999839097261429e-03 6.462979912757873535e-01 1.000000000000000000e+00 -3.563590049743652344e-01 3.798000048846006393e-03 6.478099822998046875e-01 1.000000000000000000e+00 -3.625530004501342773e-01 3.243000013753771782e-03 6.492450237274169922e-01 1.000000000000000000e+00 -3.687329888343811035e-01 2.724000019952654839e-03 6.506010293960571289e-01 1.000000000000000000e+00 -3.748970031738281250e-01 2.245000097900629044e-03 6.518759727478027344e-01 1.000000000000000000e+00 -3.810470104217529297e-01 1.813999959267675877e-03 6.530680060386657715e-01 1.000000000000000000e+00 -3.871830105781555176e-01 1.433999976143240929e-03 6.541770100593566895e-01 1.000000000000000000e+00 -3.933039903640747070e-01 1.113999984227120876e-03 6.551989912986755371e-01 1.000000000000000000e+00 -3.994109928607940674e-01 8.590000215917825699e-04 6.561329960823059082e-01 1.000000000000000000e+00 -4.055030047893524170e-01 6.779999821446835995e-04 6.569769978523254395e-01 1.000000000000000000e+00 -4.115799963474273682e-01 5.770000279881060123e-04 6.577299833297729492e-01 1.000000000000000000e+00 -4.176419973373413086e-01 5.639999872073531151e-04 6.583899855613708496e-01 1.000000000000000000e+00 -4.236890077590942383e-01 6.459999713115394115e-04 6.589559912681579590e-01 1.000000000000000000e+00 -4.297190010547637939e-01 8.309999830089509487e-04 6.594250202178955078e-01 1.000000000000000000e+00 -4.357340037822723389e-01 1.126999966800212860e-03 6.597970128059387207e-01 1.000000000000000000e+00 -4.417319893836975098e-01 1.539999968372285366e-03 6.600689888000488281e-01 1.000000000000000000e+00 -4.477140009403228760e-01 2.080000005662441254e-03 6.602399945259094238e-01 1.000000000000000000e+00 -4.536769986152648926e-01 2.755000023171305656e-03 6.603099703788757324e-01 1.000000000000000000e+00 -4.596230089664459229e-01 3.573999973013997078e-03 6.602770090103149414e-01 1.000000000000000000e+00 -4.655500054359436035e-01 4.544999916106462479e-03 6.601390242576599121e-01 1.000000000000000000e+00 -4.714570045471191406e-01 5.677999928593635559e-03 6.598970293998718262e-01 1.000000000000000000e+00 -4.773440062999725342e-01 6.980000063776969910e-03 6.595489978790283203e-01 1.000000000000000000e+00 -4.832099974155426025e-01 8.460000157356262207e-03 6.590949892997741699e-01 1.000000000000000000e+00 -4.890550076961517334e-01 1.012699957937002182e-02 6.585339903831481934e-01 1.000000000000000000e+00 -4.948770105838775635e-01 1.198999956250190735e-02 6.578649878501892090e-01 1.000000000000000000e+00 -5.006780028343200684e-01 1.405499968677759171e-02 6.570879817008972168e-01 1.000000000000000000e+00 -5.064539909362792969e-01 1.633300073444843292e-02 6.562020182609558105e-01 1.000000000000000000e+00 -5.122060179710388184e-01 1.883300021290779114e-02 6.552090048789978027e-01 1.000000000000000000e+00 -5.179330110549926758e-01 2.156299911439418793e-02 6.541090011596679688e-01 1.000000000000000000e+00 -5.236330032348632812e-01 2.453199960291385651e-02 6.529009938240051270e-01 1.000000000000000000e+00 -5.293059945106506348e-01 2.774699963629245758e-02 6.515859961509704590e-01 1.000000000000000000e+00 -5.349519848823547363e-01 3.121699951589107513e-02 6.501650214195251465e-01 1.000000000000000000e+00 -5.405700206756591797e-01 3.494999930262565613e-02 6.486399769783020020e-01 1.000000000000000000e+00 -5.461570024490356445e-01 3.895400092005729675e-02 6.470100283622741699e-01 1.000000000000000000e+00 -5.517150163650512695e-01 4.313600063323974609e-02 6.452770233154296875e-01 1.000000000000000000e+00 -5.572429895401000977e-01 4.733100160956382751e-02 6.434429883956909180e-01 1.000000000000000000e+00 -5.627380013465881348e-01 5.154500156641006470e-02 6.415089964866638184e-01 1.000000000000000000e+00 -5.682010054588317871e-01 5.577800050377845764e-02 6.394770145416259766e-01 1.000000000000000000e+00 -5.736320018768310547e-01 6.002800166606903076e-02 6.373490095138549805e-01 1.000000000000000000e+00 -5.790290236473083496e-01 6.429599970579147339e-02 6.351259946823120117e-01 1.000000000000000000e+00 -5.843909978866577148e-01 6.857900321483612061e-02 6.328120231628417969e-01 1.000000000000000000e+00 -5.897189974784851074e-01 7.287800312042236328e-02 6.304079890251159668e-01 1.000000000000000000e+00 -5.950109958648681641e-01 7.718999683856964111e-02 6.279169917106628418e-01 1.000000000000000000e+00 -6.002659797668457031e-01 8.151599764823913574e-02 6.253420114517211914e-01 1.000000000000000000e+00 -6.054850220680236816e-01 8.585400134325027466e-02 6.226860284805297852e-01 1.000000000000000000e+00 -6.106669902801513672e-01 9.020400047302246094e-02 6.199510097503662109e-01 1.000000000000000000e+00 -6.158120036125183105e-01 9.456399828195571899e-02 6.171399950981140137e-01 1.000000000000000000e+00 -6.209189891815185547e-01 9.893400222063064575e-02 6.142569780349731445e-01 1.000000000000000000e+00 -6.259869933128356934e-01 1.033120006322860718e-01 6.113049983978271484e-01 1.000000000000000000e+00 -6.310170292854309082e-01 1.076989993453025818e-01 6.082869768142700195e-01 1.000000000000000000e+00 -6.360080242156982422e-01 1.120920032262802124e-01 6.052049994468688965e-01 1.000000000000000000e+00 -6.409590244293212891e-01 1.164920032024383545e-01 6.020650267601013184e-01 1.000000000000000000e+00 -6.458719968795776367e-01 1.208980008959770203e-01 5.988669991493225098e-01 1.000000000000000000e+00 -6.507459878921508789e-01 1.253090053796768188e-01 5.956169962882995605e-01 1.000000000000000000e+00 -6.555799841880798340e-01 1.297249943017959595e-01 5.923169851303100586e-01 1.000000000000000000e+00 -6.603739857673645020e-01 1.341439932584762573e-01 5.889710187911987305e-01 1.000000000000000000e+00 -6.651290059089660645e-01 1.385660022497177124e-01 5.855820178985595703e-01 1.000000000000000000e+00 -6.698449850082397461e-01 1.429920047521591187e-01 5.821539759635925293e-01 1.000000000000000000e+00 -6.745219826698303223e-01 1.474190056324005127e-01 5.786880254745483398e-01 1.000000000000000000e+00 -6.791599988937377930e-01 1.518480032682418823e-01 5.751889944076538086e-01 1.000000000000000000e+00 -6.837580204010009766e-01 1.562779992818832397e-01 5.716599822044372559e-01 1.000000000000000000e+00 -6.883180141448974609e-01 1.607089936733245850e-01 5.681030154228210449e-01 1.000000000000000000e+00 -6.928399801254272461e-01 1.651410013437271118e-01 5.645220279693603516e-01 1.000000000000000000e+00 -6.973239779472351074e-01 1.695729941129684448e-01 5.609189867973327637e-01 1.000000000000000000e+00 -7.017689943313598633e-01 1.740050017833709717e-01 5.572959780693054199e-01 1.000000000000000000e+00 -7.061780095100402832e-01 1.784369945526123047e-01 5.536569952964782715e-01 1.000000000000000000e+00 -7.105489969253540039e-01 1.828680038452148438e-01 5.500040054321289062e-01 1.000000000000000000e+00 -7.148830294609069824e-01 1.872989982366561890e-01 5.463380217552185059e-01 1.000000000000000000e+00 -7.191810011863708496e-01 1.917289942502975464e-01 5.426629781723022461e-01 1.000000000000000000e+00 -7.234439849853515625e-01 1.961580067873001099e-01 5.389810204505920410e-01 1.000000000000000000e+00 -7.276700139045715332e-01 2.005860060453414917e-01 5.352929830551147461e-01 1.000000000000000000e+00 -7.318620085716247559e-01 2.050130069255828857e-01 5.316010117530822754e-01 1.000000000000000000e+00 -7.360190153121948242e-01 2.094389945268630981e-01 5.279080271720886230e-01 1.000000000000000000e+00 -7.401430010795593262e-01 2.138639986515045166e-01 5.242159962654113770e-01 1.000000000000000000e+00 -7.442319989204406738e-01 2.182880043983459473e-01 5.205240249633789062e-01 1.000000000000000000e+00 -7.482889890670776367e-01 2.227109968662261963e-01 5.168340206146240234e-01 1.000000000000000000e+00 -7.523120045661926270e-01 2.271330058574676514e-01 5.131490230560302734e-01 1.000000000000000000e+00 -7.563040256500244141e-01 2.315549999475479126e-01 5.094680190086364746e-01 1.000000000000000000e+00 -7.602639794349670410e-01 2.359759956598281860e-01 5.057939887046813965e-01 1.000000000000000000e+00 -7.641929984092712402e-01 2.403959929943084717e-01 5.021259784698486328e-01 1.000000000000000000e+00 -7.680900096893310547e-01 2.448170036077499390e-01 4.984650015830993652e-01 1.000000000000000000e+00 -7.719579935073852539e-01 2.492370009422302246e-01 4.948129951953887939e-01 1.000000000000000000e+00 -7.757959961891174316e-01 2.536579966545104980e-01 4.911710023880004883e-01 1.000000000000000000e+00 -7.796040177345275879e-01 2.580780088901519775e-01 4.875389933586120605e-01 1.000000000000000000e+00 -7.833830118179321289e-01 2.624999880790710449e-01 4.839180111885070801e-01 1.000000000000000000e+00 -7.871329784393310547e-01 2.669219970703125000e-01 4.803070127964019775e-01 1.000000000000000000e+00 -7.908549904823303223e-01 2.713449895381927490e-01 4.767059981822967529e-01 1.000000000000000000e+00 -7.945489883422851562e-01 2.757700085639953613e-01 4.731169939041137695e-01 1.000000000000000000e+00 -7.982159852981567383e-01 2.801969945430755615e-01 4.695380032062530518e-01 1.000000000000000000e+00 -8.018550276756286621e-01 2.846260070800781250e-01 4.659709930419921875e-01 1.000000000000000000e+00 -8.054670095443725586e-01 2.890569865703582764e-01 4.624150097370147705e-01 1.000000000000000000e+00 -8.090519905090332031e-01 2.934910058975219727e-01 4.588699936866760254e-01 1.000000000000000000e+00 -8.126119971275329590e-01 2.979280054569244385e-01 4.553380012512207031e-01 1.000000000000000000e+00 -8.161439895629882812e-01 3.023679852485656738e-01 4.518159925937652588e-01 1.000000000000000000e+00 -8.196510076522827148e-01 3.068119883537292480e-01 4.483059942722320557e-01 1.000000000000000000e+00 -8.231319785118103027e-01 3.112609982490539551e-01 4.448060095310211182e-01 1.000000000000000000e+00 -8.265879750251770020e-01 3.157140016555786133e-01 4.413160085678100586e-01 1.000000000000000000e+00 -8.300179839134216309e-01 3.201720118522644043e-01 4.378359913825988770e-01 1.000000000000000000e+00 -8.334220051765441895e-01 3.246349990367889404e-01 4.343659877777099609e-01 1.000000000000000000e+00 -8.368009924888610840e-01 3.291049897670745850e-01 4.309050142765045166e-01 1.000000000000000000e+00 -8.401550054550170898e-01 3.335799872875213623e-01 4.274550080299377441e-01 1.000000000000000000e+00 -8.434839844703674316e-01 3.380619883537292480e-01 4.240129888057708740e-01 1.000000000000000000e+00 -8.467879891395568848e-01 3.425509929656982422e-01 4.205789864063262939e-01 1.000000000000000000e+00 -8.500660061836242676e-01 3.470480144023895264e-01 4.171530008316040039e-01 1.000000000000000000e+00 -8.533189892768859863e-01 3.515529930591583252e-01 4.137339890003204346e-01 1.000000000000000000e+00 -8.565469980239868164e-01 3.560659885406494141e-01 4.103220105171203613e-01 1.000000000000000000e+00 -8.597499728202819824e-01 3.605880141258239746e-01 4.069170057773590088e-01 1.000000000000000000e+00 -8.629270195960998535e-01 3.651190102100372314e-01 4.035190045833587646e-01 1.000000000000000000e+00 -8.660780191421508789e-01 3.696599900722503662e-01 4.001260101795196533e-01 1.000000000000000000e+00 -8.692029714584350586e-01 3.742119967937469482e-01 3.967379927635192871e-01 1.000000000000000000e+00 -8.723030090332031250e-01 3.787739872932434082e-01 3.933550119400024414e-01 1.000000000000000000e+00 -8.753759860992431641e-01 3.833470046520233154e-01 3.899759948253631592e-01 1.000000000000000000e+00 -8.784229755401611328e-01 3.879320025444030762e-01 3.865999877452850342e-01 1.000000000000000000e+00 -8.814430236816406250e-01 3.925290107727050781e-01 3.832289874553680420e-01 1.000000000000000000e+00 -8.844360113143920898e-01 3.971390128135681152e-01 3.798600137233734131e-01 1.000000000000000000e+00 -8.874019980430603027e-01 4.017620086669921875e-01 3.764939904212951660e-01 1.000000000000000000e+00 -8.903399705886840820e-01 4.063979983329772949e-01 3.731299936771392822e-01 1.000000000000000000e+00 -8.932499885559082031e-01 4.110479950904846191e-01 3.697679936885833740e-01 1.000000000000000000e+00 -8.961309790611267090e-01 4.157119989395141602e-01 3.664070069789886475e-01 1.000000000000000000e+00 -8.989840149879455566e-01 4.203920066356658936e-01 3.630470037460327148e-01 1.000000000000000000e+00 -9.018070101737976074e-01 4.250870048999786377e-01 3.596880137920379639e-01 1.000000000000000000e+00 -9.046009778976440430e-01 4.297969937324523926e-01 3.563289940357208252e-01 1.000000000000000000e+00 -9.073650240898132324e-01 4.345239996910095215e-01 3.529700040817260742e-01 1.000000000000000000e+00 -9.100980162620544434e-01 4.392679929733276367e-01 3.496100008487701416e-01 1.000000000000000000e+00 -9.128000140190124512e-01 4.440290033817291260e-01 3.462510108947753906e-01 1.000000000000000000e+00 -9.154710173606872559e-01 4.488070011138916016e-01 3.428899943828582764e-01 1.000000000000000000e+00 -9.181089997291564941e-01 4.536029994487762451e-01 3.395290076732635498e-01 1.000000000000000000e+00 -9.207140207290649414e-01 4.584169983863830566e-01 3.361659944057464600e-01 1.000000000000000000e+00 -9.232869744300842285e-01 4.632509946823120117e-01 3.328010141849517822e-01 1.000000000000000000e+00 -9.258249998092651367e-01 4.681029915809631348e-01 3.294349908828735352e-01 1.000000000000000000e+00 -9.283289909362792969e-01 4.729749858379364014e-01 3.260670006275177002e-01 1.000000000000000000e+00 -9.307979941368103027e-01 4.778670072555541992e-01 3.226970136165618896e-01 1.000000000000000000e+00 -9.332320094108581543e-01 4.827800095081329346e-01 3.193250000476837158e-01 1.000000000000000000e+00 -9.356300234794616699e-01 4.877119958400726318e-01 3.159520030021667480e-01 1.000000000000000000e+00 -9.379900097846984863e-01 4.926669895648956299e-01 3.125750124454498291e-01 1.000000000000000000e+00 -9.403129816055297852e-01 4.976420104503631592e-01 3.091970086097717285e-01 1.000000000000000000e+00 -9.425979852676391602e-01 5.026389956474304199e-01 3.058159947395324707e-01 1.000000000000000000e+00 -9.448440074920654297e-01 5.076580047607421875e-01 3.024330139160156250e-01 1.000000000000000000e+00 -9.470509886741638184e-01 5.126990079879760742e-01 2.990489900112152100e-01 1.000000000000000000e+00 -9.492170214653015137e-01 5.177630186080932617e-01 2.956619858741760254e-01 1.000000000000000000e+00 -9.513440132141113281e-01 5.228499770164489746e-01 2.922750115394592285e-01 1.000000000000000000e+00 -9.534279704093933105e-01 5.279600024223327637e-01 2.888830006122589111e-01 1.000000000000000000e+00 -9.554700255393981934e-01 5.330929756164550781e-01 2.854900062084197998e-01 1.000000000000000000e+00 -9.574689865112304688e-01 5.382500290870666504e-01 2.820959985256195068e-01 1.000000000000000000e+00 -9.594240188598632812e-01 5.434309840202331543e-01 2.787010073661804199e-01 1.000000000000000000e+00 -9.613360166549682617e-01 5.486360192298889160e-01 2.753050029277801514e-01 1.000000000000000000e+00 -9.632030129432678223e-01 5.538650155067443848e-01 2.719089984893798828e-01 1.000000000000000000e+00 -9.650239944458007812e-01 5.591179728507995605e-01 2.685129940509796143e-01 1.000000000000000000e+00 -9.667980074882507324e-01 5.643960237503051758e-01 2.651180028915405273e-01 1.000000000000000000e+00 -9.685260057449340820e-01 5.697000026702880859e-01 2.617209851741790771e-01 1.000000000000000000e+00 -9.702050089836120605e-01 5.750280022621154785e-01 2.583250105381011963e-01 1.000000000000000000e+00 -9.718350172042846680e-01 5.803819894790649414e-01 2.549310028553009033e-01 1.000000000000000000e+00 -9.734159708023071289e-01 5.857610106468200684e-01 2.515400052070617676e-01 1.000000000000000000e+00 -9.749469757080078125e-01 5.911650061607360840e-01 2.481510043144226074e-01 1.000000000000000000e+00 -9.764279723167419434e-01 5.965949892997741699e-01 2.447669953107833862e-01 1.000000000000000000e+00 -9.778559803962707520e-01 6.020510196685791016e-01 2.413869947195053101e-01 1.000000000000000000e+00 -9.792330265045166016e-01 6.075320243835449219e-01 2.380129992961883545e-01 1.000000000000000000e+00 -9.805560111999511719e-01 6.130390167236328125e-01 2.346460074186325073e-01 1.000000000000000000e+00 -9.818260073661804199e-01 6.185719966888427734e-01 2.312870025634765625e-01 1.000000000000000000e+00 -9.830409884452819824e-01 6.241310238838195801e-01 2.279369980096817017e-01 1.000000000000000000e+00 -9.841989874839782715e-01 6.297180056571960449e-01 2.245949953794479370e-01 1.000000000000000000e+00 -9.853010177612304688e-01 6.353300213813781738e-01 2.212650030851364136e-01 1.000000000000000000e+00 -9.863449931144714355e-01 6.409689784049987793e-01 2.179480046033859253e-01 1.000000000000000000e+00 -9.873319864273071289e-01 6.466330289840698242e-01 2.146479934453964233e-01 1.000000000000000000e+00 -9.882599711418151855e-01 6.523249745368957520e-01 2.113640010356903076e-01 1.000000000000000000e+00 -9.891279935836791992e-01 6.580430269241333008e-01 2.081000059843063354e-01 1.000000000000000000e+00 -9.899349808692932129e-01 6.637870073318481445e-01 2.048590034246444702e-01 1.000000000000000000e+00 -9.906809926033020020e-01 6.695579886436462402e-01 2.016420066356658936e-01 1.000000000000000000e+00 -9.913650155067443848e-01 6.753550171852111816e-01 1.984529942274093628e-01 1.000000000000000000e+00 -9.919850230216979980e-01 6.811789870262145996e-01 1.952950060367584229e-01 1.000000000000000000e+00 -9.925410151481628418e-01 6.870300173759460449e-01 1.921699941158294678e-01 1.000000000000000000e+00 -9.930319786071777344e-01 6.929069757461547852e-01 1.890839934349060059e-01 1.000000000000000000e+00 -9.934560060501098633e-01 6.988099813461303711e-01 1.860409975051879883e-01 1.000000000000000000e+00 -9.938139915466308594e-01 7.047410011291503906e-01 1.830430030822753906e-01 1.000000000000000000e+00 -9.941030144691467285e-01 7.106980085372924805e-01 1.800969988107681274e-01 1.000000000000000000e+00 -9.943240284919738770e-01 7.166810035705566406e-01 1.772080063819885254e-01 1.000000000000000000e+00 -9.944739937782287598e-01 7.226909995079040527e-01 1.743810027837753296e-01 1.000000000000000000e+00 -9.945530295372009277e-01 7.287279963493347168e-01 1.716219931840896606e-01 1.000000000000000000e+00 -9.945610165596008301e-01 7.347909808158874512e-01 1.689379960298538208e-01 1.000000000000000000e+00 -9.944949746131896973e-01 7.408800125122070312e-01 1.663350015878677368e-01 1.000000000000000000e+00 -9.943550229072570801e-01 7.469949722290039062e-01 1.638209968805313110e-01 1.000000000000000000e+00 -9.941409826278686523e-01 7.531369924545288086e-01 1.614039987325668335e-01 1.000000000000000000e+00 -9.938510060310363770e-01 7.593039870262145996e-01 1.590919941663742065e-01 1.000000000000000000e+00 -9.934819936752319336e-01 7.654989957809448242e-01 1.568910032510757446e-01 1.000000000000000000e+00 -9.930329918861389160e-01 7.717199921607971191e-01 1.548079997301101685e-01 1.000000000000000000e+00 -9.925050139427185059e-01 7.779669761657714844e-01 1.528549939393997192e-01 1.000000000000000000e+00 -9.918969869613647461e-01 7.842389941215515137e-01 1.510419994592666626e-01 1.000000000000000000e+00 -9.912089705467224121e-01 7.905369997024536133e-01 1.493770033121109009e-01 1.000000000000000000e+00 -9.904389977455139160e-01 7.968590259552001953e-01 1.478700041770935059e-01 1.000000000000000000e+00 -9.895870089530944824e-01 8.032050132751464844e-01 1.465290039777755737e-01 1.000000000000000000e+00 -9.886479973793029785e-01 8.095790147781372070e-01 1.453569978475570679e-01 1.000000000000000000e+00 -9.876210093498229980e-01 8.159779906272888184e-01 1.443630009889602661e-01 1.000000000000000000e+00 -9.865090250968933105e-01 8.224009871482849121e-01 1.435569971799850464e-01 1.000000000000000000e+00 -9.853140115737915039e-01 8.288459777832031250e-01 1.429450064897537231e-01 1.000000000000000000e+00 -9.840310215950012207e-01 8.353149890899658203e-01 1.425279974937438965e-01 1.000000000000000000e+00 -9.826530218124389648e-01 8.418120145797729492e-01 1.423030048608779907e-01 1.000000000000000000e+00 -9.811900258064270020e-01 8.483290076255798340e-01 1.422789990901947021e-01 1.000000000000000000e+00 -9.796440005302429199e-01 8.548660278320312500e-01 1.424529999494552612e-01 1.000000000000000000e+00 -9.779949784278869629e-01 8.614320158958435059e-01 1.428080052137374878e-01 1.000000000000000000e+00 -9.762650132179260254e-01 8.680160045623779297e-01 1.433510035276412964e-01 1.000000000000000000e+00 -9.744430184364318848e-01 8.746219873428344727e-01 1.440609991550445557e-01 1.000000000000000000e+00 -9.725300073623657227e-01 8.812500238418579102e-01 1.449230015277862549e-01 1.000000000000000000e+00 -9.705330133438110352e-01 8.878960013389587402e-01 1.459189951419830322e-01 1.000000000000000000e+00 -9.684429764747619629e-01 8.945639729499816895e-01 1.470140069723129272e-01 1.000000000000000000e+00 -9.662709832191467285e-01 9.012489914894104004e-01 1.481799930334091187e-01 1.000000000000000000e+00 -9.640210270881652832e-01 9.079499840736389160e-01 1.493699997663497925e-01 1.000000000000000000e+00 -9.616810083389282227e-01 9.146720170974731445e-01 1.505199968814849854e-01 1.000000000000000000e+00 -9.592760205268859863e-01 9.214069843292236328e-01 1.515659987926483154e-01 1.000000000000000000e+00 -9.568079710006713867e-01 9.281520247459411621e-01 1.524090021848678589e-01 1.000000000000000000e+00 -9.542869925498962402e-01 9.349079728126525879e-01 1.529210060834884644e-01 1.000000000000000000e+00 -9.517260193824768066e-01 9.416710138320922852e-01 1.529249995946884155e-01 1.000000000000000000e+00 -9.491509795188903809e-01 9.484350085258483887e-01 1.521780043840408325e-01 1.000000000000000000e+00 -9.466019868850708008e-01 9.551900029182434082e-01 1.503279954195022583e-01 1.000000000000000000e+00 -9.441519975662231445e-01 9.619160294532775879e-01 1.468610018491744995e-01 1.000000000000000000e+00 -9.418960213661193848e-01 9.685900211334228516e-01 1.409559994935989380e-01 1.000000000000000000e+00 -9.400150179862976074e-01 9.751579761505126953e-01 1.313260048627853394e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/prism b/fastplotlib/utils/colormaps/prism deleted file mode 100644 index 2fa55cbc4..000000000 --- a/fastplotlib/utils/colormaps/prism +++ /dev/null @@ -1,256 +0,0 @@ -1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.296454221010208130e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.202982842922210693e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.115908384323120117e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.909103393554687500e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.464334607124328613e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.679059386253356934e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.889842033386230469e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.990986466407775879e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.072944164276123047e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.262181282043457031e-01 9.965688586235046387e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.678088009357452393e-01 8.870493769645690918e-01 0.000000000000000000e+00 1.000000000000000000e+00 -4.251110181212425232e-02 7.408012747764587402e-01 2.247245609760284424e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.674672722816467285e-01 4.915249347686767578e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.784760832786560059e-01 7.259169816970825195e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.862886250019073486e-01 9.124462604522705078e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.576675429940223694e-03 1.000000000000000000e+00 1.000000000000000000e+00 -1.001458391547203064e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -2.431036680936813354e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -4.142085909843444824e-01 0.000000000000000000e+00 9.964607357978820801e-01 1.000000000000000000e+00 -6.021789312362670898e-01 0.000000000000000000e+00 8.449673056602478027e-01 1.000000000000000000e+00 -7.946209907531738281e-01 0.000000000000000000e+00 6.377614736557006836e-01 1.000000000000000000e+00 -9.788463115692138672e-01 0.000000000000000000e+00 3.885053694248199463e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 1.136334463953971863e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.605566874146461487e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.428793311119079590e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.354472160339355469e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.210625171661376953e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.874868512153625488e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.237471222877502441e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.619138836860656738e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.763619422912597656e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.837971568107604980e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.969160318374633789e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.280405163764953613e-01 9.359470605850219727e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.830527961254119873e-02 8.034250140190124512e-01 1.122854202985763550e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.396880745887756348e-01 3.872372210025787354e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.555320739746093750e-01 6.366568207740783691e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.630991935729980469e-01 8.440989255905151367e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.507738471031188965e-02 9.958860278129577637e-01 1.000000000000000000e+00 -5.282267183065414429e-02 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -1.816855221986770630e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -3.427450358867645264e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -5.253818631172180176e-01 0.000000000000000000e+00 9.132024049758911133e-01 1.000000000000000000e+00 -7.175539731979370117e-01 0.000000000000000000e+00 7.269346117973327637e-01 1.000000000000000000e+00 -9.065906405448913574e-01 0.000000000000000000e+00 4.927369356155395508e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 2.260510027408599854e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.663925647735595703e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.581753075122833252e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.481002926826477051e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.236450314521789551e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.732351064682006836e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.870073199272155762e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.524872064590454102e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.612222194671630859e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.705359935760498047e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.930012643337249756e-01 9.783609509468078613e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.403236836194992065e-01 8.609830737113952637e-01 0.000000000000000000e+00 1.000000000000000000e+00 -2.256991341710090637e-02 7.085951566696166992e-01 2.788060307502746582e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.312448740005493164e-01 5.405843853950500488e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.406256139278411865e-01 7.667196989059448242e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.493058055639266968e-01 9.423018693923950195e-01 1.000000000000000000e+00 -1.210340391844511032e-02 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -1.254924237728118896e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -2.747831642627716064e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -4.501322209835052490e-01 0.000000000000000000e+00 9.716661572456359863e-01 1.000000000000000000e+00 -6.399781107902526855e-01 0.000000000000000000e+00 8.083294630050659180e-01 1.000000000000000000e+00 -8.318034410476684570e-01 0.000000000000000000e+00 5.916961431503295898e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 3.360497653484344482e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 5.824621021747589111e-02 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.165643900632858276e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.806018888950347900e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.728043973445892334e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.555911898612976074e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.169103860855102539e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.461254477500915527e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.266598224639892578e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.387412190437316895e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.462902188301086426e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.619959652423858643e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.980096995830535889e-01 9.128594994544982910e-01 0.000000000000000000e+00 1.000000000000000000e+00 -6.514377892017364502e-02 7.734512090682983398e-01 1.673915386199951172e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.048042774200439453e-01 4.387275278568267822e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.180383384227752686e-01 6.811363697052001953e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.254676818847656250e-01 8.786349892616271973e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.978926688432693481e-02 1.000000000000000000e+00 1.000000000000000000e+00 -7.512564957141876221e-02 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -2.110501527786254883e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -3.772352039813995361e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -5.627234578132629395e-01 0.000000000000000000e+00 8.810750246047973633e-01 1.000000000000000000e+00 -7.552849054336547852e-01 0.000000000000000000e+00 6.843240857124328613e-01 1.000000000000000000e+00 -9.422231912612915039e-01 0.000000000000000000e+00 4.424527585506439209e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 1.714086234569549561e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.947061717510223389e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.035570740699768066e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.959804475307464600e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.840534567832946777e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.553756237030029297e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.986509442329406738e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.980860948562622070e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.155246973037719727e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.233682036399841309e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.342863559722900391e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.607460916042327881e-01 9.584991931915283203e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.141897067427635193e-01 8.335622549057006836e-01 5.418593436479568481e-02 1.000000000000000000e+00 -4.280258435755968094e-03 6.754232645034790039e-01 3.321762681007385254e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.945090413093566895e-01 5.882648229598999023e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.027480542659759521e-01 8.055665493011474609e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.127839162945747375e-01 9.697538018226623535e-01 1.000000000000000000e+00 -3.112411871552467346e-02 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -1.522279977798461914e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -3.074707984924316406e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -4.866167008876800537e-01 0.000000000000000000e+00 9.443929791450500488e-01 1.000000000000000000e+00 -6.778538227081298828e-01 0.000000000000000000e+00 7.696296572685241699e-01 1.000000000000000000e+00 -8.685731291770935059e-01 0.000000000000000000e+00 5.441214442253112793e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 2.827369272708892822e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 2.710390836000442505e-03 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.278651952743530273e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.184504806995391846e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.097972750663757324e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.892892718315124512e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.450918197631835938e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.669321179389953613e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.907510042190551758e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.009451389312744141e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.090988874435424805e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.278615772724151611e-01 9.974138736724853516e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.691828966140747070e-01 8.882851004600524902e-01 0.000000000000000000e+00 1.000000000000000000e+00 -4.352521896362304688e-02 7.423461675643920898e-01 2.220706492662429810e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.692194700241088867e-01 4.890986979007720947e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.803200423717498779e-01 7.238783836364746094e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.881028115749359131e-01 9.109296798706054688e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.241449922323226929e-03 1.000000000000000000e+00 1.000000000000000000e+00 -9.894609451293945312e-02 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -2.415855377912521362e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -4.124721586704254150e-01 0.000000000000000000e+00 9.976057410240173340e-01 1.000000000000000000e+00 -6.003386974334716797e-01 0.000000000000000000e+00 8.467000722885131836e-01 1.000000000000000000e+00 -7.927983403205871582e-01 0.000000000000000000e+00 6.399679183959960938e-01 1.000000000000000000e+00 -9.771612882614135742e-01 0.000000000000000000e+00 3.910399079322814941e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 1.163289919495582581e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.433619394898414612e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.410441040992736816e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.336172640323638916e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.193584799766540527e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.860211133956909180e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.226162433624267578e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.636152982711791992e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.781909704208374023e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.856331586837768555e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.986380100250244141e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.295349091291427612e-01 9.370341897010803223e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.947356045246124268e-02 8.048568964004516602e-01 1.095888465642929077e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.413702368736267090e-01 3.846991658210754395e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.573536217212677002e-01 6.344446539878845215e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.649400234222412109e-01 8.423584699630737305e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.681612670421600342e-02 9.947319626808166504e-01 1.000000000000000000e+00 -5.177454650402069092e-02 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -1.802843660116195679e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -3.410832285881042480e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -5.235689878463745117e-01 0.000000000000000000e+00 9.147107005119323730e-01 1.000000000000000000e+00 -7.157095670700073242e-01 0.000000000000000000e+00 7.289666533470153809e-01 1.000000000000000000e+00 -9.048362970352172852e-01 0.000000000000000000e+00 4.951587021350860596e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 2.287028580904006958e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.645895838737487793e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.563285171985626221e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.463315248489379883e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.220708727836608887e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.719593286514282227e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.861140251159667969e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.542791008949279785e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.630701422691345215e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.723180234432220459e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.945999801158905029e-01 9.792879223823547363e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.416336297988891602e-01 8.622865676879882812e-01 0.000000000000000000e+00 1.000000000000000000e+00 -2.350473403930664062e-02 7.101892828941345215e-01 2.761832773685455322e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.330244898796081543e-01 5.382221937179565430e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.424733877182006836e-01 7.647738456726074219e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.510999053716659546e-01 9.409006834030151367e-01 1.000000000000000000e+00 -1.121811661869287491e-02 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -1.242232397198677063e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -2.732137441635131836e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -4.483660757541656494e-01 0.000000000000000000e+00 9.729337096214294434e-01 1.000000000000000000e+00 -6.381316781044006348e-01 0.000000000000000000e+00 8.101652860641479492e-01 1.000000000000000000e+00 -8.299984931945800781e-01 0.000000000000000000e+00 5.939792394638061523e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 3.386295735836029053e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 6.095262244343757629e-02 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.990496397018432617e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.787580788135528564e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 4.709897637367248535e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.539254188537597656e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.155032992362976074e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.450698494911193848e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.283954501152038574e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.405812144279479980e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.481132864952087402e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.636818826198577881e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.994473040103912354e-01 9.140206575393676758e-01 0.000000000000000000e+00 1.000000000000000000e+00 -6.623827666044235229e-02 7.749401926994323730e-01 1.647122055292129517e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.065229177474975586e-01 4.362407326698303223e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.198732972145080566e-01 6.790060400962829590e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.272979468107223511e-01 8.770015835762023926e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.149418324232101440e-02 1.000000000000000000e+00 1.000000000000000000e+00 -7.400201261043548584e-02 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -2.095899581909179688e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -3.755346834659576416e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -5.608947873115539551e-01 0.000000000000000000e+00 8.826950788497924805e-01 1.000000000000000000e+00 -7.534486651420593262e-01 0.000000000000000000e+00 6.864440441131591797e-01 1.000000000000000000e+00 -9.405003786087036133e-01 0.000000000000000000e+00 4.449328780174255371e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 1.740853786468505859e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.778940670192241669e-02 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 2.017359137535095215e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.941394090652465820e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.823139548301696777e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 7.538523077964782715e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.974443078041076660e-01 0.000000000000000000e+00 1.000000000000000000e+00 -9.997469186782836914e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.173370957374572754e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.252127885818481445e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -4.360414147377014160e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -2.622959613800048828e-01 9.595056772232055664e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.154321730136871338e-01 8.349302411079406738e-01 5.147866904735565186e-02 1.000000000000000000e+00 -5.133399274200201035e-03 6.770625114440917969e-01 3.295913934707641602e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.963115453720092773e-01 5.859727859497070312e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.045949339866638184e-01 8.037184476852416992e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.145533621311187744e-01 9.684715270996093750e-01 1.000000000000000000e+00 -3.015805967152118683e-02 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -1.508926153182983398e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -3.058541417121887207e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -4.848253428936004639e-01 0.000000000000000000e+00 9.457798600196838379e-01 1.000000000000000000e+00 -6.760058999061584473e-01 0.000000000000000000e+00 7.715638279914855957e-01 1.000000000000000000e+00 -8.667904734611511230e-01 0.000000000000000000e+00 5.464753508567810059e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 2.853554189205169678e-01 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 5.420765373855829239e-03 1.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 1.260861903429031372e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.166027367115020752e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 5.080026388168334961e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 6.876660585403442383e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 8.437470197677612305e-01 0.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.659544229507446289e-01 0.000000000000000000e+00 1.000000000000000000e+00 -8.925164937973022461e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.027914524078369141e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.109043717384338379e-01 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -3.295071125030517578e-01 9.982548952102661133e-01 0.000000000000000000e+00 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/rainbow b/fastplotlib/utils/colormaps/rainbow deleted file mode 100644 index f90067236..000000000 --- a/fastplotlib/utils/colormaps/rainbow +++ /dev/null @@ -1,256 +0,0 @@ -5.000000000000000000e-01 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -4.921568632125854492e-01 1.231965981423854828e-02 9.999810457229614258e-01 1.000000000000000000e+00 -4.843137264251708984e-01 2.463744953274726868e-02 9.999241232872009277e-01 1.000000000000000000e+00 -4.764705896377563477e-01 3.695150092244148254e-02 9.998292326927185059e-01 1.000000000000000000e+00 -4.686274528503417969e-01 4.925994202494621277e-02 9.996964335441589355e-01 1.000000000000000000e+00 -4.607843160629272461e-01 6.156090646982192993e-02 9.995257258415222168e-01 1.000000000000000000e+00 -4.529411792755126953e-01 7.385252416133880615e-02 9.993170499801635742e-01 1.000000000000000000e+00 -4.450980424880981445e-01 8.613293617963790894e-02 9.990704655647277832e-01 1.000000000000000000e+00 -4.372549057006835938e-01 9.840027987957000732e-02 9.987859725952148438e-01 1.000000000000000000e+00 -4.294117689132690430e-01 1.106526851654052734e-01 9.984636306762695312e-01 1.000000000000000000e+00 -4.215686321258544922e-01 1.228882893919944763e-01 9.981033205986022949e-01 1.000000000000000000e+00 -4.137254953384399414e-01 1.351052522659301758e-01 9.977051615715026855e-01 1.000000000000000000e+00 -4.058823585510253906e-01 1.473017036914825439e-01 9.972691535949707031e-01 1.000000000000000000e+00 -3.980392217636108398e-01 1.594757884740829468e-01 9.967952966690063477e-01 1.000000000000000000e+00 -3.901960849761962891e-01 1.716256737709045410e-01 9.962836503982543945e-01 1.000000000000000000e+00 -3.823529481887817383e-01 1.837495118379592896e-01 9.957341551780700684e-01 1.000000000000000000e+00 -3.745098114013671875e-01 1.958454698324203491e-01 9.951469302177429199e-01 1.000000000000000000e+00 -3.666666746139526367e-01 2.079116851091384888e-01 9.945219159126281738e-01 1.000000000000000000e+00 -3.588235378265380859e-01 2.199463546276092529e-01 9.938591122627258301e-01 1.000000000000000000e+00 -3.509804010391235352e-01 2.319476455450057983e-01 9.931586384773254395e-01 1.000000000000000000e+00 -3.431372642517089844e-01 2.439137250185012817e-01 9.924204945564270020e-01 1.000000000000000000e+00 -3.352941274642944336e-01 2.558427751064300537e-01 9.916446805000305176e-01 1.000000000000000000e+00 -3.274509906768798828e-01 2.677330076694488525e-01 9.908312559127807617e-01 1.000000000000000000e+00 -3.196078538894653320e-01 2.795825898647308350e-01 9.899802207946777344e-01 1.000000000000000000e+00 -3.117647171020507812e-01 2.913897335529327393e-01 9.890916347503662109e-01 1.000000000000000000e+00 -3.039215803146362305e-01 3.031526803970336914e-01 9.881654977798461914e-01 1.000000000000000000e+00 -2.960784435272216797e-01 3.148695826530456543e-01 9.872018098831176758e-01 1.000000000000000000e+00 -2.882353067398071289e-01 3.265387117862701416e-01 9.862007498741149902e-01 1.000000000000000000e+00 -2.803921699523925781e-01 3.381582796573638916e-01 9.851622581481933594e-01 1.000000000000000000e+00 -2.725490331649780273e-01 3.497264981269836426e-01 9.840863347053527832e-01 1.000000000000000000e+00 -2.647058963775634766e-01 3.612416684627532959e-01 9.829730987548828125e-01 1.000000000000000000e+00 -2.568627595901489258e-01 3.727020025253295898e-01 9.818225502967834473e-01 1.000000000000000000e+00 -2.490196079015731812e-01 3.841057419776916504e-01 9.806347489356994629e-01 1.000000000000000000e+00 -2.411764711141586304e-01 3.954512178897857666e-01 9.794097542762756348e-01 1.000000000000000000e+00 -2.333333343267440796e-01 4.067366421222686768e-01 9.781476259231567383e-01 1.000000000000000000e+00 -2.254901975393295288e-01 4.179603457450866699e-01 9.768483042716979980e-01 1.000000000000000000e+00 -2.176470607519149780e-01 4.291206002235412598e-01 9.755119681358337402e-01 1.000000000000000000e+00 -2.098039239645004272e-01 4.402157366275787354e-01 9.741386175155639648e-01 1.000000000000000000e+00 -2.019607871770858765e-01 4.512440562248229980e-01 9.727282524108886719e-01 1.000000000000000000e+00 -1.941176503896713257e-01 4.622038900852203369e-01 9.712810516357421875e-01 1.000000000000000000e+00 -1.862745136022567749e-01 4.730935692787170410e-01 9.697969555854797363e-01 1.000000000000000000e+00 -1.784313768148422241e-01 4.839114248752593994e-01 9.682760238647460938e-01 1.000000000000000000e+00 -1.705882400274276733e-01 4.946558475494384766e-01 9.667183756828308105e-01 1.000000000000000000e+00 -1.627451032400131226e-01 5.053251981735229492e-01 9.651240706443786621e-01 1.000000000000000000e+00 -1.549019664525985718e-01 5.159178376197814941e-01 9.634931683540344238e-01 1.000000000000000000e+00 -1.470588296651840210e-01 5.264321565628051758e-01 9.618256688117980957e-01 1.000000000000000000e+00 -1.392156928777694702e-01 5.368666052818298340e-01 9.601216316223144531e-01 1.000000000000000000e+00 -1.313725560903549194e-01 5.472195744514465332e-01 9.583812355995178223e-01 1.000000000000000000e+00 -1.235294118523597717e-01 5.574894547462463379e-01 9.566044211387634277e-01 1.000000000000000000e+00 -1.156862750649452209e-01 5.676746964454650879e-01 9.547913074493408203e-01 1.000000000000000000e+00 -1.078431382775306702e-01 5.777738094329833984e-01 9.529420137405395508e-01 1.000000000000000000e+00 -1.000000014901161194e-01 5.877852439880371094e-01 9.510565400123596191e-01 1.000000000000000000e+00 -9.215686470270156860e-02 5.977074503898620605e-01 9.491349458694458008e-01 1.000000000000000000e+00 -8.431372791528701782e-02 6.075389385223388672e-01 9.471773505210876465e-01 1.000000000000000000e+00 -7.647059112787246704e-02 6.172782182693481445e-01 9.451838135719299316e-01 1.000000000000000000e+00 -6.862745434045791626e-02 6.269237995147705078e-01 9.431544542312622070e-01 1.000000000000000000e+00 -6.078431382775306702e-02 6.364742517471313477e-01 9.410892724990844727e-01 1.000000000000000000e+00 -5.294117704033851624e-02 6.459280848503112793e-01 9.389883875846862793e-01 1.000000000000000000e+00 -4.509804025292396545e-02 6.552838683128356934e-01 9.368518590927124023e-01 1.000000000000000000e+00 -3.725490346550941467e-02 6.645401716232299805e-01 9.346797466278076172e-01 1.000000000000000000e+00 -2.941176481544971466e-02 6.736956238746643066e-01 9.324722290039062500e-01 1.000000000000000000e+00 -2.156862802803516388e-02 6.827488541603088379e-01 9.302293062210083008e-01 1.000000000000000000e+00 -1.372549030929803848e-02 6.916984319686889648e-01 9.279510974884033203e-01 1.000000000000000000e+00 -5.882353056222200394e-03 7.005430459976196289e-01 9.256376624107360840e-01 1.000000000000000000e+00 -1.960784429684281349e-03 7.092813253402709961e-01 9.232891201972961426e-01 1.000000000000000000e+00 -9.803921915590763092e-03 7.179118990898132324e-01 9.209055304527282715e-01 1.000000000000000000e+00 -1.764705963432788849e-02 7.264335751533508301e-01 9.184870123863220215e-01 1.000000000000000000e+00 -2.549019642174243927e-02 7.348449826240539551e-01 9.160336256027221680e-01 1.000000000000000000e+00 -3.333333507180213928e-02 7.431448101997375488e-01 9.135454297065734863e-01 1.000000000000000000e+00 -4.117647185921669006e-02 7.513318657875061035e-01 9.110226631164550781e-01 1.000000000000000000e+00 -4.901960864663124084e-02 7.594048976898193359e-01 9.084652662277221680e-01 1.000000000000000000e+00 -5.686274543404579163e-02 7.673626542091369629e-01 9.058734178543090820e-01 1.000000000000000000e+00 -6.470588594675064087e-02 7.752040028572082520e-01 9.032471776008605957e-01 1.000000000000000000e+00 -7.254902273416519165e-02 7.829276323318481445e-01 9.005867242813110352e-01 1.000000000000000000e+00 -8.039215952157974243e-02 7.905324101448059082e-01 8.978920578956604004e-01 1.000000000000000000e+00 -8.823529630899429321e-02 7.980172038078308105e-01 8.951632976531982422e-01 1.000000000000000000e+00 -9.607843309640884399e-02 8.053809404373168945e-01 8.924005627632141113e-01 1.000000000000000000e+00 -1.039215698838233948e-01 8.126223683357238770e-01 8.896040320396423340e-01 1.000000000000000000e+00 -1.117647066712379456e-01 8.197404742240905762e-01 8.867737054824829102e-01 1.000000000000000000e+00 -1.196078434586524963e-01 8.267341852188110352e-01 8.839097023010253906e-01 1.000000000000000000e+00 -1.274509876966476440e-01 8.336023688316345215e-01 8.810122013092041016e-01 1.000000000000000000e+00 -1.352941244840621948e-01 8.403440713882446289e-01 8.780812621116638184e-01 1.000000000000000000e+00 -1.431372612714767456e-01 8.469582200050354004e-01 8.751170039176940918e-01 1.000000000000000000e+00 -1.509803980588912964e-01 8.534438014030456543e-01 8.721194863319396973e-01 1.000000000000000000e+00 -1.588235348463058472e-01 8.597998619079589844e-01 8.690889477729797363e-01 1.000000000000000000e+00 -1.666666716337203979e-01 8.660253882408142090e-01 8.660253882408142090e-01 1.000000000000000000e+00 -1.745098084211349487e-01 8.721194863319396973e-01 8.629289865493774414e-01 1.000000000000000000e+00 -1.823529452085494995e-01 8.780812621116638184e-01 8.597998619079589844e-01 1.000000000000000000e+00 -1.901960819959640503e-01 8.839097023010253906e-01 8.566380739212036133e-01 1.000000000000000000e+00 -1.980392187833786011e-01 8.896040320396423340e-01 8.534438014030456543e-01 1.000000000000000000e+00 -2.058823555707931519e-01 8.951632976531982422e-01 8.502171635627746582e-01 1.000000000000000000e+00 -2.137254923582077026e-01 9.005867242813110352e-01 8.469582200050354004e-01 1.000000000000000000e+00 -2.215686291456222534e-01 9.058734178543090820e-01 8.436671495437622070e-01 1.000000000000000000e+00 -2.294117659330368042e-01 9.110226631164550781e-01 8.403440713882446289e-01 1.000000000000000000e+00 -2.372549027204513550e-01 9.160336256027221680e-01 8.369891047477722168e-01 1.000000000000000000e+00 -2.450980395078659058e-01 9.209055304527282715e-01 8.336023688316345215e-01 1.000000000000000000e+00 -2.529411911964416504e-01 9.256376624107360840e-01 8.301840424537658691e-01 1.000000000000000000e+00 -2.607843279838562012e-01 9.302293062210083008e-01 8.267341852188110352e-01 1.000000000000000000e+00 -2.686274647712707520e-01 9.346797466278076172e-01 8.232529759407043457e-01 1.000000000000000000e+00 -2.764706015586853027e-01 9.389883875846862793e-01 8.197404742240905762e-01 1.000000000000000000e+00 -2.843137383460998535e-01 9.431544542312622070e-01 8.161969184875488281e-01 1.000000000000000000e+00 -2.921568751335144043e-01 9.471773505210876465e-01 8.126223683357238770e-01 1.000000000000000000e+00 -3.000000119209289551e-01 9.510565400123596191e-01 8.090170025825500488e-01 1.000000000000000000e+00 -3.078431487083435059e-01 9.547913074493408203e-01 8.053809404373168945e-01 1.000000000000000000e+00 -3.156862854957580566e-01 9.583812355995178223e-01 8.017143011093139648e-01 1.000000000000000000e+00 -3.235294222831726074e-01 9.618256688117980957e-01 7.980172038078308105e-01 1.000000000000000000e+00 -3.313725590705871582e-01 9.651240706443786621e-01 7.942898869514465332e-01 1.000000000000000000e+00 -3.392156958580017090e-01 9.682760238647460938e-01 7.905324101448059082e-01 1.000000000000000000e+00 -3.470588326454162598e-01 9.712810516357421875e-01 7.867449522018432617e-01 1.000000000000000000e+00 -3.549019694328308105e-01 9.741386175155639648e-01 7.829276323318481445e-01 1.000000000000000000e+00 -3.627451062202453613e-01 9.768483042716979980e-01 7.790805697441101074e-01 1.000000000000000000e+00 -3.705882430076599121e-01 9.794097542762756348e-01 7.752040028572082520e-01 1.000000000000000000e+00 -3.784313797950744629e-01 9.818225502967834473e-01 7.712979912757873535e-01 1.000000000000000000e+00 -3.862745165824890137e-01 9.840863347053527832e-01 7.673626542091369629e-01 1.000000000000000000e+00 -3.941176533699035645e-01 9.862007498741149902e-01 7.633982896804809570e-01 1.000000000000000000e+00 -4.019607901573181152e-01 9.881654977798461914e-01 7.594048976898193359e-01 1.000000000000000000e+00 -4.098039269447326660e-01 9.899802207946777344e-01 7.553827166557312012e-01 1.000000000000000000e+00 -4.176470637321472168e-01 9.916446805000305176e-01 7.513318657875061035e-01 1.000000000000000000e+00 -4.254902005195617676e-01 9.931586384773254395e-01 7.472525238990783691e-01 1.000000000000000000e+00 -4.333333373069763184e-01 9.945219159126281738e-01 7.431448101997375488e-01 1.000000000000000000e+00 -4.411764740943908691e-01 9.957341551780700684e-01 7.390089035034179688e-01 1.000000000000000000e+00 -4.490196108818054199e-01 9.967952966690063477e-01 7.348449826240539551e-01 1.000000000000000000e+00 -4.568627476692199707e-01 9.977051615715026855e-01 7.306531071662902832e-01 1.000000000000000000e+00 -4.647058844566345215e-01 9.984636306762695312e-01 7.264335751533508301e-01 1.000000000000000000e+00 -4.725490212440490723e-01 9.990704655647277832e-01 7.221864461898803711e-01 1.000000000000000000e+00 -4.803921580314636230e-01 9.995257258415222168e-01 7.179118990898132324e-01 1.000000000000000000e+00 -4.882352948188781738e-01 9.998292326927185059e-01 7.136101722717285156e-01 1.000000000000000000e+00 -4.960784316062927246e-01 9.999810457229614258e-01 7.092813253402709961e-01 1.000000000000000000e+00 -5.039215683937072754e-01 9.999810457229614258e-01 7.049255371093750000e-01 1.000000000000000000e+00 -5.117647051811218262e-01 9.998292326927185059e-01 7.005430459976196289e-01 1.000000000000000000e+00 -5.196078419685363770e-01 9.995257258415222168e-01 6.961339712142944336e-01 1.000000000000000000e+00 -5.274509787559509277e-01 9.990704655647277832e-01 6.916984319686889648e-01 1.000000000000000000e+00 -5.352941155433654785e-01 9.984636306762695312e-01 6.872366666793823242e-01 1.000000000000000000e+00 -5.431372523307800293e-01 9.977051615715026855e-01 6.827488541603088379e-01 1.000000000000000000e+00 -5.509803891181945801e-01 9.967952966690063477e-01 6.782351136207580566e-01 1.000000000000000000e+00 -5.588235259056091309e-01 9.957341551780700684e-01 6.736956238746643066e-01 1.000000000000000000e+00 -5.666666626930236816e-01 9.945219159126281738e-01 6.691306233406066895e-01 1.000000000000000000e+00 -5.745097994804382324e-01 9.931586384773254395e-01 6.645401716232299805e-01 1.000000000000000000e+00 -5.823529362678527832e-01 9.916446805000305176e-01 6.599245071411132812e-01 1.000000000000000000e+00 -5.901960730552673340e-01 9.899802207946777344e-01 6.552838683128356934e-01 1.000000000000000000e+00 -5.980392098426818848e-01 9.881654977798461914e-01 6.506183147430419922e-01 1.000000000000000000e+00 -6.058823466300964355e-01 9.862007498741149902e-01 6.459280848503112793e-01 1.000000000000000000e+00 -6.137254834175109863e-01 9.840863347053527832e-01 6.412132978439331055e-01 1.000000000000000000e+00 -6.215686202049255371e-01 9.818225502967834473e-01 6.364742517471313477e-01 1.000000000000000000e+00 -6.294117569923400879e-01 9.794097542762756348e-01 6.317110061645507812e-01 1.000000000000000000e+00 -6.372548937797546387e-01 9.768483042716979980e-01 6.269237995147705078e-01 1.000000000000000000e+00 -6.450980305671691895e-01 9.741386175155639648e-01 6.221128106117248535e-01 1.000000000000000000e+00 -6.529411673545837402e-01 9.712810516357421875e-01 6.172782182693481445e-01 1.000000000000000000e+00 -6.607843041419982910e-01 9.682760238647460938e-01 6.124202013015747070e-01 1.000000000000000000e+00 -6.686274409294128418e-01 9.651240706443786621e-01 6.075389385223388672e-01 1.000000000000000000e+00 -6.764705777168273926e-01 9.618256688117980957e-01 6.026346087455749512e-01 1.000000000000000000e+00 -6.843137145042419434e-01 9.583812355995178223e-01 5.977074503898620605e-01 1.000000000000000000e+00 -6.921568512916564941e-01 9.547913074493408203e-01 5.927575826644897461e-01 1.000000000000000000e+00 -6.999999880790710449e-01 9.510565400123596191e-01 5.877852439880371094e-01 1.000000000000000000e+00 -7.078431248664855957e-01 9.471773505210876465e-01 5.827906131744384766e-01 1.000000000000000000e+00 -7.156862616539001465e-01 9.431544542312622070e-01 5.777738094329833984e-01 1.000000000000000000e+00 -7.235293984413146973e-01 9.389883875846862793e-01 5.727351307868957520e-01 1.000000000000000000e+00 -7.313725352287292480e-01 9.346797466278076172e-01 5.676746964454650879e-01 1.000000000000000000e+00 -7.392156720161437988e-01 9.302293062210083008e-01 5.625927448272705078e-01 1.000000000000000000e+00 -7.470588088035583496e-01 9.256376624107360840e-01 5.574894547462463379e-01 1.000000000000000000e+00 -7.549019455909729004e-01 9.209055304527282715e-01 5.523649454116821289e-01 1.000000000000000000e+00 -7.627450823783874512e-01 9.160336256027221680e-01 5.472195744514465332e-01 1.000000000000000000e+00 -7.705882191658020020e-01 9.110226631164550781e-01 5.420533418655395508e-01 1.000000000000000000e+00 -7.784313559532165527e-01 9.058734178543090820e-01 5.368666052818298340e-01 1.000000000000000000e+00 -7.862744927406311035e-01 9.005867242813110352e-01 5.316594839096069336e-01 1.000000000000000000e+00 -7.941176295280456543e-01 8.951632976531982422e-01 5.264321565628051758e-01 1.000000000000000000e+00 -8.019607663154602051e-01 8.896040320396423340e-01 5.211848616600036621e-01 1.000000000000000000e+00 -8.098039031028747559e-01 8.839097023010253906e-01 5.159178376197814941e-01 1.000000000000000000e+00 -8.176470398902893066e-01 8.780812621116638184e-01 5.106312036514282227e-01 1.000000000000000000e+00 -8.254901766777038574e-01 8.721194863319396973e-01 5.053251981735229492e-01 1.000000000000000000e+00 -8.333333134651184082e-01 8.660253882408142090e-01 5.000000000000000000e-01 1.000000000000000000e+00 -8.411764502525329590e-01 8.597998619079589844e-01 4.946558475494384766e-01 1.000000000000000000e+00 -8.490195870399475098e-01 8.534438014030456543e-01 4.892929196357727051e-01 1.000000000000000000e+00 -8.568627238273620605e-01 8.469582200050354004e-01 4.839114248752593994e-01 1.000000000000000000e+00 -8.647058606147766113e-01 8.403440713882446289e-01 4.785115718841552734e-01 1.000000000000000000e+00 -8.725489974021911621e-01 8.336023688316345215e-01 4.730935692787170410e-01 1.000000000000000000e+00 -8.803921341896057129e-01 8.267341852188110352e-01 4.676575958728790283e-01 1.000000000000000000e+00 -8.882352709770202637e-01 8.197404742240905762e-01 4.622038900852203369e-01 1.000000000000000000e+00 -8.960784077644348145e-01 8.126223683357238770e-01 4.567326307296752930e-01 1.000000000000000000e+00 -9.039215445518493652e-01 8.053809404373168945e-01 4.512440562248229980e-01 1.000000000000000000e+00 -9.117646813392639160e-01 7.980172038078308105e-01 4.457383453845977783e-01 1.000000000000000000e+00 -9.196078181266784668e-01 7.905324101448059082e-01 4.402157366275787354e-01 1.000000000000000000e+00 -9.274509549140930176e-01 7.829276323318481445e-01 4.346764087677001953e-01 1.000000000000000000e+00 -9.352940917015075684e-01 7.752040028572082520e-01 4.291206002235412598e-01 1.000000000000000000e+00 -9.431372284889221191e-01 7.673626542091369629e-01 4.235485196113586426e-01 1.000000000000000000e+00 -9.509803652763366699e-01 7.594048976898193359e-01 4.179603457450866699e-01 1.000000000000000000e+00 -9.588235020637512207e-01 7.513318657875061035e-01 4.123563170433044434e-01 1.000000000000000000e+00 -9.666666388511657715e-01 7.431448101997375488e-01 4.067366421222686768e-01 1.000000000000000000e+00 -9.745097756385803223e-01 7.348449826240539551e-01 4.011015295982360840e-01 1.000000000000000000e+00 -9.823529124259948730e-01 7.264335751533508301e-01 3.954512178897857666e-01 1.000000000000000000e+00 -9.901960492134094238e-01 7.179118990898132324e-01 3.897858858108520508e-01 1.000000000000000000e+00 -9.980391860008239746e-01 7.092813253402709961e-01 3.841057419776916504e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.005430459976196289e-01 3.784110546112060547e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.916984319686889648e-01 3.727020025253295898e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.827488541603088379e-01 3.669787943363189697e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.736956238746643066e-01 3.612416684627532959e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.645401716232299805e-01 3.554908335208892822e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.552838683128356934e-01 3.497264981269836426e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.459280848503112793e-01 3.439489305019378662e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.364742517471313477e-01 3.381582796573638916e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.269237995147705078e-01 3.323548138141632080e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.172782182693481445e-01 3.265387117862701416e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.075389385223388672e-01 3.207102417945861816e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.977074503898620605e-01 3.148695826530456543e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.877852439880371094e-01 3.090170025825500488e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.777738094329833984e-01 3.031526803970336914e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.676746964454650879e-01 2.972768545150756836e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.574894547462463379e-01 2.913897335529327393e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.472195744514465332e-01 2.854915857315063477e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.368666052818298340e-01 2.795825898647308350e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.264321565628051758e-01 2.736629843711853027e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.159178376197814941e-01 2.677330076694488525e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.053251981735229492e-01 2.617928683757781982e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.946558475494384766e-01 2.558427751064300537e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.839114248752593994e-01 2.498829960823059082e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.730935692787170410e-01 2.439137250185012817e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.622038900852203369e-01 2.379352003335952759e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.512440562248229980e-01 2.319476455450057983e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.402157366275787354e-01 2.259512841701507568e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.291206002235412598e-01 2.199463546276092529e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.179603457450866699e-01 2.139330804347991943e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.067366421222686768e-01 2.079116851091384888e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.954512178897857666e-01 2.018824070692062378e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.841057419776916504e-01 1.958454698324203491e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.727020025253295898e-01 1.898010969161987305e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.612416684627532959e-01 1.837495118379592896e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.497264981269836426e-01 1.776909679174423218e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.381582796573638916e-01 1.716256737709045410e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.265387117862701416e-01 1.655538827180862427e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.148695826530456543e-01 1.594757884740829468e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.031526803970336914e-01 1.533916592597961426e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.913897335529327393e-01 1.473017036914825439e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.795825898647308350e-01 1.412061452865600586e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.677330076694488525e-01 1.351052522659301758e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.558427751064300537e-01 1.289992183446884155e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.439137250185012817e-01 1.228882893919944763e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.319476455450057983e-01 1.167727038264274597e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.199463546276092529e-01 1.106526851654052734e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.079116851091384888e-01 1.045284643769264221e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.958454698324203491e-01 9.840027987957000732e-02 1.000000000000000000e+00 -1.000000000000000000e+00 1.837495118379592896e-01 9.226836264133453369e-02 1.000000000000000000e+00 -1.000000000000000000e+00 1.716256737709045410e-01 8.613293617963790894e-02 1.000000000000000000e+00 -1.000000000000000000e+00 1.594757884740829468e-01 7.999425381422042847e-02 1.000000000000000000e+00 -1.000000000000000000e+00 1.473017036914825439e-01 7.385252416133880615e-02 1.000000000000000000e+00 -1.000000000000000000e+00 1.351052522659301758e-01 6.770800054073333740e-02 1.000000000000000000e+00 -1.000000000000000000e+00 1.228882893919944763e-01 6.156090646982192993e-02 1.000000000000000000e+00 -1.000000000000000000e+00 1.106526851654052734e-01 5.541147664189338684e-02 1.000000000000000000e+00 -1.000000000000000000e+00 9.840027987957000732e-02 4.925994202494621277e-02 1.000000000000000000e+00 -1.000000000000000000e+00 8.613293617963790894e-02 4.310653731226921082e-02 1.000000000000000000e+00 -1.000000000000000000e+00 7.385252416133880615e-02 3.695150092244148254e-02 1.000000000000000000e+00 -1.000000000000000000e+00 6.156090646982192993e-02 3.079505823552608490e-02 1.000000000000000000e+00 -1.000000000000000000e+00 4.925994202494621277e-02 2.463744953274726868e-02 1.000000000000000000e+00 -1.000000000000000000e+00 3.695150092244148254e-02 1.847890578210353851e-02 1.000000000000000000e+00 -1.000000000000000000e+00 2.463744953274726868e-02 1.231965981423854828e-02 1.000000000000000000e+00 -1.000000000000000000e+00 1.231965981423854828e-02 6.159946788102388382e-03 1.000000000000000000e+00 -1.000000000000000000e+00 1.224646852585167854e-16 6.123234262925839272e-17 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/seismic b/fastplotlib/utils/colormaps/seismic deleted file mode 100644 index d66ad3a88..000000000 --- a/fastplotlib/utils/colormaps/seismic +++ /dev/null @@ -1,256 +0,0 @@ -0.000000000000000000e+00 0.000000000000000000e+00 3.000000119209289551e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 3.109803795814514160e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 3.219607770442962646e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 3.329411745071411133e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 3.439215719699859619e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 3.549019694328308105e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 3.658823668956756592e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 3.768627345561981201e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 3.878431320190429688e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 3.988235294818878174e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 4.098039269447326660e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 4.207843244075775146e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 4.317646920680999756e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 4.427450895309448242e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 4.537254869937896729e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 4.647058844566345215e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 4.756862819194793701e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 4.866666793823242188e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 4.976470470428466797e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 5.086274743080139160e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 5.196078419685363770e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 5.305882096290588379e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 5.415686368942260742e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 5.525490045547485352e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 5.635294318199157715e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 5.745097994804382324e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 5.854901671409606934e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 5.964705944061279297e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 6.074509620666503906e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 6.184313893318176270e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 6.294117569923400879e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 6.403921842575073242e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 6.513725519180297852e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 6.623529195785522461e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 6.733333468437194824e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 6.843137145042419434e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 6.952941417694091797e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 7.062745094299316406e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 7.172548770904541016e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 7.282353043556213379e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 7.392156720161437988e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 7.501960992813110352e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 7.611764669418334961e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 7.721568346023559570e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 7.831372618675231934e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 7.941176295280456543e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 8.050980567932128906e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 8.160784244537353516e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 8.270588517189025879e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 8.380392193794250488e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 8.490195870399475098e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 8.600000143051147461e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 8.709803819656372070e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 8.819608092308044434e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 8.929411768913269043e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 9.039215445518493652e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 9.149019718170166016e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 9.258823394775390625e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 9.368627667427062988e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 9.478431344032287598e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 9.588235020637512207e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 9.698039293289184570e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 9.807842969894409180e-01 1.000000000000000000e+00 -0.000000000000000000e+00 0.000000000000000000e+00 9.917647242546081543e-01 1.000000000000000000e+00 -3.921568859368562698e-03 3.921568859368562698e-03 1.000000000000000000e+00 1.000000000000000000e+00 -1.960784383118152618e-02 1.960784383118152618e-02 1.000000000000000000e+00 1.000000000000000000e+00 -3.529411926865577698e-02 3.529411926865577698e-02 1.000000000000000000e+00 1.000000000000000000e+00 -5.098039284348487854e-02 5.098039284348487854e-02 1.000000000000000000e+00 1.000000000000000000e+00 -6.666667014360427856e-02 6.666667014360427856e-02 1.000000000000000000e+00 1.000000000000000000e+00 -8.235294371843338013e-02 8.235294371843338013e-02 1.000000000000000000e+00 1.000000000000000000e+00 -9.803921729326248169e-02 9.803921729326248169e-02 1.000000000000000000e+00 1.000000000000000000e+00 -1.137254908680915833e-01 1.137254908680915833e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.294117718935012817e-01 1.294117718935012817e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.450980454683303833e-01 1.450980454683303833e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.607843190431594849e-01 1.607843190431594849e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.764705926179885864e-01 1.764705926179885864e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.921568661928176880e-01 1.921568661928176880e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.078431397676467896e-01 2.078431397676467896e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.235294133424758911e-01 2.235294133424758911e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.392156869173049927e-01 2.392156869173049927e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.549019753932952881e-01 2.549019753932952881e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.705882489681243896e-01 2.705882489681243896e-01 1.000000000000000000e+00 1.000000000000000000e+00 -2.862745225429534912e-01 2.862745225429534912e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.019607961177825928e-01 3.019607961177825928e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.176470696926116943e-01 3.176470696926116943e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.333333432674407959e-01 3.333333432674407959e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.490196168422698975e-01 3.490196168422698975e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.647058904170989990e-01 3.647058904170989990e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.803921639919281006e-01 3.803921639919281006e-01 1.000000000000000000e+00 1.000000000000000000e+00 -3.960784375667572021e-01 3.960784375667572021e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.117647111415863037e-01 4.117647111415863037e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.274509847164154053e-01 4.274509847164154053e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.431372582912445068e-01 4.431372582912445068e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.588235318660736084e-01 4.588235318660736084e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.745098054409027100e-01 4.745098054409027100e-01 1.000000000000000000e+00 1.000000000000000000e+00 -4.901960790157318115e-01 4.901960790157318115e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.058823823928833008e-01 5.058823823928833008e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.215686559677124023e-01 5.215686559677124023e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.372549295425415039e-01 5.372549295425415039e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.529412031173706055e-01 5.529412031173706055e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.686274766921997070e-01 5.686274766921997070e-01 1.000000000000000000e+00 1.000000000000000000e+00 -5.843137502670288086e-01 5.843137502670288086e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.000000238418579102e-01 6.000000238418579102e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.156862974166870117e-01 6.156862974166870117e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.313725709915161133e-01 6.313725709915161133e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.470588445663452148e-01 6.470588445663452148e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.627451181411743164e-01 6.627451181411743164e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.784313917160034180e-01 6.784313917160034180e-01 1.000000000000000000e+00 1.000000000000000000e+00 -6.941176652908325195e-01 6.941176652908325195e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.098039388656616211e-01 7.098039388656616211e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.254902124404907227e-01 7.254902124404907227e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.411764860153198242e-01 7.411764860153198242e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.568627595901489258e-01 7.568627595901489258e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.725490331649780273e-01 7.725490331649780273e-01 1.000000000000000000e+00 1.000000000000000000e+00 -7.882353067398071289e-01 7.882353067398071289e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.039215803146362305e-01 8.039215803146362305e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.196078538894653320e-01 8.196078538894653320e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.352941274642944336e-01 8.352941274642944336e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.509804010391235352e-01 8.509804010391235352e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.666666746139526367e-01 8.666666746139526367e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.823529481887817383e-01 8.823529481887817383e-01 1.000000000000000000e+00 1.000000000000000000e+00 -8.980392217636108398e-01 8.980392217636108398e-01 1.000000000000000000e+00 1.000000000000000000e+00 -9.137254953384399414e-01 9.137254953384399414e-01 1.000000000000000000e+00 1.000000000000000000e+00 -9.294117689132690430e-01 9.294117689132690430e-01 1.000000000000000000e+00 1.000000000000000000e+00 -9.450980424880981445e-01 9.450980424880981445e-01 1.000000000000000000e+00 1.000000000000000000e+00 -9.607843160629272461e-01 9.607843160629272461e-01 1.000000000000000000e+00 1.000000000000000000e+00 -9.764705896377563477e-01 9.764705896377563477e-01 1.000000000000000000e+00 1.000000000000000000e+00 -9.921568632125854492e-01 9.921568632125854492e-01 1.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 9.921568632125854492e-01 9.921568632125854492e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.764705896377563477e-01 9.764705896377563477e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.607843160629272461e-01 9.607843160629272461e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.450980424880981445e-01 9.450980424880981445e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.294117689132690430e-01 9.294117689132690430e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.137254953384399414e-01 9.137254953384399414e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.980392217636108398e-01 8.980392217636108398e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.823529481887817383e-01 8.823529481887817383e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.666666746139526367e-01 8.666666746139526367e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.509804010391235352e-01 8.509804010391235352e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.352941274642944336e-01 8.352941274642944336e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.196078538894653320e-01 8.196078538894653320e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.039215803146362305e-01 8.039215803146362305e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.882353067398071289e-01 7.882353067398071289e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.725490331649780273e-01 7.725490331649780273e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.568627595901489258e-01 7.568627595901489258e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.411764860153198242e-01 7.411764860153198242e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.254902124404907227e-01 7.254902124404907227e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.098039388656616211e-01 7.098039388656616211e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.941176652908325195e-01 6.941176652908325195e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.784313917160034180e-01 6.784313917160034180e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.627451181411743164e-01 6.627451181411743164e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.470588445663452148e-01 6.470588445663452148e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.313725709915161133e-01 6.313725709915161133e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.156862974166870117e-01 6.156862974166870117e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.000000238418579102e-01 6.000000238418579102e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.843137502670288086e-01 5.843137502670288086e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.686274766921997070e-01 5.686274766921997070e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.529412031173706055e-01 5.529412031173706055e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.372549295425415039e-01 5.372549295425415039e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.215686559677124023e-01 5.215686559677124023e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.058823823928833008e-01 5.058823823928833008e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.901960790157318115e-01 4.901960790157318115e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.745098054409027100e-01 4.745098054409027100e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.588235318660736084e-01 4.588235318660736084e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.431372582912445068e-01 4.431372582912445068e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.274509847164154053e-01 4.274509847164154053e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.117647111415863037e-01 4.117647111415863037e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.960784375667572021e-01 3.960784375667572021e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.803921639919281006e-01 3.803921639919281006e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.647058904170989990e-01 3.647058904170989990e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.490196168422698975e-01 3.490196168422698975e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.333333432674407959e-01 3.333333432674407959e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.176470696926116943e-01 3.176470696926116943e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.019607961177825928e-01 3.019607961177825928e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.862745225429534912e-01 2.862745225429534912e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.705882489681243896e-01 2.705882489681243896e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.549019753932952881e-01 2.549019753932952881e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.392156869173049927e-01 2.392156869173049927e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.235294133424758911e-01 2.235294133424758911e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.078431397676467896e-01 2.078431397676467896e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.921568661928176880e-01 1.921568661928176880e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.764705926179885864e-01 1.764705926179885864e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.607843190431594849e-01 1.607843190431594849e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.450980454683303833e-01 1.450980454683303833e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.294117718935012817e-01 1.294117718935012817e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.137254908680915833e-01 1.137254908680915833e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.803921729326248169e-02 9.803921729326248169e-02 1.000000000000000000e+00 -1.000000000000000000e+00 8.235294371843338013e-02 8.235294371843338013e-02 1.000000000000000000e+00 -1.000000000000000000e+00 6.666667014360427856e-02 6.666667014360427856e-02 1.000000000000000000e+00 -1.000000000000000000e+00 5.098039284348487854e-02 5.098039284348487854e-02 1.000000000000000000e+00 -1.000000000000000000e+00 3.529411926865577698e-02 3.529411926865577698e-02 1.000000000000000000e+00 -1.000000000000000000e+00 1.960784383118152618e-02 1.960784383118152618e-02 1.000000000000000000e+00 -1.000000000000000000e+00 3.921568859368562698e-03 3.921568859368562698e-03 1.000000000000000000e+00 -9.941176176071166992e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.862744808197021484e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.784313440322875977e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.705882072448730469e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.627450704574584961e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.549019336700439453e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.470587968826293945e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.392156600952148438e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.313725233078002930e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.235293865203857422e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.156862497329711914e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -9.078431129455566406e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.999999761581420898e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.921568393707275391e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.843137025833129883e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.764705657958984375e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.686274290084838867e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.607842922210693359e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.529411554336547852e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.450980186462402344e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.372548818588256836e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.294117450714111328e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.215686082839965820e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.137254714965820312e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -8.058823347091674805e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.980391979217529297e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.901960611343383789e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.823529243469238281e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.745097875595092773e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.666666507720947266e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.588235139846801758e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.509803771972656250e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.431372404098510742e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.352941036224365234e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.274509668350219727e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.196078300476074219e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.117646932601928711e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -7.039215564727783203e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.960784196853637695e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.882352828979492188e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.803921461105346680e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.725490093231201172e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.647058725357055664e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.568627357482910156e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.490195989608764648e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.411764621734619141e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.333333253860473633e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.254901885986328125e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.176470518112182617e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.098039150238037109e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -6.019607782363891602e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.941176414489746094e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.862745046615600586e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.784313678741455078e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.705882310867309570e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.627450942993164062e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.549019575119018555e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.470588207244873047e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.392156839370727539e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.313725471496582031e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.235294103622436523e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.156862735748291016e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.078431367874145508e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 -5.000000000000000000e-01 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/spring b/fastplotlib/utils/colormaps/spring deleted file mode 100644 index fcec30dc6..000000000 --- a/fastplotlib/utils/colormaps/spring +++ /dev/null @@ -1,256 +0,0 @@ -1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -1.000000000000000000e+00 3.921568859368562698e-03 9.960784316062927246e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.843137718737125397e-03 9.921568632125854492e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.176470611244440079e-02 9.882352948188781738e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.568627543747425079e-02 9.843137264251708984e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.960784383118152618e-02 9.803921580314636230e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.352941222488880157e-02 9.764705896377563477e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.745098061859607697e-02 9.725490212440490723e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.137255087494850159e-02 9.686274528503417969e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.529411926865577698e-02 9.647058844566345215e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.921568766236305237e-02 9.607843160629272461e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.313725605607032776e-02 9.568627476692199707e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.705882444977760315e-02 9.529411792755126953e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.098039284348487854e-02 9.490196108818054199e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.490196123719215393e-02 9.450980424880981445e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.882352963089942932e-02 9.411764740943908691e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.274510174989700317e-02 9.372549057006835938e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.666667014360427856e-02 9.333333373069763184e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.058823853731155396e-02 9.294117689132690430e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.450980693101882935e-02 9.254902005195617676e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.843137532472610474e-02 9.215686321258544922e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.235294371843338013e-02 9.176470637321472168e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.627451211214065552e-02 9.137254953384399414e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.019608050584793091e-02 9.098039269447326660e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.411764889955520630e-02 9.058823585510253906e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.803921729326248169e-02 9.019607901573181152e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.019607856869697571e-01 8.980392217636108398e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.058823540806770325e-01 8.941176533699035645e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.098039224743843079e-01 8.901960849761962891e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.137254908680915833e-01 8.862745165824890137e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.176470592617988586e-01 8.823529481887817383e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.215686276555061340e-01 8.784313797950744629e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.254902034997940063e-01 8.745098114013671875e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.294117718935012817e-01 8.705882430076599121e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.333333402872085571e-01 8.666666746139526367e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.372549086809158325e-01 8.627451062202453613e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.411764770746231079e-01 8.588235378265380859e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.450980454683303833e-01 8.549019694328308105e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.490196138620376587e-01 8.509804010391235352e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.529411822557449341e-01 8.470588326454162598e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.568627506494522095e-01 8.431372642517089844e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.607843190431594849e-01 8.392156958580017090e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.647058874368667603e-01 8.352941274642944336e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.686274558305740356e-01 8.313725590705871582e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.725490242242813110e-01 8.274509906768798828e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.764705926179885864e-01 8.235294222831726074e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.803921610116958618e-01 8.196078538894653320e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.843137294054031372e-01 8.156862854957580566e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.882352977991104126e-01 8.117647171020507812e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.921568661928176880e-01 8.078431487083435059e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.960784345865249634e-01 8.039215803146362305e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.000000029802322388e-01 8.000000119209289551e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.039215713739395142e-01 7.960784435272216797e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.078431397676467896e-01 7.921568751335144043e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.117647081613540649e-01 7.882353067398071289e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.156862765550613403e-01 7.843137383460998535e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.196078449487686157e-01 7.803921699523925781e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.235294133424758911e-01 7.764706015586853027e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.274509817361831665e-01 7.725490331649780273e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.313725501298904419e-01 7.686274647712707520e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.352941185235977173e-01 7.647058963775634766e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.392156869173049927e-01 7.607843279838562012e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.431372553110122681e-01 7.568627595901489258e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.470588237047195435e-01 7.529411911964416504e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.509804069995880127e-01 7.490196228027343750e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.549019753932952881e-01 7.450980544090270996e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.588235437870025635e-01 7.411764860153198242e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.627451121807098389e-01 7.372549176216125488e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.666666805744171143e-01 7.333333492279052734e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.705882489681243896e-01 7.294117808341979980e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.745098173618316650e-01 7.254902124404907227e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.784313857555389404e-01 7.215686440467834473e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.823529541492462158e-01 7.176470756530761719e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.862745225429534912e-01 7.137255072593688965e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.901960909366607666e-01 7.098039388656616211e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.941176593303680420e-01 7.058823704719543457e-01 1.000000000000000000e+00 -1.000000000000000000e+00 2.980392277240753174e-01 7.019608020782470703e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.019607961177825928e-01 6.980392336845397949e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.058823645114898682e-01 6.941176652908325195e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.098039329051971436e-01 6.901960968971252441e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.137255012989044189e-01 6.862745285034179688e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.176470696926116943e-01 6.823529601097106934e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.215686380863189697e-01 6.784313917160034180e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.254902064800262451e-01 6.745098233222961426e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.294117748737335205e-01 6.705882549285888672e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.333333432674407959e-01 6.666666865348815918e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.372549116611480713e-01 6.627451181411743164e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.411764800548553467e-01 6.588235497474670410e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.450980484485626221e-01 6.549019813537597656e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.490196168422698975e-01 6.509804129600524902e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.529411852359771729e-01 6.470588445663452148e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.568627536296844482e-01 6.431372761726379395e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.607843220233917236e-01 6.392157077789306641e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.647058904170989990e-01 6.352941393852233887e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.686274588108062744e-01 6.313725709915161133e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.725490272045135498e-01 6.274510025978088379e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.764705955982208252e-01 6.235294342041015625e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.803921639919281006e-01 6.196078658103942871e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.843137323856353760e-01 6.156862974166870117e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.882353007793426514e-01 6.117647290229797363e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.921568691730499268e-01 6.078431606292724609e-01 1.000000000000000000e+00 -1.000000000000000000e+00 3.960784375667572021e-01 6.039215922355651855e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.000000059604644775e-01 6.000000238418579102e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.039215743541717529e-01 5.960784554481506348e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.078431427478790283e-01 5.921568870544433594e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.117647111415863037e-01 5.882353186607360840e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.156862795352935791e-01 5.843137502670288086e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.196078479290008545e-01 5.803921818733215332e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.235294163227081299e-01 5.764706134796142578e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.274509847164154053e-01 5.725490450859069824e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.313725531101226807e-01 5.686274766921997070e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.352941215038299561e-01 5.647059082984924316e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.392156898975372314e-01 5.607843399047851562e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.431372582912445068e-01 5.568627715110778809e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.470588266849517822e-01 5.529412031173706055e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.509803950786590576e-01 5.490196347236633301e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.549019634723663330e-01 5.450980663299560547e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.588235318660736084e-01 5.411764979362487793e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.627451002597808838e-01 5.372549295425415039e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.666666686534881592e-01 5.333333611488342285e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.705882370471954346e-01 5.294117927551269531e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.745098054409027100e-01 5.254902243614196777e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.784313738346099854e-01 5.215686559677124023e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.823529422283172607e-01 5.176470875740051270e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.862745106220245361e-01 5.137255191802978516e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.901960790157318115e-01 5.098039507865905762e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.941176474094390869e-01 5.058823823928833008e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.980392158031463623e-01 5.019608139991760254e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.019608139991760254e-01 4.980392158031463623e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.058823823928833008e-01 4.941176474094390869e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.098039507865905762e-01 4.901960790157318115e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.137255191802978516e-01 4.862745106220245361e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.176470875740051270e-01 4.823529422283172607e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.215686559677124023e-01 4.784313738346099854e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.254902243614196777e-01 4.745098054409027100e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.294117927551269531e-01 4.705882370471954346e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.333333611488342285e-01 4.666666686534881592e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.372549295425415039e-01 4.627451002597808838e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.411764979362487793e-01 4.588235318660736084e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.450980663299560547e-01 4.549019634723663330e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.490196347236633301e-01 4.509803950786590576e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.529412031173706055e-01 4.470588266849517822e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.568627715110778809e-01 4.431372582912445068e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.607843399047851562e-01 4.392156898975372314e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.647059082984924316e-01 4.352941215038299561e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.686274766921997070e-01 4.313725531101226807e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.725490450859069824e-01 4.274509847164154053e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.764706134796142578e-01 4.235294163227081299e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.803921818733215332e-01 4.196078479290008545e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.843137502670288086e-01 4.156862795352935791e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.882353186607360840e-01 4.117647111415863037e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.921568870544433594e-01 4.078431427478790283e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.960784554481506348e-01 4.039215743541717529e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.000000238418579102e-01 4.000000059604644775e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.039215922355651855e-01 3.960784375667572021e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.078431606292724609e-01 3.921568691730499268e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.117647290229797363e-01 3.882353007793426514e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.156862974166870117e-01 3.843137323856353760e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.196078658103942871e-01 3.803921639919281006e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.235294342041015625e-01 3.764705955982208252e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.274510025978088379e-01 3.725490272045135498e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.313725709915161133e-01 3.686274588108062744e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.352941393852233887e-01 3.647058904170989990e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.392157077789306641e-01 3.607843220233917236e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.431372761726379395e-01 3.568627536296844482e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.470588445663452148e-01 3.529411852359771729e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.509804129600524902e-01 3.490196168422698975e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.549019813537597656e-01 3.450980484485626221e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.588235497474670410e-01 3.411764800548553467e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.627451181411743164e-01 3.372549116611480713e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.666666865348815918e-01 3.333333432674407959e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.705882549285888672e-01 3.294117748737335205e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.745098233222961426e-01 3.254902064800262451e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.784313917160034180e-01 3.215686380863189697e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.823529601097106934e-01 3.176470696926116943e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.862745285034179688e-01 3.137255012989044189e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.901960968971252441e-01 3.098039329051971436e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.941176652908325195e-01 3.058823645114898682e-01 1.000000000000000000e+00 -1.000000000000000000e+00 6.980392336845397949e-01 3.019607961177825928e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.019608020782470703e-01 2.980392277240753174e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.058823704719543457e-01 2.941176593303680420e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.098039388656616211e-01 2.901960909366607666e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.137255072593688965e-01 2.862745225429534912e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.176470756530761719e-01 2.823529541492462158e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.215686440467834473e-01 2.784313857555389404e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.254902124404907227e-01 2.745098173618316650e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.294117808341979980e-01 2.705882489681243896e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.333333492279052734e-01 2.666666805744171143e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.372549176216125488e-01 2.627451121807098389e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.411764860153198242e-01 2.588235437870025635e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.450980544090270996e-01 2.549019753932952881e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.490196228027343750e-01 2.509804069995880127e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.529411911964416504e-01 2.470588237047195435e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.568627595901489258e-01 2.431372553110122681e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.607843279838562012e-01 2.392156869173049927e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.647058963775634766e-01 2.352941185235977173e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.686274647712707520e-01 2.313725501298904419e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.725490331649780273e-01 2.274509817361831665e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.764706015586853027e-01 2.235294133424758911e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.803921699523925781e-01 2.196078449487686157e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.843137383460998535e-01 2.156862765550613403e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.882353067398071289e-01 2.117647081613540649e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.921568751335144043e-01 2.078431397676467896e-01 1.000000000000000000e+00 -1.000000000000000000e+00 7.960784435272216797e-01 2.039215713739395142e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.000000119209289551e-01 2.000000029802322388e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.039215803146362305e-01 1.960784345865249634e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.078431487083435059e-01 1.921568661928176880e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.117647171020507812e-01 1.882352977991104126e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.156862854957580566e-01 1.843137294054031372e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.196078538894653320e-01 1.803921610116958618e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.235294222831726074e-01 1.764705926179885864e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.274509906768798828e-01 1.725490242242813110e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.313725590705871582e-01 1.686274558305740356e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.352941274642944336e-01 1.647058874368667603e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.392156958580017090e-01 1.607843190431594849e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.431372642517089844e-01 1.568627506494522095e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.470588326454162598e-01 1.529411822557449341e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.509804010391235352e-01 1.490196138620376587e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.549019694328308105e-01 1.450980454683303833e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.588235378265380859e-01 1.411764770746231079e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.627451062202453613e-01 1.372549086809158325e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.666666746139526367e-01 1.333333402872085571e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.705882430076599121e-01 1.294117718935012817e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.745098114013671875e-01 1.254902034997940063e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.784313797950744629e-01 1.215686276555061340e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.823529481887817383e-01 1.176470592617988586e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.862745165824890137e-01 1.137254908680915833e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.901960849761962891e-01 1.098039224743843079e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.941176533699035645e-01 1.058823540806770325e-01 1.000000000000000000e+00 -1.000000000000000000e+00 8.980392217636108398e-01 1.019607856869697571e-01 1.000000000000000000e+00 -1.000000000000000000e+00 9.019607901573181152e-01 9.803921729326248169e-02 1.000000000000000000e+00 -1.000000000000000000e+00 9.058823585510253906e-01 9.411764889955520630e-02 1.000000000000000000e+00 -1.000000000000000000e+00 9.098039269447326660e-01 9.019608050584793091e-02 1.000000000000000000e+00 -1.000000000000000000e+00 9.137254953384399414e-01 8.627451211214065552e-02 1.000000000000000000e+00 -1.000000000000000000e+00 9.176470637321472168e-01 8.235294371843338013e-02 1.000000000000000000e+00 -1.000000000000000000e+00 9.215686321258544922e-01 7.843137532472610474e-02 1.000000000000000000e+00 -1.000000000000000000e+00 9.254902005195617676e-01 7.450980693101882935e-02 1.000000000000000000e+00 -1.000000000000000000e+00 9.294117689132690430e-01 7.058823853731155396e-02 1.000000000000000000e+00 -1.000000000000000000e+00 9.333333373069763184e-01 6.666667014360427856e-02 1.000000000000000000e+00 -1.000000000000000000e+00 9.372549057006835938e-01 6.274510174989700317e-02 1.000000000000000000e+00 -1.000000000000000000e+00 9.411764740943908691e-01 5.882352963089942932e-02 1.000000000000000000e+00 -1.000000000000000000e+00 9.450980424880981445e-01 5.490196123719215393e-02 1.000000000000000000e+00 -1.000000000000000000e+00 9.490196108818054199e-01 5.098039284348487854e-02 1.000000000000000000e+00 -1.000000000000000000e+00 9.529411792755126953e-01 4.705882444977760315e-02 1.000000000000000000e+00 -1.000000000000000000e+00 9.568627476692199707e-01 4.313725605607032776e-02 1.000000000000000000e+00 -1.000000000000000000e+00 9.607843160629272461e-01 3.921568766236305237e-02 1.000000000000000000e+00 -1.000000000000000000e+00 9.647058844566345215e-01 3.529411926865577698e-02 1.000000000000000000e+00 -1.000000000000000000e+00 9.686274528503417969e-01 3.137255087494850159e-02 1.000000000000000000e+00 -1.000000000000000000e+00 9.725490212440490723e-01 2.745098061859607697e-02 1.000000000000000000e+00 -1.000000000000000000e+00 9.764705896377563477e-01 2.352941222488880157e-02 1.000000000000000000e+00 -1.000000000000000000e+00 9.803921580314636230e-01 1.960784383118152618e-02 1.000000000000000000e+00 -1.000000000000000000e+00 9.843137264251708984e-01 1.568627543747425079e-02 1.000000000000000000e+00 -1.000000000000000000e+00 9.882352948188781738e-01 1.176470611244440079e-02 1.000000000000000000e+00 -1.000000000000000000e+00 9.921568632125854492e-01 7.843137718737125397e-03 1.000000000000000000e+00 -1.000000000000000000e+00 9.960784316062927246e-01 3.921568859368562698e-03 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/summer b/fastplotlib/utils/colormaps/summer deleted file mode 100644 index 6fab4e585..000000000 --- a/fastplotlib/utils/colormaps/summer +++ /dev/null @@ -1,256 +0,0 @@ -0.000000000000000000e+00 5.000000000000000000e-01 4.000000059604644775e-01 1.000000000000000000e+00 -3.921568859368562698e-03 5.019608139991760254e-01 4.000000059604644775e-01 1.000000000000000000e+00 -7.843137718737125397e-03 5.039215683937072754e-01 4.000000059604644775e-01 1.000000000000000000e+00 -1.176470611244440079e-02 5.058823823928833008e-01 4.000000059604644775e-01 1.000000000000000000e+00 -1.568627543747425079e-02 5.078431367874145508e-01 4.000000059604644775e-01 1.000000000000000000e+00 -1.960784383118152618e-02 5.098039507865905762e-01 4.000000059604644775e-01 1.000000000000000000e+00 -2.352941222488880157e-02 5.117647051811218262e-01 4.000000059604644775e-01 1.000000000000000000e+00 -2.745098061859607697e-02 5.137255191802978516e-01 4.000000059604644775e-01 1.000000000000000000e+00 -3.137255087494850159e-02 5.156862735748291016e-01 4.000000059604644775e-01 1.000000000000000000e+00 -3.529411926865577698e-02 5.176470875740051270e-01 4.000000059604644775e-01 1.000000000000000000e+00 -3.921568766236305237e-02 5.196078419685363770e-01 4.000000059604644775e-01 1.000000000000000000e+00 -4.313725605607032776e-02 5.215686559677124023e-01 4.000000059604644775e-01 1.000000000000000000e+00 -4.705882444977760315e-02 5.235294103622436523e-01 4.000000059604644775e-01 1.000000000000000000e+00 -5.098039284348487854e-02 5.254902243614196777e-01 4.000000059604644775e-01 1.000000000000000000e+00 -5.490196123719215393e-02 5.274509787559509277e-01 4.000000059604644775e-01 1.000000000000000000e+00 -5.882352963089942932e-02 5.294117927551269531e-01 4.000000059604644775e-01 1.000000000000000000e+00 -6.274510174989700317e-02 5.313725471496582031e-01 4.000000059604644775e-01 1.000000000000000000e+00 -6.666667014360427856e-02 5.333333611488342285e-01 4.000000059604644775e-01 1.000000000000000000e+00 -7.058823853731155396e-02 5.352941155433654785e-01 4.000000059604644775e-01 1.000000000000000000e+00 -7.450980693101882935e-02 5.372549295425415039e-01 4.000000059604644775e-01 1.000000000000000000e+00 -7.843137532472610474e-02 5.392156839370727539e-01 4.000000059604644775e-01 1.000000000000000000e+00 -8.235294371843338013e-02 5.411764979362487793e-01 4.000000059604644775e-01 1.000000000000000000e+00 -8.627451211214065552e-02 5.431372523307800293e-01 4.000000059604644775e-01 1.000000000000000000e+00 -9.019608050584793091e-02 5.450980663299560547e-01 4.000000059604644775e-01 1.000000000000000000e+00 -9.411764889955520630e-02 5.470588207244873047e-01 4.000000059604644775e-01 1.000000000000000000e+00 -9.803921729326248169e-02 5.490196347236633301e-01 4.000000059604644775e-01 1.000000000000000000e+00 -1.019607856869697571e-01 5.509803891181945801e-01 4.000000059604644775e-01 1.000000000000000000e+00 -1.058823540806770325e-01 5.529412031173706055e-01 4.000000059604644775e-01 1.000000000000000000e+00 -1.098039224743843079e-01 5.549019575119018555e-01 4.000000059604644775e-01 1.000000000000000000e+00 -1.137254908680915833e-01 5.568627715110778809e-01 4.000000059604644775e-01 1.000000000000000000e+00 -1.176470592617988586e-01 5.588235259056091309e-01 4.000000059604644775e-01 1.000000000000000000e+00 -1.215686276555061340e-01 5.607843399047851562e-01 4.000000059604644775e-01 1.000000000000000000e+00 -1.254902034997940063e-01 5.627450942993164062e-01 4.000000059604644775e-01 1.000000000000000000e+00 -1.294117718935012817e-01 5.647059082984924316e-01 4.000000059604644775e-01 1.000000000000000000e+00 -1.333333402872085571e-01 5.666666626930236816e-01 4.000000059604644775e-01 1.000000000000000000e+00 -1.372549086809158325e-01 5.686274766921997070e-01 4.000000059604644775e-01 1.000000000000000000e+00 -1.411764770746231079e-01 5.705882310867309570e-01 4.000000059604644775e-01 1.000000000000000000e+00 -1.450980454683303833e-01 5.725490450859069824e-01 4.000000059604644775e-01 1.000000000000000000e+00 -1.490196138620376587e-01 5.745097994804382324e-01 4.000000059604644775e-01 1.000000000000000000e+00 -1.529411822557449341e-01 5.764706134796142578e-01 4.000000059604644775e-01 1.000000000000000000e+00 -1.568627506494522095e-01 5.784313678741455078e-01 4.000000059604644775e-01 1.000000000000000000e+00 -1.607843190431594849e-01 5.803921818733215332e-01 4.000000059604644775e-01 1.000000000000000000e+00 -1.647058874368667603e-01 5.823529362678527832e-01 4.000000059604644775e-01 1.000000000000000000e+00 -1.686274558305740356e-01 5.843137502670288086e-01 4.000000059604644775e-01 1.000000000000000000e+00 -1.725490242242813110e-01 5.862745046615600586e-01 4.000000059604644775e-01 1.000000000000000000e+00 -1.764705926179885864e-01 5.882353186607360840e-01 4.000000059604644775e-01 1.000000000000000000e+00 -1.803921610116958618e-01 5.901960730552673340e-01 4.000000059604644775e-01 1.000000000000000000e+00 -1.843137294054031372e-01 5.921568870544433594e-01 4.000000059604644775e-01 1.000000000000000000e+00 -1.882352977991104126e-01 5.941176414489746094e-01 4.000000059604644775e-01 1.000000000000000000e+00 -1.921568661928176880e-01 5.960784554481506348e-01 4.000000059604644775e-01 1.000000000000000000e+00 -1.960784345865249634e-01 5.980392098426818848e-01 4.000000059604644775e-01 1.000000000000000000e+00 -2.000000029802322388e-01 6.000000238418579102e-01 4.000000059604644775e-01 1.000000000000000000e+00 -2.039215713739395142e-01 6.019607782363891602e-01 4.000000059604644775e-01 1.000000000000000000e+00 -2.078431397676467896e-01 6.039215922355651855e-01 4.000000059604644775e-01 1.000000000000000000e+00 -2.117647081613540649e-01 6.058823466300964355e-01 4.000000059604644775e-01 1.000000000000000000e+00 -2.156862765550613403e-01 6.078431606292724609e-01 4.000000059604644775e-01 1.000000000000000000e+00 -2.196078449487686157e-01 6.098039150238037109e-01 4.000000059604644775e-01 1.000000000000000000e+00 -2.235294133424758911e-01 6.117647290229797363e-01 4.000000059604644775e-01 1.000000000000000000e+00 -2.274509817361831665e-01 6.137254834175109863e-01 4.000000059604644775e-01 1.000000000000000000e+00 -2.313725501298904419e-01 6.156862974166870117e-01 4.000000059604644775e-01 1.000000000000000000e+00 -2.352941185235977173e-01 6.176470518112182617e-01 4.000000059604644775e-01 1.000000000000000000e+00 -2.392156869173049927e-01 6.196078658103942871e-01 4.000000059604644775e-01 1.000000000000000000e+00 -2.431372553110122681e-01 6.215686202049255371e-01 4.000000059604644775e-01 1.000000000000000000e+00 -2.470588237047195435e-01 6.235294342041015625e-01 4.000000059604644775e-01 1.000000000000000000e+00 -2.509804069995880127e-01 6.254901885986328125e-01 4.000000059604644775e-01 1.000000000000000000e+00 -2.549019753932952881e-01 6.274510025978088379e-01 4.000000059604644775e-01 1.000000000000000000e+00 -2.588235437870025635e-01 6.294117569923400879e-01 4.000000059604644775e-01 1.000000000000000000e+00 -2.627451121807098389e-01 6.313725709915161133e-01 4.000000059604644775e-01 1.000000000000000000e+00 -2.666666805744171143e-01 6.333333253860473633e-01 4.000000059604644775e-01 1.000000000000000000e+00 -2.705882489681243896e-01 6.352941393852233887e-01 4.000000059604644775e-01 1.000000000000000000e+00 -2.745098173618316650e-01 6.372548937797546387e-01 4.000000059604644775e-01 1.000000000000000000e+00 -2.784313857555389404e-01 6.392157077789306641e-01 4.000000059604644775e-01 1.000000000000000000e+00 -2.823529541492462158e-01 6.411764621734619141e-01 4.000000059604644775e-01 1.000000000000000000e+00 -2.862745225429534912e-01 6.431372761726379395e-01 4.000000059604644775e-01 1.000000000000000000e+00 -2.901960909366607666e-01 6.450980305671691895e-01 4.000000059604644775e-01 1.000000000000000000e+00 -2.941176593303680420e-01 6.470588445663452148e-01 4.000000059604644775e-01 1.000000000000000000e+00 -2.980392277240753174e-01 6.490195989608764648e-01 4.000000059604644775e-01 1.000000000000000000e+00 -3.019607961177825928e-01 6.509804129600524902e-01 4.000000059604644775e-01 1.000000000000000000e+00 -3.058823645114898682e-01 6.529411673545837402e-01 4.000000059604644775e-01 1.000000000000000000e+00 -3.098039329051971436e-01 6.549019813537597656e-01 4.000000059604644775e-01 1.000000000000000000e+00 -3.137255012989044189e-01 6.568627357482910156e-01 4.000000059604644775e-01 1.000000000000000000e+00 -3.176470696926116943e-01 6.588235497474670410e-01 4.000000059604644775e-01 1.000000000000000000e+00 -3.215686380863189697e-01 6.607843041419982910e-01 4.000000059604644775e-01 1.000000000000000000e+00 -3.254902064800262451e-01 6.627451181411743164e-01 4.000000059604644775e-01 1.000000000000000000e+00 -3.294117748737335205e-01 6.647058725357055664e-01 4.000000059604644775e-01 1.000000000000000000e+00 -3.333333432674407959e-01 6.666666865348815918e-01 4.000000059604644775e-01 1.000000000000000000e+00 -3.372549116611480713e-01 6.686274409294128418e-01 4.000000059604644775e-01 1.000000000000000000e+00 -3.411764800548553467e-01 6.705882549285888672e-01 4.000000059604644775e-01 1.000000000000000000e+00 -3.450980484485626221e-01 6.725490093231201172e-01 4.000000059604644775e-01 1.000000000000000000e+00 -3.490196168422698975e-01 6.745098233222961426e-01 4.000000059604644775e-01 1.000000000000000000e+00 -3.529411852359771729e-01 6.764705777168273926e-01 4.000000059604644775e-01 1.000000000000000000e+00 -3.568627536296844482e-01 6.784313917160034180e-01 4.000000059604644775e-01 1.000000000000000000e+00 -3.607843220233917236e-01 6.803921461105346680e-01 4.000000059604644775e-01 1.000000000000000000e+00 -3.647058904170989990e-01 6.823529601097106934e-01 4.000000059604644775e-01 1.000000000000000000e+00 -3.686274588108062744e-01 6.843137145042419434e-01 4.000000059604644775e-01 1.000000000000000000e+00 -3.725490272045135498e-01 6.862745285034179688e-01 4.000000059604644775e-01 1.000000000000000000e+00 -3.764705955982208252e-01 6.882352828979492188e-01 4.000000059604644775e-01 1.000000000000000000e+00 -3.803921639919281006e-01 6.901960968971252441e-01 4.000000059604644775e-01 1.000000000000000000e+00 -3.843137323856353760e-01 6.921568512916564941e-01 4.000000059604644775e-01 1.000000000000000000e+00 -3.882353007793426514e-01 6.941176652908325195e-01 4.000000059604644775e-01 1.000000000000000000e+00 -3.921568691730499268e-01 6.960784196853637695e-01 4.000000059604644775e-01 1.000000000000000000e+00 -3.960784375667572021e-01 6.980392336845397949e-01 4.000000059604644775e-01 1.000000000000000000e+00 -4.000000059604644775e-01 6.999999880790710449e-01 4.000000059604644775e-01 1.000000000000000000e+00 -4.039215743541717529e-01 7.019608020782470703e-01 4.000000059604644775e-01 1.000000000000000000e+00 -4.078431427478790283e-01 7.039215564727783203e-01 4.000000059604644775e-01 1.000000000000000000e+00 -4.117647111415863037e-01 7.058823704719543457e-01 4.000000059604644775e-01 1.000000000000000000e+00 -4.156862795352935791e-01 7.078431248664855957e-01 4.000000059604644775e-01 1.000000000000000000e+00 -4.196078479290008545e-01 7.098039388656616211e-01 4.000000059604644775e-01 1.000000000000000000e+00 -4.235294163227081299e-01 7.117646932601928711e-01 4.000000059604644775e-01 1.000000000000000000e+00 -4.274509847164154053e-01 7.137255072593688965e-01 4.000000059604644775e-01 1.000000000000000000e+00 -4.313725531101226807e-01 7.156862616539001465e-01 4.000000059604644775e-01 1.000000000000000000e+00 -4.352941215038299561e-01 7.176470756530761719e-01 4.000000059604644775e-01 1.000000000000000000e+00 -4.392156898975372314e-01 7.196078300476074219e-01 4.000000059604644775e-01 1.000000000000000000e+00 -4.431372582912445068e-01 7.215686440467834473e-01 4.000000059604644775e-01 1.000000000000000000e+00 -4.470588266849517822e-01 7.235293984413146973e-01 4.000000059604644775e-01 1.000000000000000000e+00 -4.509803950786590576e-01 7.254902124404907227e-01 4.000000059604644775e-01 1.000000000000000000e+00 -4.549019634723663330e-01 7.274509668350219727e-01 4.000000059604644775e-01 1.000000000000000000e+00 -4.588235318660736084e-01 7.294117808341979980e-01 4.000000059604644775e-01 1.000000000000000000e+00 -4.627451002597808838e-01 7.313725352287292480e-01 4.000000059604644775e-01 1.000000000000000000e+00 -4.666666686534881592e-01 7.333333492279052734e-01 4.000000059604644775e-01 1.000000000000000000e+00 -4.705882370471954346e-01 7.352941036224365234e-01 4.000000059604644775e-01 1.000000000000000000e+00 -4.745098054409027100e-01 7.372549176216125488e-01 4.000000059604644775e-01 1.000000000000000000e+00 -4.784313738346099854e-01 7.392156720161437988e-01 4.000000059604644775e-01 1.000000000000000000e+00 -4.823529422283172607e-01 7.411764860153198242e-01 4.000000059604644775e-01 1.000000000000000000e+00 -4.862745106220245361e-01 7.431372404098510742e-01 4.000000059604644775e-01 1.000000000000000000e+00 -4.901960790157318115e-01 7.450980544090270996e-01 4.000000059604644775e-01 1.000000000000000000e+00 -4.941176474094390869e-01 7.470588088035583496e-01 4.000000059604644775e-01 1.000000000000000000e+00 -4.980392158031463623e-01 7.490196228027343750e-01 4.000000059604644775e-01 1.000000000000000000e+00 -5.019608139991760254e-01 7.509803771972656250e-01 4.000000059604644775e-01 1.000000000000000000e+00 -5.058823823928833008e-01 7.529411911964416504e-01 4.000000059604644775e-01 1.000000000000000000e+00 -5.098039507865905762e-01 7.549019455909729004e-01 4.000000059604644775e-01 1.000000000000000000e+00 -5.137255191802978516e-01 7.568627595901489258e-01 4.000000059604644775e-01 1.000000000000000000e+00 -5.176470875740051270e-01 7.588235139846801758e-01 4.000000059604644775e-01 1.000000000000000000e+00 -5.215686559677124023e-01 7.607843279838562012e-01 4.000000059604644775e-01 1.000000000000000000e+00 -5.254902243614196777e-01 7.627450823783874512e-01 4.000000059604644775e-01 1.000000000000000000e+00 -5.294117927551269531e-01 7.647058963775634766e-01 4.000000059604644775e-01 1.000000000000000000e+00 -5.333333611488342285e-01 7.666666507720947266e-01 4.000000059604644775e-01 1.000000000000000000e+00 -5.372549295425415039e-01 7.686274647712707520e-01 4.000000059604644775e-01 1.000000000000000000e+00 -5.411764979362487793e-01 7.705882191658020020e-01 4.000000059604644775e-01 1.000000000000000000e+00 -5.450980663299560547e-01 7.725490331649780273e-01 4.000000059604644775e-01 1.000000000000000000e+00 -5.490196347236633301e-01 7.745097875595092773e-01 4.000000059604644775e-01 1.000000000000000000e+00 -5.529412031173706055e-01 7.764706015586853027e-01 4.000000059604644775e-01 1.000000000000000000e+00 -5.568627715110778809e-01 7.784313559532165527e-01 4.000000059604644775e-01 1.000000000000000000e+00 -5.607843399047851562e-01 7.803921699523925781e-01 4.000000059604644775e-01 1.000000000000000000e+00 -5.647059082984924316e-01 7.823529243469238281e-01 4.000000059604644775e-01 1.000000000000000000e+00 -5.686274766921997070e-01 7.843137383460998535e-01 4.000000059604644775e-01 1.000000000000000000e+00 -5.725490450859069824e-01 7.862744927406311035e-01 4.000000059604644775e-01 1.000000000000000000e+00 -5.764706134796142578e-01 7.882353067398071289e-01 4.000000059604644775e-01 1.000000000000000000e+00 -5.803921818733215332e-01 7.901960611343383789e-01 4.000000059604644775e-01 1.000000000000000000e+00 -5.843137502670288086e-01 7.921568751335144043e-01 4.000000059604644775e-01 1.000000000000000000e+00 -5.882353186607360840e-01 7.941176295280456543e-01 4.000000059604644775e-01 1.000000000000000000e+00 -5.921568870544433594e-01 7.960784435272216797e-01 4.000000059604644775e-01 1.000000000000000000e+00 -5.960784554481506348e-01 7.980391979217529297e-01 4.000000059604644775e-01 1.000000000000000000e+00 -6.000000238418579102e-01 8.000000119209289551e-01 4.000000059604644775e-01 1.000000000000000000e+00 -6.039215922355651855e-01 8.019607663154602051e-01 4.000000059604644775e-01 1.000000000000000000e+00 -6.078431606292724609e-01 8.039215803146362305e-01 4.000000059604644775e-01 1.000000000000000000e+00 -6.117647290229797363e-01 8.058823347091674805e-01 4.000000059604644775e-01 1.000000000000000000e+00 -6.156862974166870117e-01 8.078431487083435059e-01 4.000000059604644775e-01 1.000000000000000000e+00 -6.196078658103942871e-01 8.098039031028747559e-01 4.000000059604644775e-01 1.000000000000000000e+00 -6.235294342041015625e-01 8.117647171020507812e-01 4.000000059604644775e-01 1.000000000000000000e+00 -6.274510025978088379e-01 8.137254714965820312e-01 4.000000059604644775e-01 1.000000000000000000e+00 -6.313725709915161133e-01 8.156862854957580566e-01 4.000000059604644775e-01 1.000000000000000000e+00 -6.352941393852233887e-01 8.176470398902893066e-01 4.000000059604644775e-01 1.000000000000000000e+00 -6.392157077789306641e-01 8.196078538894653320e-01 4.000000059604644775e-01 1.000000000000000000e+00 -6.431372761726379395e-01 8.215686082839965820e-01 4.000000059604644775e-01 1.000000000000000000e+00 -6.470588445663452148e-01 8.235294222831726074e-01 4.000000059604644775e-01 1.000000000000000000e+00 -6.509804129600524902e-01 8.254901766777038574e-01 4.000000059604644775e-01 1.000000000000000000e+00 -6.549019813537597656e-01 8.274509906768798828e-01 4.000000059604644775e-01 1.000000000000000000e+00 -6.588235497474670410e-01 8.294117450714111328e-01 4.000000059604644775e-01 1.000000000000000000e+00 -6.627451181411743164e-01 8.313725590705871582e-01 4.000000059604644775e-01 1.000000000000000000e+00 -6.666666865348815918e-01 8.333333134651184082e-01 4.000000059604644775e-01 1.000000000000000000e+00 -6.705882549285888672e-01 8.352941274642944336e-01 4.000000059604644775e-01 1.000000000000000000e+00 -6.745098233222961426e-01 8.372548818588256836e-01 4.000000059604644775e-01 1.000000000000000000e+00 -6.784313917160034180e-01 8.392156958580017090e-01 4.000000059604644775e-01 1.000000000000000000e+00 -6.823529601097106934e-01 8.411764502525329590e-01 4.000000059604644775e-01 1.000000000000000000e+00 -6.862745285034179688e-01 8.431372642517089844e-01 4.000000059604644775e-01 1.000000000000000000e+00 -6.901960968971252441e-01 8.450980186462402344e-01 4.000000059604644775e-01 1.000000000000000000e+00 -6.941176652908325195e-01 8.470588326454162598e-01 4.000000059604644775e-01 1.000000000000000000e+00 -6.980392336845397949e-01 8.490195870399475098e-01 4.000000059604644775e-01 1.000000000000000000e+00 -7.019608020782470703e-01 8.509804010391235352e-01 4.000000059604644775e-01 1.000000000000000000e+00 -7.058823704719543457e-01 8.529411554336547852e-01 4.000000059604644775e-01 1.000000000000000000e+00 -7.098039388656616211e-01 8.549019694328308105e-01 4.000000059604644775e-01 1.000000000000000000e+00 -7.137255072593688965e-01 8.568627238273620605e-01 4.000000059604644775e-01 1.000000000000000000e+00 -7.176470756530761719e-01 8.588235378265380859e-01 4.000000059604644775e-01 1.000000000000000000e+00 -7.215686440467834473e-01 8.607842922210693359e-01 4.000000059604644775e-01 1.000000000000000000e+00 -7.254902124404907227e-01 8.627451062202453613e-01 4.000000059604644775e-01 1.000000000000000000e+00 -7.294117808341979980e-01 8.647058606147766113e-01 4.000000059604644775e-01 1.000000000000000000e+00 -7.333333492279052734e-01 8.666666746139526367e-01 4.000000059604644775e-01 1.000000000000000000e+00 -7.372549176216125488e-01 8.686274290084838867e-01 4.000000059604644775e-01 1.000000000000000000e+00 -7.411764860153198242e-01 8.705882430076599121e-01 4.000000059604644775e-01 1.000000000000000000e+00 -7.450980544090270996e-01 8.725489974021911621e-01 4.000000059604644775e-01 1.000000000000000000e+00 -7.490196228027343750e-01 8.745098114013671875e-01 4.000000059604644775e-01 1.000000000000000000e+00 -7.529411911964416504e-01 8.764705657958984375e-01 4.000000059604644775e-01 1.000000000000000000e+00 -7.568627595901489258e-01 8.784313797950744629e-01 4.000000059604644775e-01 1.000000000000000000e+00 -7.607843279838562012e-01 8.803921341896057129e-01 4.000000059604644775e-01 1.000000000000000000e+00 -7.647058963775634766e-01 8.823529481887817383e-01 4.000000059604644775e-01 1.000000000000000000e+00 -7.686274647712707520e-01 8.843137025833129883e-01 4.000000059604644775e-01 1.000000000000000000e+00 -7.725490331649780273e-01 8.862745165824890137e-01 4.000000059604644775e-01 1.000000000000000000e+00 -7.764706015586853027e-01 8.882352709770202637e-01 4.000000059604644775e-01 1.000000000000000000e+00 -7.803921699523925781e-01 8.901960849761962891e-01 4.000000059604644775e-01 1.000000000000000000e+00 -7.843137383460998535e-01 8.921568393707275391e-01 4.000000059604644775e-01 1.000000000000000000e+00 -7.882353067398071289e-01 8.941176533699035645e-01 4.000000059604644775e-01 1.000000000000000000e+00 -7.921568751335144043e-01 8.960784077644348145e-01 4.000000059604644775e-01 1.000000000000000000e+00 -7.960784435272216797e-01 8.980392217636108398e-01 4.000000059604644775e-01 1.000000000000000000e+00 -8.000000119209289551e-01 8.999999761581420898e-01 4.000000059604644775e-01 1.000000000000000000e+00 -8.039215803146362305e-01 9.019607901573181152e-01 4.000000059604644775e-01 1.000000000000000000e+00 -8.078431487083435059e-01 9.039215445518493652e-01 4.000000059604644775e-01 1.000000000000000000e+00 -8.117647171020507812e-01 9.058823585510253906e-01 4.000000059604644775e-01 1.000000000000000000e+00 -8.156862854957580566e-01 9.078431129455566406e-01 4.000000059604644775e-01 1.000000000000000000e+00 -8.196078538894653320e-01 9.098039269447326660e-01 4.000000059604644775e-01 1.000000000000000000e+00 -8.235294222831726074e-01 9.117646813392639160e-01 4.000000059604644775e-01 1.000000000000000000e+00 -8.274509906768798828e-01 9.137254953384399414e-01 4.000000059604644775e-01 1.000000000000000000e+00 -8.313725590705871582e-01 9.156862497329711914e-01 4.000000059604644775e-01 1.000000000000000000e+00 -8.352941274642944336e-01 9.176470637321472168e-01 4.000000059604644775e-01 1.000000000000000000e+00 -8.392156958580017090e-01 9.196078181266784668e-01 4.000000059604644775e-01 1.000000000000000000e+00 -8.431372642517089844e-01 9.215686321258544922e-01 4.000000059604644775e-01 1.000000000000000000e+00 -8.470588326454162598e-01 9.235293865203857422e-01 4.000000059604644775e-01 1.000000000000000000e+00 -8.509804010391235352e-01 9.254902005195617676e-01 4.000000059604644775e-01 1.000000000000000000e+00 -8.549019694328308105e-01 9.274509549140930176e-01 4.000000059604644775e-01 1.000000000000000000e+00 -8.588235378265380859e-01 9.294117689132690430e-01 4.000000059604644775e-01 1.000000000000000000e+00 -8.627451062202453613e-01 9.313725233078002930e-01 4.000000059604644775e-01 1.000000000000000000e+00 -8.666666746139526367e-01 9.333333373069763184e-01 4.000000059604644775e-01 1.000000000000000000e+00 -8.705882430076599121e-01 9.352940917015075684e-01 4.000000059604644775e-01 1.000000000000000000e+00 -8.745098114013671875e-01 9.372549057006835938e-01 4.000000059604644775e-01 1.000000000000000000e+00 -8.784313797950744629e-01 9.392156600952148438e-01 4.000000059604644775e-01 1.000000000000000000e+00 -8.823529481887817383e-01 9.411764740943908691e-01 4.000000059604644775e-01 1.000000000000000000e+00 -8.862745165824890137e-01 9.431372284889221191e-01 4.000000059604644775e-01 1.000000000000000000e+00 -8.901960849761962891e-01 9.450980424880981445e-01 4.000000059604644775e-01 1.000000000000000000e+00 -8.941176533699035645e-01 9.470587968826293945e-01 4.000000059604644775e-01 1.000000000000000000e+00 -8.980392217636108398e-01 9.490196108818054199e-01 4.000000059604644775e-01 1.000000000000000000e+00 -9.019607901573181152e-01 9.509803652763366699e-01 4.000000059604644775e-01 1.000000000000000000e+00 -9.058823585510253906e-01 9.529411792755126953e-01 4.000000059604644775e-01 1.000000000000000000e+00 -9.098039269447326660e-01 9.549019336700439453e-01 4.000000059604644775e-01 1.000000000000000000e+00 -9.137254953384399414e-01 9.568627476692199707e-01 4.000000059604644775e-01 1.000000000000000000e+00 -9.176470637321472168e-01 9.588235020637512207e-01 4.000000059604644775e-01 1.000000000000000000e+00 -9.215686321258544922e-01 9.607843160629272461e-01 4.000000059604644775e-01 1.000000000000000000e+00 -9.254902005195617676e-01 9.627450704574584961e-01 4.000000059604644775e-01 1.000000000000000000e+00 -9.294117689132690430e-01 9.647058844566345215e-01 4.000000059604644775e-01 1.000000000000000000e+00 -9.333333373069763184e-01 9.666666388511657715e-01 4.000000059604644775e-01 1.000000000000000000e+00 -9.372549057006835938e-01 9.686274528503417969e-01 4.000000059604644775e-01 1.000000000000000000e+00 -9.411764740943908691e-01 9.705882072448730469e-01 4.000000059604644775e-01 1.000000000000000000e+00 -9.450980424880981445e-01 9.725490212440490723e-01 4.000000059604644775e-01 1.000000000000000000e+00 -9.490196108818054199e-01 9.745097756385803223e-01 4.000000059604644775e-01 1.000000000000000000e+00 -9.529411792755126953e-01 9.764705896377563477e-01 4.000000059604644775e-01 1.000000000000000000e+00 -9.568627476692199707e-01 9.784313440322875977e-01 4.000000059604644775e-01 1.000000000000000000e+00 -9.607843160629272461e-01 9.803921580314636230e-01 4.000000059604644775e-01 1.000000000000000000e+00 -9.647058844566345215e-01 9.823529124259948730e-01 4.000000059604644775e-01 1.000000000000000000e+00 -9.686274528503417969e-01 9.843137264251708984e-01 4.000000059604644775e-01 1.000000000000000000e+00 -9.725490212440490723e-01 9.862744808197021484e-01 4.000000059604644775e-01 1.000000000000000000e+00 -9.764705896377563477e-01 9.882352948188781738e-01 4.000000059604644775e-01 1.000000000000000000e+00 -9.803921580314636230e-01 9.901960492134094238e-01 4.000000059604644775e-01 1.000000000000000000e+00 -9.843137264251708984e-01 9.921568632125854492e-01 4.000000059604644775e-01 1.000000000000000000e+00 -9.882352948188781738e-01 9.941176176071166992e-01 4.000000059604644775e-01 1.000000000000000000e+00 -9.921568632125854492e-01 9.960784316062927246e-01 4.000000059604644775e-01 1.000000000000000000e+00 -9.960784316062927246e-01 9.980391860008239746e-01 4.000000059604644775e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 4.000000059604644775e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/tab10 b/fastplotlib/utils/colormaps/tab10 deleted file mode 100644 index a3c2ccaa5..000000000 --- a/fastplotlib/utils/colormaps/tab10 +++ /dev/null @@ -1,10 +0,0 @@ -1.215686276555061340e-01 4.666666686534881592e-01 7.058823704719543457e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.980392158031463623e-01 5.490196123719215393e-02 1.000000000000000000e+00 -1.725490242242813110e-01 6.274510025978088379e-01 1.725490242242813110e-01 1.000000000000000000e+00 -8.392156958580017090e-01 1.529411822557449341e-01 1.568627506494522095e-01 1.000000000000000000e+00 -5.803921818733215332e-01 4.039215743541717529e-01 7.411764860153198242e-01 1.000000000000000000e+00 -5.490196347236633301e-01 3.372549116611480713e-01 2.941176593303680420e-01 1.000000000000000000e+00 -8.901960849761962891e-01 4.666666686534881592e-01 7.607843279838562012e-01 1.000000000000000000e+00 -4.980392158031463623e-01 4.980392158031463623e-01 4.980392158031463623e-01 1.000000000000000000e+00 -7.372549176216125488e-01 7.411764860153198242e-01 1.333333402872085571e-01 1.000000000000000000e+00 -9.019608050584793091e-02 7.450980544090270996e-01 8.117647171020507812e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/tab20 b/fastplotlib/utils/colormaps/tab20 deleted file mode 100644 index b7a955b9e..000000000 --- a/fastplotlib/utils/colormaps/tab20 +++ /dev/null @@ -1,20 +0,0 @@ -1.215686276555061340e-01 4.666666686534881592e-01 7.058823704719543457e-01 1.000000000000000000e+00 -6.823529601097106934e-01 7.803921699523925781e-01 9.098039269447326660e-01 1.000000000000000000e+00 -1.000000000000000000e+00 4.980392158031463623e-01 5.490196123719215393e-02 1.000000000000000000e+00 -1.000000000000000000e+00 7.333333492279052734e-01 4.705882370471954346e-01 1.000000000000000000e+00 -1.725490242242813110e-01 6.274510025978088379e-01 1.725490242242813110e-01 1.000000000000000000e+00 -5.960784554481506348e-01 8.745098114013671875e-01 5.411764979362487793e-01 1.000000000000000000e+00 -8.392156958580017090e-01 1.529411822557449341e-01 1.568627506494522095e-01 1.000000000000000000e+00 -1.000000000000000000e+00 5.960784554481506348e-01 5.882353186607360840e-01 1.000000000000000000e+00 -5.803921818733215332e-01 4.039215743541717529e-01 7.411764860153198242e-01 1.000000000000000000e+00 -7.725490331649780273e-01 6.901960968971252441e-01 8.352941274642944336e-01 1.000000000000000000e+00 -5.490196347236633301e-01 3.372549116611480713e-01 2.941176593303680420e-01 1.000000000000000000e+00 -7.686274647712707520e-01 6.117647290229797363e-01 5.803921818733215332e-01 1.000000000000000000e+00 -8.901960849761962891e-01 4.666666686534881592e-01 7.607843279838562012e-01 1.000000000000000000e+00 -9.686274528503417969e-01 7.137255072593688965e-01 8.235294222831726074e-01 1.000000000000000000e+00 -4.980392158031463623e-01 4.980392158031463623e-01 4.980392158031463623e-01 1.000000000000000000e+00 -7.803921699523925781e-01 7.803921699523925781e-01 7.803921699523925781e-01 1.000000000000000000e+00 -7.372549176216125488e-01 7.411764860153198242e-01 1.333333402872085571e-01 1.000000000000000000e+00 -8.588235378265380859e-01 8.588235378265380859e-01 5.529412031173706055e-01 1.000000000000000000e+00 -9.019608050584793091e-02 7.450980544090270996e-01 8.117647171020507812e-01 1.000000000000000000e+00 -6.196078658103942871e-01 8.549019694328308105e-01 8.980392217636108398e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/tab20b b/fastplotlib/utils/colormaps/tab20b deleted file mode 100644 index f5b176e31..000000000 --- a/fastplotlib/utils/colormaps/tab20b +++ /dev/null @@ -1,20 +0,0 @@ -2.235294133424758911e-01 2.313725501298904419e-01 4.745098054409027100e-01 1.000000000000000000e+00 -3.215686380863189697e-01 3.294117748737335205e-01 6.392157077789306641e-01 1.000000000000000000e+00 -4.196078479290008545e-01 4.313725531101226807e-01 8.117647171020507812e-01 1.000000000000000000e+00 -6.117647290229797363e-01 6.196078658103942871e-01 8.705882430076599121e-01 1.000000000000000000e+00 -3.882353007793426514e-01 4.745098054409027100e-01 2.235294133424758911e-01 1.000000000000000000e+00 -5.490196347236633301e-01 6.352941393852233887e-01 3.215686380863189697e-01 1.000000000000000000e+00 -7.098039388656616211e-01 8.117647171020507812e-01 4.196078479290008545e-01 1.000000000000000000e+00 -8.078431487083435059e-01 8.588235378265380859e-01 6.117647290229797363e-01 1.000000000000000000e+00 -5.490196347236633301e-01 4.274509847164154053e-01 1.921568661928176880e-01 1.000000000000000000e+00 -7.411764860153198242e-01 6.196078658103942871e-01 2.235294133424758911e-01 1.000000000000000000e+00 -9.058823585510253906e-01 7.294117808341979980e-01 3.215686380863189697e-01 1.000000000000000000e+00 -9.058823585510253906e-01 7.960784435272216797e-01 5.803921818733215332e-01 1.000000000000000000e+00 -5.176470875740051270e-01 2.352941185235977173e-01 2.235294133424758911e-01 1.000000000000000000e+00 -6.784313917160034180e-01 2.862745225429534912e-01 2.901960909366607666e-01 1.000000000000000000e+00 -8.392156958580017090e-01 3.803921639919281006e-01 4.196078479290008545e-01 1.000000000000000000e+00 -9.058823585510253906e-01 5.882353186607360840e-01 6.117647290229797363e-01 1.000000000000000000e+00 -4.823529422283172607e-01 2.549019753932952881e-01 4.509803950786590576e-01 1.000000000000000000e+00 -6.470588445663452148e-01 3.176470696926116943e-01 5.803921818733215332e-01 1.000000000000000000e+00 -8.078431487083435059e-01 4.274509847164154053e-01 7.411764860153198242e-01 1.000000000000000000e+00 -8.705882430076599121e-01 6.196078658103942871e-01 8.392156958580017090e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/tab20c b/fastplotlib/utils/colormaps/tab20c deleted file mode 100644 index 7521c3e2a..000000000 --- a/fastplotlib/utils/colormaps/tab20c +++ /dev/null @@ -1,20 +0,0 @@ -1.921568661928176880e-01 5.098039507865905762e-01 7.411764860153198242e-01 1.000000000000000000e+00 -4.196078479290008545e-01 6.823529601097106934e-01 8.392156958580017090e-01 1.000000000000000000e+00 -6.196078658103942871e-01 7.921568751335144043e-01 8.823529481887817383e-01 1.000000000000000000e+00 -7.764706015586853027e-01 8.588235378265380859e-01 9.372549057006835938e-01 1.000000000000000000e+00 -9.019607901573181152e-01 3.333333432674407959e-01 5.098039284348487854e-02 1.000000000000000000e+00 -9.921568632125854492e-01 5.529412031173706055e-01 2.352941185235977173e-01 1.000000000000000000e+00 -9.921568632125854492e-01 6.823529601097106934e-01 4.196078479290008545e-01 1.000000000000000000e+00 -9.921568632125854492e-01 8.156862854957580566e-01 6.352941393852233887e-01 1.000000000000000000e+00 -1.921568661928176880e-01 6.392157077789306641e-01 3.294117748737335205e-01 1.000000000000000000e+00 -4.549019634723663330e-01 7.686274647712707520e-01 4.627451002597808838e-01 1.000000000000000000e+00 -6.313725709915161133e-01 8.509804010391235352e-01 6.078431606292724609e-01 1.000000000000000000e+00 -7.803921699523925781e-01 9.137254953384399414e-01 7.529411911964416504e-01 1.000000000000000000e+00 -4.588235318660736084e-01 4.196078479290008545e-01 6.941176652908325195e-01 1.000000000000000000e+00 -6.196078658103942871e-01 6.039215922355651855e-01 7.843137383460998535e-01 1.000000000000000000e+00 -7.372549176216125488e-01 7.411764860153198242e-01 8.627451062202453613e-01 1.000000000000000000e+00 -8.549019694328308105e-01 8.549019694328308105e-01 9.215686321258544922e-01 1.000000000000000000e+00 -3.882353007793426514e-01 3.882353007793426514e-01 3.882353007793426514e-01 1.000000000000000000e+00 -5.882353186607360840e-01 5.882353186607360840e-01 5.882353186607360840e-01 1.000000000000000000e+00 -7.411764860153198242e-01 7.411764860153198242e-01 7.411764860153198242e-01 1.000000000000000000e+00 -8.509804010391235352e-01 8.509804010391235352e-01 8.509804010391235352e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/terrain b/fastplotlib/utils/colormaps/terrain deleted file mode 100644 index fd79cbfa7..000000000 --- a/fastplotlib/utils/colormaps/terrain +++ /dev/null @@ -1,256 +0,0 @@ -2.000000029802322388e-01 2.000000029802322388e-01 6.000000238418579102e-01 1.000000000000000000e+00 -1.947712451219558716e-01 2.104575186967849731e-01 6.104575395584106445e-01 1.000000000000000000e+00 -1.895424872636795044e-01 2.209150344133377075e-01 6.209150552749633789e-01 1.000000000000000000e+00 -1.843137294054031372e-01 2.313725501298904419e-01 6.313725709915161133e-01 1.000000000000000000e+00 -1.790849715471267700e-01 2.418300658464431763e-01 6.418300867080688477e-01 1.000000000000000000e+00 -1.738562136888504028e-01 2.522875964641571045e-01 6.522876024246215820e-01 1.000000000000000000e+00 -1.686274558305740356e-01 2.627451121807098389e-01 6.627451181411743164e-01 1.000000000000000000e+00 -1.633986979722976685e-01 2.732026278972625732e-01 6.732026338577270508e-01 1.000000000000000000e+00 -1.581699401140213013e-01 2.836601436138153076e-01 6.836601495742797852e-01 1.000000000000000000e+00 -1.529411822557449341e-01 2.941176593303680420e-01 6.941176652908325195e-01 1.000000000000000000e+00 -1.477124243974685669e-01 3.045751750469207764e-01 7.045751810073852539e-01 1.000000000000000000e+00 -1.424836665391921997e-01 3.150326907634735107e-01 7.150326967239379883e-01 1.000000000000000000e+00 -1.372549086809158325e-01 3.254902064800262451e-01 7.254902124404907227e-01 1.000000000000000000e+00 -1.320261508226394653e-01 3.359477221965789795e-01 7.359477281570434570e-01 1.000000000000000000e+00 -1.267973929643630981e-01 3.464052379131317139e-01 7.464052438735961914e-01 1.000000000000000000e+00 -1.215686276555061340e-01 3.568627536296844482e-01 7.568627595901489258e-01 1.000000000000000000e+00 -1.163398697972297668e-01 3.673202693462371826e-01 7.673202753067016602e-01 1.000000000000000000e+00 -1.111111119389533997e-01 3.777777850627899170e-01 7.777777910232543945e-01 1.000000000000000000e+00 -1.058823540806770325e-01 3.882353007793426514e-01 7.882353067398071289e-01 1.000000000000000000e+00 -1.006535962224006653e-01 3.986928164958953857e-01 7.986928224563598633e-01 1.000000000000000000e+00 -9.542483836412429810e-02 4.091503322124481201e-01 8.091503381729125977e-01 1.000000000000000000e+00 -9.019608050584793091e-02 4.196078479290008545e-01 8.196078538894653320e-01 1.000000000000000000e+00 -8.496732264757156372e-02 4.300653636455535889e-01 8.300653696060180664e-01 1.000000000000000000e+00 -7.973856478929519653e-02 4.405228793621063232e-01 8.405228853225708008e-01 1.000000000000000000e+00 -7.450980693101882935e-02 4.509803950786590576e-01 8.509804010391235352e-01 1.000000000000000000e+00 -6.928104907274246216e-02 4.614379107952117920e-01 8.614379167556762695e-01 1.000000000000000000e+00 -6.405229121446609497e-02 4.718954265117645264e-01 8.718954324722290039e-01 1.000000000000000000e+00 -5.882352963089942932e-02 4.823529422283172607e-01 8.823529481887817383e-01 1.000000000000000000e+00 -5.359477177262306213e-02 4.928104579448699951e-01 8.928104639053344727e-01 1.000000000000000000e+00 -4.836601391434669495e-02 5.032680034637451172e-01 9.032679796218872070e-01 1.000000000000000000e+00 -4.313725605607032776e-02 5.137255191802978516e-01 9.137254953384399414e-01 1.000000000000000000e+00 -3.790849819779396057e-02 5.241830348968505859e-01 9.241830110549926758e-01 1.000000000000000000e+00 -3.267974033951759338e-02 5.346405506134033203e-01 9.346405267715454102e-01 1.000000000000000000e+00 -2.745098061859607697e-02 5.450980663299560547e-01 9.450980424880981445e-01 1.000000000000000000e+00 -2.222222276031970978e-02 5.555555820465087891e-01 9.555555582046508789e-01 1.000000000000000000e+00 -1.699346490204334259e-02 5.660130977630615234e-01 9.660130739212036133e-01 1.000000000000000000e+00 -1.176470611244440079e-02 5.764706134796142578e-01 9.764705896377563477e-01 1.000000000000000000e+00 -6.535947788506746292e-03 5.869281291961669922e-01 9.869281053543090820e-01 1.000000000000000000e+00 -1.307189580984413624e-03 5.973856449127197266e-01 9.973856210708618164e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.058823466300964355e-01 9.823529124259948730e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.137254834175109863e-01 9.588235020637512207e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.215686202049255371e-01 9.352940917015075684e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.294117569923400879e-01 9.117646813392639160e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.372548937797546387e-01 8.882352709770202637e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.450980305671691895e-01 8.647058606147766113e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.529411673545837402e-01 8.411764502525329590e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.607843041419982910e-01 8.176470398902893066e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.686274409294128418e-01 7.941176295280456543e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.764705777168273926e-01 7.705882191658020020e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.843137145042419434e-01 7.470588088035583496e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.921568512916564941e-01 7.235293984413146973e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.999999880790710449e-01 6.999999880790710449e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.078431248664855957e-01 6.764705777168273926e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.156862616539001465e-01 6.529411673545837402e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.235293984413146973e-01 6.294117569923400879e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.313725352287292480e-01 6.058823466300964355e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.392156720161437988e-01 5.823529362678527832e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.470588088035583496e-01 5.588235259056091309e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.549019455909729004e-01 5.352941155433654785e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.627450823783874512e-01 5.117647051811218262e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.705882191658020020e-01 4.882352948188781738e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.784313559532165527e-01 4.647058844566345215e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.862744927406311035e-01 4.411764740943908691e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.941176295280456543e-01 4.176470637321472168e-01 1.000000000000000000e+00 -3.921568859368562698e-03 8.007842898368835449e-01 4.007843136787414551e-01 1.000000000000000000e+00 -1.960784383118152618e-02 8.039215803146362305e-01 4.039215743541717529e-01 1.000000000000000000e+00 -3.529411926865577698e-02 8.070588111877441406e-01 4.070588350296020508e-01 1.000000000000000000e+00 -5.098039284348487854e-02 8.101961016654968262e-01 4.101960659027099609e-01 1.000000000000000000e+00 -6.666667014360427856e-02 8.133333325386047363e-01 4.133333265781402588e-01 1.000000000000000000e+00 -8.235294371843338013e-02 8.164705634117126465e-01 4.164705872535705566e-01 1.000000000000000000e+00 -9.803921729326248169e-02 8.196078538894653320e-01 4.196078479290008545e-01 1.000000000000000000e+00 -1.137254908680915833e-01 8.227450847625732422e-01 4.227451086044311523e-01 1.000000000000000000e+00 -1.294117718935012817e-01 8.258823752403259277e-01 4.258823394775390625e-01 1.000000000000000000e+00 -1.450980454683303833e-01 8.290196061134338379e-01 4.290196001529693604e-01 1.000000000000000000e+00 -1.607843190431594849e-01 8.321568369865417480e-01 4.321568608283996582e-01 1.000000000000000000e+00 -1.764705926179885864e-01 8.352941274642944336e-01 4.352941215038299561e-01 1.000000000000000000e+00 -1.921568661928176880e-01 8.384313583374023438e-01 4.384313821792602539e-01 1.000000000000000000e+00 -2.078431397676467896e-01 8.415686488151550293e-01 4.415686130523681641e-01 1.000000000000000000e+00 -2.235294133424758911e-01 8.447058796882629395e-01 4.447058737277984619e-01 1.000000000000000000e+00 -2.392156869173049927e-01 8.478431105613708496e-01 4.478431344032287598e-01 1.000000000000000000e+00 -2.549019753932952881e-01 8.509804010391235352e-01 4.509803950786590576e-01 1.000000000000000000e+00 -2.705882489681243896e-01 8.541176319122314453e-01 4.541176557540893555e-01 1.000000000000000000e+00 -2.862745225429534912e-01 8.572549223899841309e-01 4.572549164295196533e-01 1.000000000000000000e+00 -3.019607961177825928e-01 8.603921532630920410e-01 4.603921473026275635e-01 1.000000000000000000e+00 -3.176470696926116943e-01 8.635293841361999512e-01 4.635294079780578613e-01 1.000000000000000000e+00 -3.333333432674407959e-01 8.666666746139526367e-01 4.666666686534881592e-01 1.000000000000000000e+00 -3.490196168422698975e-01 8.698039054870605469e-01 4.698039293289184570e-01 1.000000000000000000e+00 -3.647058904170989990e-01 8.729411959648132324e-01 4.729411900043487549e-01 1.000000000000000000e+00 -3.803921639919281006e-01 8.760784268379211426e-01 4.760784208774566650e-01 1.000000000000000000e+00 -3.960784375667572021e-01 8.792156577110290527e-01 4.792156815528869629e-01 1.000000000000000000e+00 -4.117647111415863037e-01 8.823529481887817383e-01 4.823529422283172607e-01 1.000000000000000000e+00 -4.274509847164154053e-01 8.854901790618896484e-01 4.854902029037475586e-01 1.000000000000000000e+00 -4.431372582912445068e-01 8.886274695396423340e-01 4.886274635791778564e-01 1.000000000000000000e+00 -4.588235318660736084e-01 8.917647004127502441e-01 4.917646944522857666e-01 1.000000000000000000e+00 -4.745098054409027100e-01 8.949019312858581543e-01 4.949019551277160645e-01 1.000000000000000000e+00 -4.901960790157318115e-01 8.980392217636108398e-01 4.980392158031463623e-01 1.000000000000000000e+00 -5.058823823928833008e-01 9.011764526367187500e-01 5.011764764785766602e-01 1.000000000000000000e+00 -5.215686559677124023e-01 9.043137431144714355e-01 5.043137073516845703e-01 1.000000000000000000e+00 -5.372549295425415039e-01 9.074509739875793457e-01 5.074509978294372559e-01 1.000000000000000000e+00 -5.529412031173706055e-01 9.105882644653320312e-01 5.105882287025451660e-01 1.000000000000000000e+00 -5.686274766921997070e-01 9.137254953384399414e-01 5.137255191802978516e-01 1.000000000000000000e+00 -5.843137502670288086e-01 9.168627262115478516e-01 5.168627500534057617e-01 1.000000000000000000e+00 -6.000000238418579102e-01 9.200000166893005371e-01 5.199999809265136719e-01 1.000000000000000000e+00 -6.156862974166870117e-01 9.231372475624084473e-01 5.231372714042663574e-01 1.000000000000000000e+00 -6.313725709915161133e-01 9.262745380401611328e-01 5.262745022773742676e-01 1.000000000000000000e+00 -6.470588445663452148e-01 9.294117689132690430e-01 5.294117927551269531e-01 1.000000000000000000e+00 -6.627451181411743164e-01 9.325489997863769531e-01 5.325490236282348633e-01 1.000000000000000000e+00 -6.784313917160034180e-01 9.356862902641296387e-01 5.356862545013427734e-01 1.000000000000000000e+00 -6.941176652908325195e-01 9.388235211372375488e-01 5.388235449790954590e-01 1.000000000000000000e+00 -7.098039388656616211e-01 9.419608116149902344e-01 5.419607758522033691e-01 1.000000000000000000e+00 -7.254902124404907227e-01 9.450980424880981445e-01 5.450980663299560547e-01 1.000000000000000000e+00 -7.411764860153198242e-01 9.482352733612060547e-01 5.482352972030639648e-01 1.000000000000000000e+00 -7.568627595901489258e-01 9.513725638389587402e-01 5.513725280761718750e-01 1.000000000000000000e+00 -7.725490331649780273e-01 9.545097947120666504e-01 5.545098185539245605e-01 1.000000000000000000e+00 -7.882353067398071289e-01 9.576470851898193359e-01 5.576470494270324707e-01 1.000000000000000000e+00 -8.039215803146362305e-01 9.607843160629272461e-01 5.607843399047851562e-01 1.000000000000000000e+00 -8.196078538894653320e-01 9.639215469360351562e-01 5.639215707778930664e-01 1.000000000000000000e+00 -8.352941274642944336e-01 9.670588374137878418e-01 5.670588016510009766e-01 1.000000000000000000e+00 -8.509804010391235352e-01 9.701960682868957520e-01 5.701960921287536621e-01 1.000000000000000000e+00 -8.666666746139526367e-01 9.733333587646484375e-01 5.733333230018615723e-01 1.000000000000000000e+00 -8.823529481887817383e-01 9.764705896377563477e-01 5.764706134796142578e-01 1.000000000000000000e+00 -8.980392217636108398e-01 9.796078205108642578e-01 5.796078443527221680e-01 1.000000000000000000e+00 -9.137254953384399414e-01 9.827451109886169434e-01 5.827450752258300781e-01 1.000000000000000000e+00 -9.294117689132690430e-01 9.858823418617248535e-01 5.858823657035827637e-01 1.000000000000000000e+00 -9.450980424880981445e-01 9.890196323394775391e-01 5.890195965766906738e-01 1.000000000000000000e+00 -9.607843160629272461e-01 9.921568632125854492e-01 5.921568870544433594e-01 1.000000000000000000e+00 -9.764705896377563477e-01 9.952940940856933594e-01 5.952941179275512695e-01 1.000000000000000000e+00 -9.921568632125854492e-01 9.984313845634460449e-01 5.984313488006591797e-01 1.000000000000000000e+00 -9.960784316062927246e-01 9.949803948402404785e-01 5.978823304176330566e-01 1.000000000000000000e+00 -9.882352948188781738e-01 9.849411845207214355e-01 5.936470627784729004e-01 1.000000000000000000e+00 -9.803921580314636230e-01 9.749019742012023926e-01 5.894117355346679688e-01 1.000000000000000000e+00 -9.725490212440490723e-01 9.648627638816833496e-01 5.851764678955078125e-01 1.000000000000000000e+00 -9.647058844566345215e-01 9.548235535621643066e-01 5.809412002563476562e-01 1.000000000000000000e+00 -9.568627476692199707e-01 9.447843432426452637e-01 5.767058730125427246e-01 1.000000000000000000e+00 -9.490196108818054199e-01 9.347450733184814453e-01 5.724706053733825684e-01 1.000000000000000000e+00 -9.411764740943908691e-01 9.247058629989624023e-01 5.682352781295776367e-01 1.000000000000000000e+00 -9.333333373069763184e-01 9.146666526794433594e-01 5.640000104904174805e-01 1.000000000000000000e+00 -9.254902005195617676e-01 9.046274423599243164e-01 5.597646832466125488e-01 1.000000000000000000e+00 -9.176470637321472168e-01 8.945882320404052734e-01 5.555294156074523926e-01 1.000000000000000000e+00 -9.098039269447326660e-01 8.845490217208862305e-01 5.512940883636474609e-01 1.000000000000000000e+00 -9.019607901573181152e-01 8.745098114013671875e-01 5.470588207244873047e-01 1.000000000000000000e+00 -8.941176533699035645e-01 8.644706010818481445e-01 5.428235530853271484e-01 1.000000000000000000e+00 -8.862745165824890137e-01 8.544313907623291016e-01 5.385882258415222168e-01 1.000000000000000000e+00 -8.784313797950744629e-01 8.443921804428100586e-01 5.343529582023620605e-01 1.000000000000000000e+00 -8.705882430076599121e-01 8.343529701232910156e-01 5.301176309585571289e-01 1.000000000000000000e+00 -8.627451062202453613e-01 8.243137001991271973e-01 5.258823633193969727e-01 1.000000000000000000e+00 -8.549019694328308105e-01 8.142744898796081543e-01 5.216470360755920410e-01 1.000000000000000000e+00 -8.470588326454162598e-01 8.042352795600891113e-01 5.174117684364318848e-01 1.000000000000000000e+00 -8.392156958580017090e-01 7.941960692405700684e-01 5.131764411926269531e-01 1.000000000000000000e+00 -8.313725590705871582e-01 7.841568589210510254e-01 5.089411735534667969e-01 1.000000000000000000e+00 -8.235294222831726074e-01 7.741176486015319824e-01 5.047059059143066406e-01 1.000000000000000000e+00 -8.156862854957580566e-01 7.640784382820129395e-01 5.004705786705017090e-01 1.000000000000000000e+00 -8.078431487083435059e-01 7.540392279624938965e-01 4.962352812290191650e-01 1.000000000000000000e+00 -8.000000119209289551e-01 7.440000176429748535e-01 4.920000135898590088e-01 1.000000000000000000e+00 -7.921568751335144043e-01 7.339608073234558105e-01 4.877647161483764648e-01 1.000000000000000000e+00 -7.843137383460998535e-01 7.239215970039367676e-01 4.835294187068939209e-01 1.000000000000000000e+00 -7.764706015586853027e-01 7.138823270797729492e-01 4.792941212654113770e-01 1.000000000000000000e+00 -7.686274647712707520e-01 7.038431167602539062e-01 4.750588238239288330e-01 1.000000000000000000e+00 -7.607843279838562012e-01 6.938039064407348633e-01 4.708235263824462891e-01 1.000000000000000000e+00 -7.529411911964416504e-01 6.837646961212158203e-01 4.665882289409637451e-01 1.000000000000000000e+00 -7.450980544090270996e-01 6.737254858016967773e-01 4.623529314994812012e-01 1.000000000000000000e+00 -7.372549176216125488e-01 6.636862754821777344e-01 4.581176340579986572e-01 1.000000000000000000e+00 -7.294117808341979980e-01 6.536470651626586914e-01 4.538823664188385010e-01 1.000000000000000000e+00 -7.215686440467834473e-01 6.436078548431396484e-01 4.496470689773559570e-01 1.000000000000000000e+00 -7.137255072593688965e-01 6.335686445236206055e-01 4.454117715358734131e-01 1.000000000000000000e+00 -7.058823704719543457e-01 6.235294342041015625e-01 4.411764740943908691e-01 1.000000000000000000e+00 -6.980392336845397949e-01 6.134902238845825195e-01 4.369411766529083252e-01 1.000000000000000000e+00 -6.901960968971252441e-01 6.034509539604187012e-01 4.327058792114257812e-01 1.000000000000000000e+00 -6.823529601097106934e-01 5.934117436408996582e-01 4.284705817699432373e-01 1.000000000000000000e+00 -6.745098233222961426e-01 5.833725333213806152e-01 4.242352843284606934e-01 1.000000000000000000e+00 -6.666666865348815918e-01 5.733333230018615723e-01 4.199999868869781494e-01 1.000000000000000000e+00 -6.588235497474670410e-01 5.632941126823425293e-01 4.157647192478179932e-01 1.000000000000000000e+00 -6.509804129600524902e-01 5.532549023628234863e-01 4.115294218063354492e-01 1.000000000000000000e+00 -6.431372761726379395e-01 5.432156920433044434e-01 4.072941243648529053e-01 1.000000000000000000e+00 -6.352941393852233887e-01 5.331764817237854004e-01 4.030588269233703613e-01 1.000000000000000000e+00 -6.274510025978088379e-01 5.231372714042663574e-01 3.988235294818878174e-01 1.000000000000000000e+00 -6.196078658103942871e-01 5.130980610847473145e-01 3.945882320404052734e-01 1.000000000000000000e+00 -6.117647290229797363e-01 5.030588507652282715e-01 3.903529345989227295e-01 1.000000000000000000e+00 -6.039215922355651855e-01 4.930196106433868408e-01 3.861176371574401855e-01 1.000000000000000000e+00 -5.960784554481506348e-01 4.829804003238677979e-01 3.818823397159576416e-01 1.000000000000000000e+00 -5.882353186607360840e-01 4.729411900043487549e-01 3.776470720767974854e-01 1.000000000000000000e+00 -5.803921818733215332e-01 4.629019498825073242e-01 3.734117746353149414e-01 1.000000000000000000e+00 -5.725490450859069824e-01 4.528627395629882812e-01 3.691764771938323975e-01 1.000000000000000000e+00 -5.647059082984924316e-01 4.428235292434692383e-01 3.649411797523498535e-01 1.000000000000000000e+00 -5.568627715110778809e-01 4.327843189239501953e-01 3.607058823108673096e-01 1.000000000000000000e+00 -5.490196347236633301e-01 4.227451086044311523e-01 3.564705848693847656e-01 1.000000000000000000e+00 -5.411764979362487793e-01 4.127058684825897217e-01 3.522352874279022217e-01 1.000000000000000000e+00 -5.333333611488342285e-01 4.026666581630706787e-01 3.479999899864196777e-01 1.000000000000000000e+00 -5.254902243614196777e-01 3.926274478435516357e-01 3.437646925449371338e-01 1.000000000000000000e+00 -5.176470875740051270e-01 3.825882375240325928e-01 3.395294249057769775e-01 1.000000000000000000e+00 -5.098039507865905762e-01 3.725490272045135498e-01 3.352941274642944336e-01 1.000000000000000000e+00 -5.019608139991760254e-01 3.625098168849945068e-01 3.310588300228118896e-01 1.000000000000000000e+00 -5.058823823928833008e-01 3.675294220447540283e-01 3.378823399543762207e-01 1.000000000000000000e+00 -5.137255191802978516e-01 3.775686323642730713e-01 3.483921587467193604e-01 1.000000000000000000e+00 -5.215686559677124023e-01 3.876078426837921143e-01 3.589019477367401123e-01 1.000000000000000000e+00 -5.294117927551269531e-01 3.976470530033111572e-01 3.694117665290832520e-01 1.000000000000000000e+00 -5.372549295425415039e-01 4.076862633228302002e-01 3.799215555191040039e-01 1.000000000000000000e+00 -5.450980663299560547e-01 4.177255034446716309e-01 3.904313743114471436e-01 1.000000000000000000e+00 -5.529412031173706055e-01 4.277647137641906738e-01 4.009411633014678955e-01 1.000000000000000000e+00 -5.607843399047851562e-01 4.378039240837097168e-01 4.114509820938110352e-01 1.000000000000000000e+00 -5.686274766921997070e-01 4.478431344032287598e-01 4.219607710838317871e-01 1.000000000000000000e+00 -5.764706134796142578e-01 4.578823447227478027e-01 4.324705898761749268e-01 1.000000000000000000e+00 -5.843137502670288086e-01 4.679215550422668457e-01 4.429803788661956787e-01 1.000000000000000000e+00 -5.921568870544433594e-01 4.779607951641082764e-01 4.534901976585388184e-01 1.000000000000000000e+00 -6.000000238418579102e-01 4.880000054836273193e-01 4.639999866485595703e-01 1.000000000000000000e+00 -6.078431606292724609e-01 4.980392158031463623e-01 4.745098054409027100e-01 1.000000000000000000e+00 -6.156862974166870117e-01 5.080784559249877930e-01 4.850195944309234619e-01 1.000000000000000000e+00 -6.235294342041015625e-01 5.181176662445068359e-01 4.955294132232666016e-01 1.000000000000000000e+00 -6.313725709915161133e-01 5.281568765640258789e-01 5.060392022132873535e-01 1.000000000000000000e+00 -6.392157077789306641e-01 5.381960868835449219e-01 5.165489912033081055e-01 1.000000000000000000e+00 -6.470588445663452148e-01 5.482352972030639648e-01 5.270588397979736328e-01 1.000000000000000000e+00 -6.549019813537597656e-01 5.582745075225830078e-01 5.375686287879943848e-01 1.000000000000000000e+00 -6.627451181411743164e-01 5.683137178421020508e-01 5.480784177780151367e-01 1.000000000000000000e+00 -6.705882549285888672e-01 5.783529281616210938e-01 5.585882067680358887e-01 1.000000000000000000e+00 -6.784313917160034180e-01 5.883921384811401367e-01 5.690980553627014160e-01 1.000000000000000000e+00 -6.862745285034179688e-01 5.984313488006591797e-01 5.796078443527221680e-01 1.000000000000000000e+00 -6.941176652908325195e-01 6.084705591201782227e-01 5.901176333427429199e-01 1.000000000000000000e+00 -7.019608020782470703e-01 6.185098290443420410e-01 6.006274223327636719e-01 1.000000000000000000e+00 -7.098039388656616211e-01 6.285490393638610840e-01 6.111372709274291992e-01 1.000000000000000000e+00 -7.176470756530761719e-01 6.385882496833801270e-01 6.216470599174499512e-01 1.000000000000000000e+00 -7.254902124404907227e-01 6.486274600028991699e-01 6.321568489074707031e-01 1.000000000000000000e+00 -7.333333492279052734e-01 6.586666703224182129e-01 6.426666378974914551e-01 1.000000000000000000e+00 -7.411764860153198242e-01 6.687058806419372559e-01 6.531764864921569824e-01 1.000000000000000000e+00 -7.490196228027343750e-01 6.787450909614562988e-01 6.636862754821777344e-01 1.000000000000000000e+00 -7.568627595901489258e-01 6.887843012809753418e-01 6.741960644721984863e-01 1.000000000000000000e+00 -7.647058963775634766e-01 6.988235116004943848e-01 6.847058534622192383e-01 1.000000000000000000e+00 -7.725490331649780273e-01 7.088627219200134277e-01 6.952157020568847656e-01 1.000000000000000000e+00 -7.803921699523925781e-01 7.189019322395324707e-01 7.057254910469055176e-01 1.000000000000000000e+00 -7.882353067398071289e-01 7.289412021636962891e-01 7.162352800369262695e-01 1.000000000000000000e+00 -7.960784435272216797e-01 7.389804124832153320e-01 7.267450690269470215e-01 1.000000000000000000e+00 -8.039215803146362305e-01 7.490196228027343750e-01 7.372549176216125488e-01 1.000000000000000000e+00 -8.117647171020507812e-01 7.590588331222534180e-01 7.477647066116333008e-01 1.000000000000000000e+00 -8.196078538894653320e-01 7.690980434417724609e-01 7.582744956016540527e-01 1.000000000000000000e+00 -8.274509906768798828e-01 7.791372537612915039e-01 7.687842845916748047e-01 1.000000000000000000e+00 -8.352941274642944336e-01 7.891764640808105469e-01 7.792941331863403320e-01 1.000000000000000000e+00 -8.431372642517089844e-01 7.992156744003295898e-01 7.898039221763610840e-01 1.000000000000000000e+00 -8.509804010391235352e-01 8.092548847198486328e-01 8.003137111663818359e-01 1.000000000000000000e+00 -8.588235378265380859e-01 8.192940950393676758e-01 8.108235001564025879e-01 1.000000000000000000e+00 -8.666666746139526367e-01 8.293333053588867188e-01 8.213333487510681152e-01 1.000000000000000000e+00 -8.745098114013671875e-01 8.393725752830505371e-01 8.318431377410888672e-01 1.000000000000000000e+00 -8.823529481887817383e-01 8.494117856025695801e-01 8.423529267311096191e-01 1.000000000000000000e+00 -8.901960849761962891e-01 8.594509959220886230e-01 8.528627157211303711e-01 1.000000000000000000e+00 -8.980392217636108398e-01 8.694902062416076660e-01 8.633725643157958984e-01 1.000000000000000000e+00 -9.058823585510253906e-01 8.795294165611267090e-01 8.738823533058166504e-01 1.000000000000000000e+00 -9.137254953384399414e-01 8.895686268806457520e-01 8.843921422958374023e-01 1.000000000000000000e+00 -9.215686321258544922e-01 8.996078372001647949e-01 8.949019312858581543e-01 1.000000000000000000e+00 -9.294117689132690430e-01 9.096470475196838379e-01 9.054117798805236816e-01 1.000000000000000000e+00 -9.372549057006835938e-01 9.196862578392028809e-01 9.159215688705444336e-01 1.000000000000000000e+00 -9.450980424880981445e-01 9.297254681587219238e-01 9.264313578605651855e-01 1.000000000000000000e+00 -9.529411792755126953e-01 9.397646784782409668e-01 9.369411468505859375e-01 1.000000000000000000e+00 -9.607843160629272461e-01 9.498039484024047852e-01 9.474509954452514648e-01 1.000000000000000000e+00 -9.686274528503417969e-01 9.598431587219238281e-01 9.579607844352722168e-01 1.000000000000000000e+00 -9.764705896377563477e-01 9.698823690414428711e-01 9.684705734252929688e-01 1.000000000000000000e+00 -9.843137264251708984e-01 9.799215793609619141e-01 9.789803624153137207e-01 1.000000000000000000e+00 -9.921568632125854492e-01 9.899607896804809570e-01 9.894902110099792480e-01 1.000000000000000000e+00 -1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/turbo b/fastplotlib/utils/colormaps/turbo deleted file mode 100644 index bf6090ac4..000000000 --- a/fastplotlib/utils/colormaps/turbo +++ /dev/null @@ -1,256 +0,0 @@ -1.899500042200088501e-01 7.175999879837036133e-02 2.321700006723403931e-01 1.000000000000000000e+00 -1.948300004005432129e-01 8.338999748229980469e-02 2.614899873733520508e-01 1.000000000000000000e+00 -1.995600014925003052e-01 9.498000144958496094e-02 2.902399897575378418e-01 1.000000000000000000e+00 -2.041500061750411987e-01 1.065199971199035645e-01 3.184399902820587158e-01 1.000000000000000000e+00 -2.085999995470046997e-01 1.180199980735778809e-01 3.460699915885925293e-01 1.000000000000000000e+00 -2.129099965095520020e-01 1.294700056314468384e-01 3.731400072574615479e-01 1.000000000000000000e+00 -2.170799970626831055e-01 1.408700048923492432e-01 3.996399939060211182e-01 1.000000000000000000e+00 -2.211100012063980103e-01 1.522299945354461670e-01 4.255799949169158936e-01 1.000000000000000000e+00 -2.249999940395355225e-01 1.635400056838989258e-01 4.509600102901458740e-01 1.000000000000000000e+00 -2.287500053644180298e-01 1.748100072145462036e-01 4.757800102233886719e-01 1.000000000000000000e+00 -2.323600053787231445e-01 1.860300004482269287e-01 5.000399947166442871e-01 1.000000000000000000e+00 -2.358199954032897949e-01 1.972000002861022949e-01 5.237299799919128418e-01 1.000000000000000000e+00 -2.391500025987625122e-01 2.083300054073333740e-01 5.468599796295166016e-01 1.000000000000000000e+00 -2.423399984836578369e-01 2.194100022315979004e-01 5.694199800491333008e-01 1.000000000000000000e+00 -2.453899979591369629e-01 2.304400056600570679e-01 5.914199948310852051e-01 1.000000000000000000e+00 -2.483000010251998901e-01 2.414299994707107544e-01 6.128600239753723145e-01 1.000000000000000000e+00 -2.510699927806854248e-01 2.523699998855590820e-01 6.337400078773498535e-01 1.000000000000000000e+00 -2.536900043487548828e-01 2.632699906826019287e-01 6.540600061416625977e-01 1.000000000000000000e+00 -2.561799883842468262e-01 2.741200029850006104e-01 6.738100051879882812e-01 1.000000000000000000e+00 -2.585299909114837646e-01 2.849200069904327393e-01 6.930000185966491699e-01 1.000000000000000000e+00 -2.607400119304656982e-01 2.956799864768981934e-01 7.116199731826782227e-01 1.000000000000000000e+00 -2.628000080585479736e-01 3.063899874687194824e-01 7.296800017356872559e-01 1.000000000000000000e+00 -2.647300064563751221e-01 3.170599937438964844e-01 7.471799850463867188e-01 1.000000000000000000e+00 -2.665199935436248779e-01 3.276799917221069336e-01 7.641199827194213867e-01 1.000000000000000000e+00 -2.681599855422973633e-01 3.382500112056732178e-01 7.804999947547912598e-01 1.000000000000000000e+00 -2.696700096130371094e-01 3.487800061702728271e-01 7.963100075721740723e-01 1.000000000000000000e+00 -2.710300087928771973e-01 3.592599928379058838e-01 8.115599751472473145e-01 1.000000000000000000e+00 -2.722600102424621582e-01 3.697000145912170410e-01 8.262400031089782715e-01 1.000000000000000000e+00 -2.733399868011474609e-01 3.800800144672393799e-01 8.403699994087219238e-01 1.000000000000000000e+00 -2.742899954319000244e-01 3.904300034046173096e-01 8.539299964904785156e-01 1.000000000000000000e+00 -2.750900089740753174e-01 4.007200002670288086e-01 8.669199943542480469e-01 1.000000000000000000e+00 -2.757599949836730957e-01 4.109700024127960205e-01 8.793600201606750488e-01 1.000000000000000000e+00 -2.762799859046936035e-01 4.211800098419189453e-01 8.912299871444702148e-01 1.000000000000000000e+00 -2.766700088977813721e-01 4.313400089740753174e-01 9.025400280952453613e-01 1.000000000000000000e+00 -2.769100069999694824e-01 4.414499998092651367e-01 9.132800102233886719e-01 1.000000000000000000e+00 -2.770099937915802002e-01 4.515199959278106689e-01 9.234700202941894531e-01 1.000000000000000000e+00 -2.769800126552581787e-01 4.615299999713897705e-01 9.330899715423583984e-01 1.000000000000000000e+00 -2.768000066280364990e-01 4.715099930763244629e-01 9.421399831771850586e-01 1.000000000000000000e+00 -2.764799892902374268e-01 4.814400076866149902e-01 9.506400227546691895e-01 1.000000000000000000e+00 -2.760300040245056152e-01 4.913200139999389648e-01 9.585700035095214844e-01 1.000000000000000000e+00 -2.754299938678741455e-01 5.011500120162963867e-01 9.659399986267089844e-01 1.000000000000000000e+00 -2.746900022029876709e-01 5.109400153160095215e-01 9.727500081062316895e-01 1.000000000000000000e+00 -2.738099992275238037e-01 5.206900238990783691e-01 9.789900183677673340e-01 1.000000000000000000e+00 -2.727299928665161133e-01 5.303999781608581543e-01 9.846100211143493652e-01 1.000000000000000000e+00 -2.710599899291992188e-01 5.401499867439270020e-01 9.893000125885009766e-01 1.000000000000000000e+00 -2.687799930572509766e-01 5.499500036239624023e-01 9.930300116539001465e-01 1.000000000000000000e+00 -2.659200131893157959e-01 5.597900152206420898e-01 9.958299994468688965e-01 1.000000000000000000e+00 -2.625199854373931885e-01 5.696700215339660645e-01 9.977300167083740234e-01 1.000000000000000000e+00 -2.586199939250946045e-01 5.795800089836120605e-01 9.987599849700927734e-01 1.000000000000000000e+00 -2.542499899864196777e-01 5.895000100135803223e-01 9.989600181579589844e-01 1.000000000000000000e+00 -2.494599968194961548e-01 5.994300246238708496e-01 9.983500242233276367e-01 1.000000000000000000e+00 -2.442699968814849854e-01 6.093699932098388672e-01 9.969699978828430176e-01 1.000000000000000000e+00 -2.387399971485137939e-01 6.193100214004516602e-01 9.948499798774719238e-01 1.000000000000000000e+00 -2.328799962997436523e-01 6.292300224304199219e-01 9.920200109481811523e-01 1.000000000000000000e+00 -2.267599999904632568e-01 6.391299962997436523e-01 9.885100126266479492e-01 1.000000000000000000e+00 -2.203900068998336792e-01 6.490100026130676270e-01 9.843599796295166016e-01 1.000000000000000000e+00 -2.138199955224990845e-01 6.588600277900695801e-01 9.795899987220764160e-01 1.000000000000000000e+00 -2.070800065994262695e-01 6.686599850654602051e-01 9.742299914360046387e-01 1.000000000000000000e+00 -2.002100050449371338e-01 6.784200072288513184e-01 9.683300256729125977e-01 1.000000000000000000e+00 -1.932599991559982300e-01 6.881200075149536133e-01 9.618999958038330078e-01 1.000000000000000000e+00 -1.862500011920928955e-01 6.977499723434448242e-01 9.549800157546997070e-01 1.000000000000000000e+00 -1.792300045490264893e-01 7.073199748992919922e-01 9.476100206375122070e-01 1.000000000000000000e+00 -1.722300052642822266e-01 7.167999744415283203e-01 9.398099780082702637e-01 1.000000000000000000e+00 -1.652899980545043945e-01 7.261999845504760742e-01 9.316099882125854492e-01 1.000000000000000000e+00 -1.584399938583374023e-01 7.355099916458129883e-01 9.230499863624572754e-01 1.000000000000000000e+00 -1.517300009727478027e-01 7.447199821472167969e-01 9.141600131988525391e-01 1.000000000000000000e+00 -1.451900005340576172e-01 7.538099884986877441e-01 9.049599766731262207e-01 1.000000000000000000e+00 -1.388600021600723267e-01 7.627900242805480957e-01 8.955000042915344238e-01 1.000000000000000000e+00 -1.327800005674362183e-01 7.716500163078308105e-01 8.858000040054321289e-01 1.000000000000000000e+00 -1.269800066947937012e-01 7.803699970245361328e-01 8.758999705314636230e-01 1.000000000000000000e+00 -1.215099990367889404e-01 7.889599800109863281e-01 8.658099770545959473e-01 1.000000000000000000e+00 -1.163899973034858704e-01 7.973999977111816406e-01 8.555899858474731445e-01 1.000000000000000000e+00 -1.116700023412704468e-01 8.056899905204772949e-01 8.452500104904174805e-01 1.000000000000000000e+00 -1.073800027370452881e-01 8.138099908828735352e-01 8.348399996757507324e-01 1.000000000000000000e+00 -1.035699993371963501e-01 8.217700123786926270e-01 8.243700265884399414e-01 1.000000000000000000e+00 -1.002599969506263733e-01 8.295500278472900391e-01 8.138899803161621094e-01 1.000000000000000000e+00 -9.749999642372131348e-02 8.371400237083435059e-01 8.034200072288513184e-01 1.000000000000000000e+00 -9.532000124454498291e-02 8.445500135421752930e-01 7.929900288581848145e-01 1.000000000000000000e+00 -9.376999735832214355e-02 8.517500162124633789e-01 7.826399803161621094e-01 1.000000000000000000e+00 -9.286999702453613281e-02 8.587499856948852539e-01 7.724000215530395508e-01 1.000000000000000000e+00 -9.267000108957290649e-02 8.655400276184082031e-01 7.623000144958496094e-01 1.000000000000000000e+00 -9.319999814033508301e-02 8.721100091934204102e-01 7.523699998855590820e-01 1.000000000000000000e+00 -9.450999647378921509e-02 8.784400224685668945e-01 7.426499724388122559e-01 1.000000000000000000e+00 -9.662000089883804321e-02 8.845400214195251465e-01 7.331600189208984375e-01 1.000000000000000000e+00 -9.957999736070632935e-02 8.903999924659729004e-01 7.239300012588500977e-01 1.000000000000000000e+00 -1.034199967980384827e-01 8.960000276565551758e-01 7.149999737739562988e-01 1.000000000000000000e+00 -1.081499978899955750e-01 9.014199972152709961e-01 7.059900164604187012e-01 1.000000000000000000e+00 -1.137399971485137939e-01 9.067299962043762207e-01 6.965100169181823730e-01 1.000000000000000000e+00 -1.201400011777877808e-01 9.119300246238708496e-01 6.866000294685363770e-01 1.000000000000000000e+00 -1.273300051689147949e-01 9.170100092887878418e-01 6.762700080871582031e-01 1.000000000000000000e+00 -1.352600008249282837e-01 9.219700098037719727e-01 6.655600070953369141e-01 1.000000000000000000e+00 -1.439100056886672974e-01 9.268000125885009766e-01 6.544799804687500000e-01 1.000000000000000000e+00 -1.532299965620040894e-01 9.315099716186523438e-01 6.430799961090087891e-01 1.000000000000000000e+00 -1.631900072097778320e-01 9.360899925231933594e-01 6.313700079917907715e-01 1.000000000000000000e+00 -1.737699955701828003e-01 9.405300021171569824e-01 6.193799972534179688e-01 1.000000000000000000e+00 -1.849099993705749512e-01 9.448400139808654785e-01 6.071299910545349121e-01 1.000000000000000000e+00 -1.965900063514709473e-01 9.490100145339965820e-01 5.946599841117858887e-01 1.000000000000000000e+00 -2.087700068950653076e-01 9.530400037765502930e-01 5.819900035858154297e-01 1.000000000000000000e+00 -2.214200049638748169e-01 9.569200277328491211e-01 5.691400170326232910e-01 1.000000000000000000e+00 -2.344900071620941162e-01 9.606500267982482910e-01 5.561400055885314941e-01 1.000000000000000000e+00 -2.479699999094009399e-01 9.642300009727478027e-01 5.430300235748291016e-01 1.000000000000000000e+00 -2.617999911308288574e-01 9.676499962806701660e-01 5.298100113868713379e-01 1.000000000000000000e+00 -2.759700119495391846e-01 9.709200263023376465e-01 5.165299773216247559e-01 1.000000000000000000e+00 -2.904199957847595215e-01 9.740300178527832031e-01 5.032100081443786621e-01 1.000000000000000000e+00 -3.051300048828125000e-01 9.769700169563293457e-01 4.898700118064880371e-01 1.000000000000000000e+00 -3.200600147247314453e-01 9.797400236129760742e-01 4.765399992465972900e-01 1.000000000000000000e+00 -3.351700007915496826e-01 9.823399782180786133e-01 4.632500112056732178e-01 1.000000000000000000e+00 -3.504300117492675781e-01 9.847699999809265137e-01 4.500199854373931885e-01 1.000000000000000000e+00 -3.658100068569183350e-01 9.870200157165527344e-01 4.368799924850463867e-01 1.000000000000000000e+00 -3.812699913978576660e-01 9.890900254249572754e-01 4.238600134849548340e-01 1.000000000000000000e+00 -3.967800140380859375e-01 9.909800291061401367e-01 4.109799861907958984e-01 1.000000000000000000e+00 -4.122900068759918213e-01 9.926800131797790527e-01 3.982599973678588867e-01 1.000000000000000000e+00 -4.277800023555755615e-01 9.941899776458740234e-01 3.857499957084655762e-01 1.000000000000000000e+00 -4.432100057601928711e-01 9.955099821090698242e-01 3.734500110149383545e-01 1.000000000000000000e+00 -4.585399925708770752e-01 9.966300129890441895e-01 3.614000082015991211e-01 1.000000000000000000e+00 -4.737499952316284180e-01 9.975500106811523438e-01 3.496299982070922852e-01 1.000000000000000000e+00 -4.887900054454803467e-01 9.982799887657165527e-01 3.381600081920623779e-01 1.000000000000000000e+00 -5.036200284957885742e-01 9.987900257110595703e-01 3.270100057125091553e-01 1.000000000000000000e+00 -5.182200074195861816e-01 9.991000294685363770e-01 3.162199854850769043e-01 1.000000000000000000e+00 -5.325499773025512695e-01 9.991899728775024414e-01 3.058100044727325439e-01 1.000000000000000000e+00 -5.465800166130065918e-01 9.990699887275695801e-01 2.958100140094757080e-01 1.000000000000000000e+00 -5.602599978446960449e-01 9.987300038337707520e-01 2.862299978733062744e-01 1.000000000000000000e+00 -5.735700130462646484e-01 9.981700181961059570e-01 2.771199941635131836e-01 1.000000000000000000e+00 -5.864599943161010742e-01 9.973899722099304199e-01 2.684899866580963135e-01 1.000000000000000000e+00 -5.989099740982055664e-01 9.963799715042114258e-01 2.603799998760223389e-01 1.000000000000000000e+00 -6.108800172805786133e-01 9.951400160789489746e-01 2.527999877929687500e-01 1.000000000000000000e+00 -6.223300099372863770e-01 9.936599731445312500e-01 2.457900047302246094e-01 1.000000000000000000e+00 -6.332299709320068359e-01 9.919499754905700684e-01 2.393700033426284790e-01 1.000000000000000000e+00 -6.436200141906738281e-01 9.899899959564208984e-01 2.335599958896636963e-01 1.000000000000000000e+00 -6.539400219917297363e-01 9.877499938011169434e-01 2.283499985933303833e-01 1.000000000000000000e+00 -6.642799973487854004e-01 9.852399826049804688e-01 2.237000018358230591e-01 1.000000000000000000e+00 -6.746199727058410645e-01 9.824600219726562500e-01 2.196000069379806519e-01 1.000000000000000000e+00 -6.849399805068969727e-01 9.794099926948547363e-01 2.160200029611587524e-01 1.000000000000000000e+00 -6.952499747276306152e-01 9.761000275611877441e-01 2.129400074481964111e-01 1.000000000000000000e+00 -7.055299878120422363e-01 9.725499749183654785e-01 2.103199958801269531e-01 1.000000000000000000e+00 -7.157700061798095703e-01 9.687500000000000000e-01 2.081499993801116943e-01 1.000000000000000000e+00 -7.259600162506103516e-01 9.646999835968017578e-01 2.064000070095062256e-01 1.000000000000000000e+00 -7.361000180244445801e-01 9.604300260543823242e-01 2.050399929285049438e-01 1.000000000000000000e+00 -7.461699843406677246e-01 9.559299945831298828e-01 2.040600031614303589e-01 1.000000000000000000e+00 -7.561699748039245605e-01 9.512100219726562500e-01 2.034299969673156738e-01 1.000000000000000000e+00 -7.660800218582153320e-01 9.462699890136718750e-01 2.031099945306777954e-01 1.000000000000000000e+00 -7.759100198745727539e-01 9.411299824714660645e-01 2.030999958515167236e-01 1.000000000000000000e+00 -7.856299877166748047e-01 9.357900023460388184e-01 2.033600062131881714e-01 1.000000000000000000e+00 -7.952399849891662598e-01 9.302499890327453613e-01 2.038599997758865356e-01 1.000000000000000000e+00 -8.047299981117248535e-01 9.245200157165527344e-01 2.045899927616119385e-01 1.000000000000000000e+00 -8.141000270843505859e-01 9.186099767684936523e-01 2.055200040340423584e-01 1.000000000000000000e+00 -8.233299851417541504e-01 9.125300049781799316e-01 2.066300064325332642e-01 1.000000000000000000e+00 -8.324099779129028320e-01 9.062700271606445312e-01 2.078800052404403687e-01 1.000000000000000000e+00 -8.413299918174743652e-01 8.998600244522094727e-01 2.092600017786026001e-01 1.000000000000000000e+00 -8.500999808311462402e-01 8.932800292968750000e-01 2.107400000095367432e-01 1.000000000000000000e+00 -8.586800098419189453e-01 8.865500092506408691e-01 2.123000025749206543e-01 1.000000000000000000e+00 -8.670899868011474609e-01 8.796799778938293457e-01 2.139099985361099243e-01 1.000000000000000000e+00 -8.752999901771545410e-01 8.726699948310852051e-01 2.155500054359436035e-01 1.000000000000000000e+00 -8.833100199699401855e-01 8.655300140380859375e-01 2.171899974346160889e-01 1.000000000000000000e+00 -8.911200165748596191e-01 8.582599759101867676e-01 2.187999933958053589e-01 1.000000000000000000e+00 -8.986999988555908203e-01 8.508700132369995117e-01 2.203799933195114136e-01 1.000000000000000000e+00 -9.060500264167785645e-01 8.433700203895568848e-01 2.218800038099288940e-01 1.000000000000000000e+00 -9.131699800491333008e-01 8.357599973678588867e-01 2.232799977064132690e-01 1.000000000000000000e+00 -9.200400114059448242e-01 8.280599713325500488e-01 2.245599925518035889e-01 1.000000000000000000e+00 -9.266600012779235840e-01 8.202499747276306152e-01 2.257000058889389038e-01 1.000000000000000000e+00 -9.330099821090698242e-01 8.123599886894226074e-01 2.266699969768524170e-01 1.000000000000000000e+00 -9.390900135040283203e-01 8.043900132179260254e-01 2.274399995803833008e-01 1.000000000000000000e+00 -9.448900222778320312e-01 7.963399887084960938e-01 2.280000001192092896e-01 1.000000000000000000e+00 -9.503899812698364258e-01 7.882300019264221191e-01 2.283100038766860962e-01 1.000000000000000000e+00 -9.556000232696533203e-01 7.800499796867370605e-01 2.283599972724914551e-01 1.000000000000000000e+00 -9.604899883270263672e-01 7.718099951744079590e-01 2.281100004911422729e-01 1.000000000000000000e+00 -9.650700092315673828e-01 7.635200023651123047e-01 2.275400012731552124e-01 1.000000000000000000e+00 -9.693099856376647949e-01 7.551900148391723633e-01 2.266300022602081299e-01 1.000000000000000000e+00 -9.732300043106079102e-01 7.468199729919433594e-01 2.253600060939788818e-01 1.000000000000000000e+00 -9.767900109291076660e-01 7.384200096130371094e-01 2.236900031566619873e-01 1.000000000000000000e+00 -9.800000190734863281e-01 7.300000190734863281e-01 2.216099947690963745e-01 1.000000000000000000e+00 -9.828900098800659180e-01 7.214000225067138672e-01 2.191800028085708618e-01 1.000000000000000000e+00 -9.854900240898132324e-01 7.124999761581420898e-01 2.164999991655349731e-01 1.000000000000000000e+00 -9.878100156784057617e-01 7.032999992370605469e-01 2.135799974203109741e-01 1.000000000000000000e+00 -9.898599982261657715e-01 6.938199996948242188e-01 2.104299962520599365e-01 1.000000000000000000e+00 -9.916300177574157715e-01 6.840800046920776367e-01 2.070599943399429321e-01 1.000000000000000000e+00 -9.931399822235107422e-01 6.740800142288208008e-01 2.034800052642822266e-01 1.000000000000000000e+00 -9.943799972534179688e-01 6.638600230216979980e-01 1.997099965810775757e-01 1.000000000000000000e+00 -9.953500032424926758e-01 6.534100174903869629e-01 1.957699954509735107e-01 1.000000000000000000e+00 -9.960700273513793945e-01 6.427699923515319824e-01 1.916500031948089600e-01 1.000000000000000000e+00 -9.965400099754333496e-01 6.319299936294555664e-01 1.873800009489059448e-01 1.000000000000000000e+00 -9.967499971389770508e-01 6.209300160408020020e-01 1.829700022935867310e-01 1.000000000000000000e+00 -9.967200160026550293e-01 6.097699999809265137e-01 1.784200072288513184e-01 1.000000000000000000e+00 -9.964399933815002441e-01 5.984600186347961426e-01 1.737599968910217285e-01 1.000000000000000000e+00 -9.959300160408020020e-01 5.870299935340881348e-01 1.689900010824203491e-01 1.000000000000000000e+00 -9.951699972152709961e-01 5.754899978637695312e-01 1.641200035810470581e-01 1.000000000000000000e+00 -9.941899776458740234e-01 5.638599991798400879e-01 1.591800004243850708e-01 1.000000000000000000e+00 -9.929699897766113281e-01 5.521399974822998047e-01 1.541700065135955811e-01 1.000000000000000000e+00 -9.915300011634826660e-01 5.403599739074707031e-01 1.491000056266784668e-01 1.000000000000000000e+00 -9.898700118064880371e-01 5.285400152206420898e-01 1.439799964427947998e-01 1.000000000000000000e+00 -9.879900217056274414e-01 5.166699886322021484e-01 1.388300061225891113e-01 1.000000000000000000e+00 -9.858999848365783691e-01 5.047900080680847168e-01 1.336700022220611572e-01 1.000000000000000000e+00 -9.836000204086303711e-01 4.929099977016448975e-01 1.284900009632110596e-01 1.000000000000000000e+00 -9.810799956321716309e-01 4.810400009155273438e-01 1.233199983835220337e-01 1.000000000000000000e+00 -9.783700108528137207e-01 4.691999852657318115e-01 1.181700006127357483e-01 1.000000000000000000e+00 -9.754499793052673340e-01 4.573999941349029541e-01 1.130499988794326782e-01 1.000000000000000000e+00 -9.723399877548217773e-01 4.456500113010406494e-01 1.079699993133544922e-01 1.000000000000000000e+00 -9.690399765968322754e-01 4.339900016784667969e-01 1.029400005936622620e-01 1.000000000000000000e+00 -9.655500054359436035e-01 4.224100112915039062e-01 9.798000007867813110e-02 1.000000000000000000e+00 -9.618700146675109863e-01 4.109300076961517334e-01 9.309999644756317139e-02 1.000000000000000000e+00 -9.580100178718566895e-01 3.995800018310546875e-01 8.831000328063964844e-02 1.000000000000000000e+00 -9.539800286293029785e-01 3.883599936962127686e-01 8.361999690532684326e-02 1.000000000000000000e+00 -9.497699737548828125e-01 3.772900104522705078e-01 7.904999703168869019e-02 1.000000000000000000e+00 -9.453799724578857422e-01 3.663800060749053955e-01 7.461000233888626099e-02 1.000000000000000000e+00 -9.408400058746337891e-01 3.556599915027618408e-01 7.030999660491943359e-02 1.000000000000000000e+00 -9.361199736595153809e-01 3.451299965381622314e-01 6.616000086069107056e-02 1.000000000000000000e+00 -9.312499761581420898e-01 3.348200023174285889e-01 6.218000128865242004e-02 1.000000000000000000e+00 -9.262300133705139160e-01 3.247300088405609131e-01 5.837000161409378052e-02 1.000000000000000000e+00 -9.210500121116638184e-01 3.148899972438812256e-01 5.474999919533729553e-02 1.000000000000000000e+00 -9.157199859619140625e-01 3.052999973297119141e-01 5.133999884128570557e-02 1.000000000000000000e+00 -9.102399945259094238e-01 2.959899902343750000e-01 4.814000055193901062e-02 1.000000000000000000e+00 -9.046300053596496582e-01 2.869600057601928711e-01 4.515999928116798401e-02 1.000000000000000000e+00 -8.988800048828125000e-01 2.782399952411651611e-01 4.242999851703643799e-02 1.000000000000000000e+00 -8.929799795150756836e-01 2.698099911212921143e-01 3.993000090122222900e-02 1.000000000000000000e+00 -8.869100213050842285e-01 2.615199983119964600e-01 3.753000125288963318e-02 1.000000000000000000e+00 -8.806599974632263184e-01 2.533400058746337891e-01 3.520999848842620850e-02 1.000000000000000000e+00 -8.742200136184692383e-01 2.452600002288818359e-01 3.297000005841255188e-02 1.000000000000000000e+00 -8.676000237464904785e-01 2.372999936342239380e-01 3.082000091671943665e-02 1.000000000000000000e+00 -8.607900142669677734e-01 2.294500023126602173e-01 2.875000052154064178e-02 1.000000000000000000e+00 -8.537999987602233887e-01 2.216999977827072144e-01 2.676999941468238831e-02 1.000000000000000000e+00 -8.466200232505798340e-01 2.140700072050094604e-01 2.487000077962875366e-02 1.000000000000000000e+00 -8.392599821090698242e-01 2.065400034189224243e-01 2.305000089108943939e-02 1.000000000000000000e+00 -8.317199945449829102e-01 1.991200000047683716e-01 2.130999974906444550e-02 1.000000000000000000e+00 -8.239899873733520508e-01 1.918199956417083740e-01 1.965999975800514221e-02 1.000000000000000000e+00 -8.160799741744995117e-01 1.846199929714202881e-01 1.809000037610530853e-02 1.000000000000000000e+00 -8.079900145530700684e-01 1.775300055742263794e-01 1.659999974071979523e-02 1.000000000000000000e+00 -7.997099757194519043e-01 1.705500036478042603e-01 1.520000025629997253e-02 1.000000000000000000e+00 -7.912499904632568359e-01 1.636800020933151245e-01 1.386999990791082382e-02 1.000000000000000000e+00 -7.825999855995178223e-01 1.569299995899200439e-01 1.264000032097101212e-02 1.000000000000000000e+00 -7.737699747085571289e-01 1.502799987792968750e-01 1.147999987006187439e-02 1.000000000000000000e+00 -7.647600173950195312e-01 1.437399983406066895e-01 1.040999963879585266e-02 1.000000000000000000e+00 -7.555599808692932129e-01 1.373099982738494873e-01 9.420000016689300537e-03 1.000000000000000000e+00 -7.461699843406677246e-01 1.309799998998641968e-01 8.510000072419643402e-03 1.000000000000000000e+00 -7.366099953651428223e-01 1.247700005769729614e-01 7.689999882131814957e-03 1.000000000000000000e+00 -7.268599867820739746e-01 1.186700016260147095e-01 6.949999835342168808e-03 1.000000000000000000e+00 -7.169200181961059570e-01 1.126800030469894409e-01 6.289999932050704956e-03 1.000000000000000000e+00 -7.067999839782714844e-01 1.067999973893165588e-01 5.710000172257423401e-03 1.000000000000000000e+00 -6.965000033378601074e-01 1.010200008749961853e-01 5.220000166445970535e-03 1.000000000000000000e+00 -6.860200166702270508e-01 9.536000341176986694e-02 4.809999838471412659e-03 1.000000000000000000e+00 -6.753500103950500488e-01 8.980000019073486328e-02 4.490000195801258087e-03 1.000000000000000000e+00 -6.644899845123291016e-01 8.436000347137451172e-02 4.240000154823064804e-03 1.000000000000000000e+00 -6.534500122070312500e-01 7.902000099420547485e-02 4.079999867826700211e-03 1.000000000000000000e+00 -6.422299742698669434e-01 7.379999756813049316e-02 4.009999800473451614e-03 1.000000000000000000e+00 -6.308199763298034668e-01 6.868000328540802002e-02 4.009999800473451614e-03 1.000000000000000000e+00 -6.192299723625183105e-01 6.367000192403793335e-02 4.100000020116567612e-03 1.000000000000000000e+00 -6.074600219726562500e-01 5.877999961376190186e-02 4.269999917596578598e-03 1.000000000000000000e+00 -5.954999923706054688e-01 5.398999899625778198e-02 4.530000034719705582e-03 1.000000000000000000e+00 -5.833600163459777832e-01 4.930999875068664551e-02 4.860000219196081161e-03 1.000000000000000000e+00 -5.710300207138061523e-01 4.473999887704849243e-02 5.289999768137931824e-03 1.000000000000000000e+00 -5.585200190544128418e-01 4.027999937534332275e-02 5.789999850094318390e-03 1.000000000000000000e+00 -5.458300113677978516e-01 3.593000024557113647e-02 6.380000151693820953e-03 1.000000000000000000e+00 -5.329499840736389160e-01 3.169000148773193359e-02 7.050000131130218506e-03 1.000000000000000000e+00 -5.198900103569030762e-01 2.755999937653541565e-02 7.799999788403511047e-03 1.000000000000000000e+00 -5.066400170326232910e-01 2.353999949991703033e-02 8.630000054836273193e-03 1.000000000000000000e+00 -4.932099878787994385e-01 1.962999999523162842e-02 9.549999609589576721e-03 1.000000000000000000e+00 -4.796000123023986816e-01 1.583000086247920990e-02 1.054999977350234985e-02 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/twilight b/fastplotlib/utils/colormaps/twilight deleted file mode 100644 index c148a835c..000000000 --- a/fastplotlib/utils/colormaps/twilight +++ /dev/null @@ -1,256 +0,0 @@ -8.857501745223999023e-01 8.500092625617980957e-01 8.879736661911010742e-01 1.000000000000000000e+00 -8.837851881980895996e-01 8.507294058799743652e-01 8.872322440147399902e-01 1.000000000000000000e+00 -8.817223310470581055e-01 8.512759208679199219e-01 8.863805532455444336e-01 1.000000000000000000e+00 -8.795410394668579102e-01 8.516567349433898926e-01 8.854143619537353516e-01 1.000000000000000000e+00 -8.772488236427307129e-01 8.518702983856201172e-01 8.843411803245544434e-01 1.000000000000000000e+00 -8.748534917831420898e-01 8.519152402877807617e-01 8.831692934036254883e-01 1.000000000000000000e+00 -8.723313212394714355e-01 8.518016338348388672e-01 8.818970322608947754e-01 1.000000000000000000e+00 -8.697047233581542969e-01 8.515240550041198730e-01 8.805388212203979492e-01 1.000000000000000000e+00 -8.669601678848266602e-01 8.510895967483520508e-01 8.790976405143737793e-01 1.000000000000000000e+00 -8.640898466110229492e-01 8.505039215087890625e-01 8.775792717933654785e-01 1.000000000000000000e+00 -8.611024618148803711e-01 8.497675657272338867e-01 8.759924173355102539e-01 1.000000000000000000e+00 -8.579825758934020996e-01 8.488893508911132812e-01 8.743404150009155273e-01 1.000000000000000000e+00 -8.547259569168090820e-01 8.478748798370361328e-01 8.726282715797424316e-01 1.000000000000000000e+00 -8.513371348381042480e-01 8.467273712158203125e-01 8.708608150482177734e-01 1.000000000000000000e+00 -8.478071093559265137e-01 8.454546332359313965e-01 8.690403699874877930e-01 1.000000000000000000e+00 -8.441261649131774902e-01 8.440648317337036133e-01 8.671697378158569336e-01 1.000000000000000000e+00 -8.403041958808898926e-01 8.425605893135070801e-01 8.652508854866027832e-01 1.000000000000000000e+00 -8.363403081893920898e-01 8.409479856491088867e-01 8.632853031158447266e-01 1.000000000000000000e+00 -8.322270512580871582e-01 8.392348885536193848e-01 8.612756133079528809e-01 1.000000000000000000e+00 -8.279689550399780273e-01 8.374260067939758301e-01 8.592240214347839355e-01 1.000000000000000000e+00 -8.235743045806884766e-01 8.355248570442199707e-01 8.571318984031677246e-01 1.000000000000000000e+00 -8.190465569496154785e-01 8.335365056991577148e-01 8.550020456314086914e-01 1.000000000000000000e+00 -8.143898248672485352e-01 8.314656019210815430e-01 8.528375625610351562e-01 1.000000000000000000e+00 -8.095999956130981445e-01 8.293189406394958496e-01 8.506444096565246582e-01 1.000000000000000000e+00 -8.046916723251342773e-01 8.270983695983886719e-01 8.484244942665100098e-01 1.000000000000000000e+00 -7.996707558631896973e-01 8.248078227043151855e-01 8.461821079254150391e-01 1.000000000000000000e+00 -7.945430278778076172e-01 8.224511742591857910e-01 8.439218401908874512e-01 1.000000000000000000e+00 -7.893144488334655762e-01 8.200321197509765625e-01 8.416486382484436035e-01 1.000000000000000000e+00 -7.839910387992858887e-01 8.175542354583740234e-01 8.393674492835998535e-01 1.000000000000000000e+00 -7.785789370536804199e-01 8.150209188461303711e-01 8.370834589004516602e-01 1.000000000000000000e+00 -7.730841636657714844e-01 8.124352693557739258e-01 8.348017334938049316e-01 1.000000000000000000e+00 -7.675110697746276855e-01 8.098007440567016602e-01 8.325281739234924316e-01 1.000000000000000000e+00 -7.618690729141235352e-01 8.071194887161254883e-01 8.302664756774902344e-01 1.000000000000000000e+00 -7.561644315719604492e-01 8.043940663337707520e-01 8.280214071273803711e-01 1.000000000000000000e+00 -7.504034638404846191e-01 8.016269803047180176e-01 8.257973790168762207e-01 1.000000000000000000e+00 -7.445924878120422363e-01 7.988204956054687500e-01 8.235986828804016113e-01 1.000000000000000000e+00 -7.387377023696899414e-01 7.959766387939453125e-01 8.214292526245117188e-01 1.000000000000000000e+00 -7.328454256057739258e-01 7.930974364280700684e-01 8.192926049232482910e-01 1.000000000000000000e+00 -7.269217967987060547e-01 7.901846766471862793e-01 8.171921968460083008e-01 1.000000000000000000e+00 -7.209728360176086426e-01 7.872399687767028809e-01 8.151307106018066406e-01 1.000000000000000000e+00 -7.150040268898010254e-01 7.842648625373840332e-01 8.131111860275268555e-01 1.000000000000000000e+00 -7.090207934379577637e-01 7.812609076499938965e-01 8.111359477043151855e-01 1.000000000000000000e+00 -7.030297517776489258e-01 7.782290577888488770e-01 8.092061877250671387e-01 1.000000000000000000e+00 -6.970365643501281738e-01 7.751705050468444824e-01 8.073233366012573242e-01 1.000000000000000000e+00 -6.910464167594909668e-01 7.720863223075866699e-01 8.054884076118469238e-01 1.000000000000000000e+00 -6.850644350051879883e-01 7.689774036407470703e-01 8.037020564079284668e-01 1.000000000000000000e+00 -6.790955662727355957e-01 7.658447027206420898e-01 8.019646406173706055e-01 1.000000000000000000e+00 -6.731442213058471680e-01 7.626891136169433594e-01 8.002762794494628906e-01 1.000000000000000000e+00 -6.672148108482360840e-01 7.595112919807434082e-01 7.986367344856262207e-01 1.000000000000000000e+00 -6.613112688064575195e-01 7.563120126724243164e-01 7.970455884933471680e-01 1.000000000000000000e+00 -6.554369330406188965e-01 7.530921101570129395e-01 7.955027222633361816e-01 1.000000000000000000e+00 -6.495957374572753906e-01 7.498520016670227051e-01 7.940067648887634277e-01 1.000000000000000000e+00 -6.437910795211791992e-01 7.465924024581909180e-01 7.925565242767333984e-01 1.000000000000000000e+00 -6.380258798599243164e-01 7.433137893676757812e-01 7.911509871482849121e-01 1.000000000000000000e+00 -6.323027014732360840e-01 7.400166988372802734e-01 7.897889018058776855e-01 1.000000000000000000e+00 -6.266240477561950684e-01 7.367017269134521484e-01 7.884690165519714355e-01 1.000000000000000000e+00 -6.209919452667236328e-01 7.333693504333496094e-01 7.871899604797363281e-01 1.000000000000000000e+00 -6.154084801673889160e-01 7.300199270248413086e-01 7.859502434730529785e-01 1.000000000000000000e+00 -6.098754405975341797e-01 7.266539931297302246e-01 7.847483754158020020e-01 1.000000000000000000e+00 -6.043943166732788086e-01 7.232718467712402344e-01 7.835829854011535645e-01 1.000000000000000000e+00 -5.989665985107421875e-01 7.198739647865295410e-01 7.824525833129882812e-01 1.000000000000000000e+00 -5.935933589935302734e-01 7.164605855941772461e-01 7.813558578491210938e-01 1.000000000000000000e+00 -5.882757902145385742e-01 7.130321264266967773e-01 7.802914381027221680e-01 1.000000000000000000e+00 -5.830148458480834961e-01 7.095888853073120117e-01 7.792578339576721191e-01 1.000000000000000000e+00 -5.778116583824157715e-01 7.061310410499572754e-01 7.782534360885620117e-01 1.000000000000000000e+00 -5.726668834686279297e-01 7.026589512825012207e-01 7.772770524024963379e-01 1.000000000000000000e+00 -5.675811767578125000e-01 6.991727948188781738e-01 7.763274908065795898e-01 1.000000000000000000e+00 -5.625551342964172363e-01 6.956728100776672363e-01 7.754036188125610352e-01 1.000000000000000000e+00 -5.575894117355346680e-01 6.921591162681579590e-01 7.745041251182556152e-01 1.000000000000000000e+00 -5.526844859123229980e-01 6.886319518089294434e-01 7.736279368400573730e-01 1.000000000000000000e+00 -5.478409528732299805e-01 6.850914359092712402e-01 7.727738618850708008e-01 1.000000000000000000e+00 -5.430593490600585938e-01 6.815376877784729004e-01 7.719407677650451660e-01 1.000000000000000000e+00 -5.383401513099670410e-01 6.779708266258239746e-01 7.711273431777954102e-01 1.000000000000000000e+00 -5.336838960647583008e-01 6.743909120559692383e-01 7.703325152397155762e-01 1.000000000000000000e+00 -5.290908813476562500e-01 6.707981228828430176e-01 7.695555090904235840e-01 1.000000000000000000e+00 -5.245615243911743164e-01 6.671924591064453125e-01 7.687954306602478027e-01 1.000000000000000000e+00 -5.200963020324707031e-01 6.635739207267761230e-01 7.680512070655822754e-01 1.000000000000000000e+00 -5.156955718994140625e-01 6.599426269531250000e-01 7.673219442367553711e-01 1.000000000000000000e+00 -5.113599300384521484e-01 6.562985181808471680e-01 7.666066288948059082e-01 1.000000000000000000e+00 -5.070896744728088379e-01 6.526417136192321777e-01 7.659044861793518066e-01 1.000000000000000000e+00 -5.028853416442871094e-01 6.489721536636352539e-01 7.652144432067871094e-01 1.000000000000000000e+00 -4.987473487854003906e-01 6.452898979187011719e-01 7.645357847213745117e-01 1.000000000000000000e+00 -4.946761727333068848e-01 6.415948271751403809e-01 7.638671994209289551e-01 1.000000000000000000e+00 -4.906722605228424072e-01 6.378870606422424316e-01 7.632081508636474609e-01 1.000000000000000000e+00 -4.867359697818756104e-01 6.341664791107177734e-01 7.625578045845031738e-01 1.000000000000000000e+00 -4.828677773475646973e-01 6.304330229759216309e-01 7.619153857231140137e-01 1.000000000000000000e+00 -4.790681600570678711e-01 6.266867518424987793e-01 7.612800002098083496e-01 1.000000000000000000e+00 -4.753375351428985596e-01 6.229275465011596680e-01 7.606508731842041016e-01 1.000000000000000000e+00 -4.716762900352478027e-01 6.191554069519042969e-01 7.600271105766296387e-01 1.000000000000000000e+00 -4.680849015712738037e-01 6.153702735900878906e-01 7.594078779220581055e-01 1.000000000000000000e+00 -4.645637571811676025e-01 6.115720868110656738e-01 7.587924003601074219e-01 1.000000000000000000e+00 -4.611132740974426270e-01 6.077607870101928711e-01 7.581798434257507324e-01 1.000000000000000000e+00 -4.577337801456451416e-01 6.039363145828247070e-01 7.575693726539611816e-01 1.000000000000000000e+00 -4.544256329536437988e-01 6.000986099243164062e-01 7.569601535797119141e-01 1.000000000000000000e+00 -4.511891901493072510e-01 5.962476134300231934e-01 7.563512325286865234e-01 1.000000000000000000e+00 -4.480247199535369873e-01 5.923833250999450684e-01 7.557417750358581543e-01 1.000000000000000000e+00 -4.449324607849121094e-01 5.885056257247924805e-01 7.551311254501342773e-01 1.000000000000000000e+00 -4.419127106666564941e-01 5.846143960952758789e-01 7.545183897018432617e-01 1.000000000000000000e+00 -4.389656484127044678e-01 5.807096958160400391e-01 7.539027333259582520e-01 1.000000000000000000e+00 -4.360913932323455811e-01 5.767914056777954102e-01 7.532833814620971680e-01 1.000000000000000000e+00 -4.332900941371917725e-01 5.728594064712524414e-01 7.526594400405883789e-01 1.000000000000000000e+00 -4.305617809295654297e-01 5.689137578010559082e-01 7.520300745964050293e-01 1.000000000000000000e+00 -4.279065132141113281e-01 5.649542808532714844e-01 7.513944506645202637e-01 1.000000000000000000e+00 -4.253242313861846924e-01 5.609810352325439453e-01 7.507516741752624512e-01 1.000000000000000000e+00 -4.228148460388183594e-01 5.569939017295837402e-01 7.501008510589599609e-01 1.000000000000000000e+00 -4.203782379627227783e-01 5.529928803443908691e-01 7.494412660598754883e-01 1.000000000000000000e+00 -4.180141389369964600e-01 5.489778518676757812e-01 7.487719058990478516e-01 1.000000000000000000e+00 -4.157223403453826904e-01 5.449488162994384766e-01 7.480920553207397461e-01 1.000000000000000000e+00 -4.135024547576904297e-01 5.409057736396789551e-01 7.474007606506347656e-01 1.000000000000000000e+00 -4.113541543483734131e-01 5.368486046791076660e-01 7.466971278190612793e-01 1.000000000000000000e+00 -4.092769026756286621e-01 5.327773094177246094e-01 7.459803223609924316e-01 1.000000000000000000e+00 -4.072701930999755859e-01 5.286918878555297852e-01 7.452494502067565918e-01 1.000000000000000000e+00 -4.053334295749664307e-01 5.245922803878784180e-01 7.445036768913269043e-01 1.000000000000000000e+00 -4.034660160541534424e-01 5.204784870147705078e-01 7.437421679496765137e-01 1.000000000000000000e+00 -4.016671478748321533e-01 5.163504481315612793e-01 7.429640293121337891e-01 1.000000000000000000e+00 -3.999360799789428711e-01 5.122081637382507324e-01 7.421684265136718750e-01 1.000000000000000000e+00 -3.982719182968139648e-01 5.080516934394836426e-01 7.413545250892639160e-01 1.000000000000000000e+00 -3.966737389564514160e-01 5.038809180259704590e-01 7.405213713645935059e-01 1.000000000000000000e+00 -3.951405882835388184e-01 4.996958673000335693e-01 7.396681904792785645e-01 1.000000000000000000e+00 -3.936713635921478271e-01 4.954965710639953613e-01 7.387940883636474609e-01 1.000000000000000000e+00 -3.922649621963500977e-01 4.912829995155334473e-01 7.378982305526733398e-01 1.000000000000000000e+00 -3.909201622009277344e-01 4.870552122592926025e-01 7.369797825813293457e-01 1.000000000000000000e+00 -3.896358013153076172e-01 4.828131794929504395e-01 7.360378503799438477e-01 1.000000000000000000e+00 -3.884105384349822998e-01 4.785569012165069580e-01 7.350715994834899902e-01 1.000000000000000000e+00 -3.872430026531219482e-01 4.742864668369293213e-01 7.340801954269409180e-01 1.000000000000000000e+00 -3.861318528652191162e-01 4.700018465518951416e-01 7.330628037452697754e-01 1.000000000000000000e+00 -3.850755691528320312e-01 4.657030701637268066e-01 7.320185303688049316e-01 1.000000000000000000e+00 -3.840726912021636963e-01 4.613901972770690918e-01 7.309466600418090820e-01 1.000000000000000000e+00 -3.831216692924499512e-01 4.570632278919219971e-01 7.298462390899658203e-01 1.000000000000000000e+00 -3.822209537029266357e-01 4.527222514152526855e-01 7.287165522575378418e-01 1.000000000000000000e+00 -3.813688755035400391e-01 4.483672678470611572e-01 7.275567054748535156e-01 1.000000000000000000e+00 -3.805637955665588379e-01 4.439983665943145752e-01 7.263658642768859863e-01 1.000000000000000000e+00 -3.798040449619293213e-01 4.396155774593353271e-01 7.251432538032531738e-01 1.000000000000000000e+00 -3.790878951549530029e-01 4.352189898490905762e-01 7.238879799842834473e-01 1.000000000000000000e+00 -3.784136474132537842e-01 4.308086037635803223e-01 7.225993275642395020e-01 1.000000000000000000e+00 -3.777794837951660156e-01 4.263845086097717285e-01 7.212764024734497070e-01 1.000000000000000000e+00 -3.771837055683135986e-01 4.219467937946319580e-01 7.199184298515319824e-01 1.000000000000000000e+00 -3.766244947910308838e-01 4.174955487251281738e-01 7.185245752334594727e-01 1.000000000000000000e+00 -3.761000037193298340e-01 4.130308032035827637e-01 7.170939445495605469e-01 1.000000000000000000e+00 -3.756084740161895752e-01 4.085526764392852783e-01 7.156258225440979004e-01 1.000000000000000000e+00 -3.751480281352996826e-01 4.040612578392028809e-01 7.141193747520446777e-01 1.000000000000000000e+00 -3.747168481349945068e-01 3.995566368103027344e-01 7.125737071037292480e-01 1.000000000000000000e+00 -3.743131458759307861e-01 3.950389623641967773e-01 7.109879851341247559e-01 1.000000000000000000e+00 -3.739349842071533203e-01 3.905082643032073975e-01 7.093613147735595703e-01 1.000000000000000000e+00 -3.735806345939636230e-01 3.859647512435913086e-01 7.076929807662963867e-01 1.000000000000000000e+00 -3.732481598854064941e-01 3.814084827899932861e-01 7.059820294380187988e-01 1.000000000000000000e+00 -3.729357719421386719e-01 3.768396377563476562e-01 7.042275667190551758e-01 1.000000000000000000e+00 -3.726416528224945068e-01 3.722583353519439697e-01 7.024287581443786621e-01 1.000000000000000000e+00 -3.723639845848083496e-01 3.676647841930389404e-01 7.005846500396728516e-01 1.000000000000000000e+00 -3.721008896827697754e-01 3.630591034889221191e-01 6.986943483352661133e-01 1.000000000000000000e+00 -3.718506097793579102e-01 3.584414720535278320e-01 6.967569589614868164e-01 1.000000000000000000e+00 -3.716113269329071045e-01 3.538121283054351807e-01 6.947715282440185547e-01 1.000000000000000000e+00 -3.713812530040740967e-01 3.491712808609008789e-01 6.927370429039001465e-01 1.000000000000000000e+00 -3.711585700511932373e-01 3.445191085338592529e-01 6.906525492668151855e-01 1.000000000000000000e+00 -3.709415197372436523e-01 3.398559093475341797e-01 6.885170340538024902e-01 1.000000000000000000e+00 -3.707283437252044678e-01 3.351819515228271484e-01 6.863294839859008789e-01 1.000000000000000000e+00 -3.705173730850219727e-01 3.304974138736724854e-01 6.840888857841491699e-01 1.000000000000000000e+00 -3.703068196773529053e-01 3.258026838302612305e-01 6.817941069602966309e-01 1.000000000000000000e+00 -3.700948655605316162e-01 3.210981488227844238e-01 6.794440746307373047e-01 1.000000000000000000e+00 -3.698798120021820068e-01 3.163841068744659424e-01 6.770375370979309082e-01 1.000000000000000000e+00 -3.696598708629608154e-01 3.116609752178192139e-01 6.745734214782714844e-01 1.000000000000000000e+00 -3.694333434104919434e-01 3.069292306900024414e-01 6.720505356788635254e-01 1.000000000000000000e+00 -3.691984713077545166e-01 3.021893203258514404e-01 6.694675683975219727e-01 1.000000000000000000e+00 -3.689535558223724365e-01 2.974417507648468018e-01 6.668232083320617676e-01 1.000000000000000000e+00 -3.686968088150024414e-01 2.926870882511138916e-01 6.641162633895874023e-01 1.000000000000000000e+00 -3.684265613555908203e-01 2.879259586334228516e-01 6.613452434539794922e-01 1.000000000000000000e+00 -3.681410253047943115e-01 2.831590175628662109e-01 6.585088968276977539e-01 1.000000000000000000e+00 -3.678384423255920410e-01 2.783869802951812744e-01 6.556056737899780273e-01 1.000000000000000000e+00 -3.675170838832855225e-01 2.736106216907501221e-01 6.526341438293457031e-01 1.000000000000000000e+00 -3.671751320362091064e-01 2.688308656215667725e-01 6.495926976203918457e-01 1.000000000000000000e+00 -3.668108582496643066e-01 2.640485763549804688e-01 6.464799046516418457e-01 1.000000000000000000e+00 -3.664224445819854736e-01 2.592647969722747803e-01 6.432940959930419922e-01 1.000000000000000000e+00 -3.660085499286651611e-01 2.544804513454437256e-01 6.400336027145385742e-01 1.000000000000000000e+00 -3.655669689178466797e-01 2.496968358755111694e-01 6.366967558860778809e-01 1.000000000000000000e+00 -3.650957942008972168e-01 2.449153661727905273e-01 6.332817077636718750e-01 1.000000000000000000e+00 -3.645930886268615723e-01 2.401374727487564087e-01 6.297867894172668457e-01 1.000000000000000000e+00 -3.640569448471069336e-01 2.353647053241729736e-01 6.262101531028747559e-01 1.000000000000000000e+00 -3.634853661060333252e-01 2.305987626314163208e-01 6.225498914718627930e-01 1.000000000000000000e+00 -3.628764450550079346e-01 2.258414924144744873e-01 6.188041567802429199e-01 1.000000000000000000e+00 -3.622280955314636230e-01 2.210948914289474487e-01 6.149711012840270996e-01 1.000000000000000000e+00 -3.615382909774780273e-01 2.163611203432083130e-01 6.110488176345825195e-01 1.000000000000000000e+00 -3.608049452304840088e-01 2.116425186395645142e-01 6.070353388786315918e-01 1.000000000000000000e+00 -3.600268065929412842e-01 2.069412320852279663e-01 6.029284596443176270e-01 1.000000000000000000e+00 -3.592008948326110840e-01 2.022603750228881836e-01 5.987265110015869141e-01 1.000000000000000000e+00 -3.583248853683471680e-01 1.976029425859451294e-01 5.944277048110961914e-01 1.000000000000000000e+00 -3.573966324329376221e-01 1.929720789194107056e-01 5.900301337242126465e-01 1.000000000000000000e+00 -3.564138114452362061e-01 1.883711963891983032e-01 5.855320692062377930e-01 1.000000000000000000e+00 -3.553741574287414551e-01 1.838039308786392212e-01 5.809319019317626953e-01 1.000000000000000000e+00 -3.542753458023071289e-01 1.792741268873214722e-01 5.762280821800231934e-01 1.000000000000000000e+00 -3.531157374382019043e-01 1.747857034206390381e-01 5.714187026023864746e-01 1.000000000000000000e+00 -3.518924713134765625e-01 1.703432053327560425e-01 5.665028691291809082e-01 1.000000000000000000e+00 -3.506030440330505371e-01 1.659512966871261597e-01 5.614796280860900879e-01 1.000000000000000000e+00 -3.492451310157775879e-01 1.616147756576538086e-01 5.563483834266662598e-01 1.000000000000000000e+00 -3.478165268898010254e-01 1.573386341333389282e-01 5.511085391044616699e-01 1.000000000000000000e+00 -3.463150858879089355e-01 1.531280279159545898e-01 5.457599759101867676e-01 1.000000000000000000e+00 -3.447390198707580566e-01 1.489882022142410278e-01 5.403024554252624512e-01 1.000000000000000000e+00 -3.430860042572021484e-01 1.449246555566787720e-01 5.347370505332946777e-01 1.000000000000000000e+00 -3.413541018962860107e-01 1.409427970647811890e-01 5.290650129318237305e-01 1.000000000000000000e+00 -3.395416736602783203e-01 1.370480209589004517e-01 5.232879519462585449e-01 1.000000000000000000e+00 -3.376473188400268555e-01 1.332456171512603760e-01 5.174080729484558105e-01 1.000000000000000000e+00 -3.356697857379913330e-01 1.295407414436340332e-01 5.114280581474304199e-01 1.000000000000000000e+00 -3.336080610752105713e-01 1.259381771087646484e-01 5.053516626358032227e-01 1.000000000000000000e+00 -3.314615488052368164e-01 1.224424540996551514e-01 4.991827607154846191e-01 1.000000000000000000e+00 -3.292300403118133545e-01 1.190576404333114624e-01 4.929259419441223145e-01 1.000000000000000000e+00 -3.269137144088745117e-01 1.157873496413230896e-01 4.865864515304565430e-01 1.000000000000000000e+00 -3.245130777359008789e-01 1.126345992088317871e-01 4.801700711250305176e-01 1.000000000000000000e+00 -3.220288157463073730e-01 1.096011400222778320e-01 4.736849367618560791e-01 1.000000000000000000e+00 -3.194626271724700928e-01 1.066887974739074707e-01 4.671372771263122559e-01 1.000000000000000000e+00 -3.168164789676666260e-01 1.038986146450042725e-01 4.605341553688049316e-01 1.000000000000000000e+00 -3.140927851200103760e-01 1.012307778000831604e-01 4.538833498954772949e-01 1.000000000000000000e+00 -3.112943470478057861e-01 9.868477284908294678e-02 4.471931457519531250e-01 1.000000000000000000e+00 -3.084244430065155029e-01 9.625938534736633301e-02 4.404719471931457520e-01 1.000000000000000000e+00 -3.054867684841156006e-01 9.395276755094528198e-02 4.337284862995147705e-01 1.000000000000000000e+00 -3.024853765964508057e-01 9.176118671894073486e-02 4.269740283489227295e-01 1.000000000000000000e+00 -2.994248270988464355e-01 8.968225121498107910e-02 4.202162027359008789e-01 1.000000000000000000e+00 -2.963100075721740723e-01 8.771324902772903442e-02 4.134625792503356934e-01 1.000000000000000000e+00 -2.931459248065948486e-01 8.585065603256225586e-02 4.067217707633972168e-01 1.000000000000000000e+00 -2.899379134178161621e-01 8.409079164266586304e-02 4.000021517276763916e-01 1.000000000000000000e+00 -2.866915166378021240e-01 8.242987096309661865e-02 3.933118283748626709e-01 1.000000000000000000e+00 -2.834123969078063965e-01 8.086415380239486694e-02 3.866586983203887939e-01 1.000000000000000000e+00 -2.801063954830169678e-01 7.938999682664871216e-02 3.800502717494964600e-01 1.000000000000000000e+00 -2.767793834209442139e-01 7.800394296646118164e-02 3.734938204288482666e-01 1.000000000000000000e+00 -2.734373807907104492e-01 7.670280337333679199e-02 3.669961690902709961e-01 1.000000000000000000e+00 -2.700863778591156006e-01 7.548367232084274292e-02 3.605637550354003906e-01 1.000000000000000000e+00 -2.667323350906372070e-01 7.434401661157608032e-02 3.542027473449707031e-01 1.000000000000000000e+00 -2.633812129497528076e-01 7.328166067600250244e-02 3.479188978672027588e-01 1.000000000000000000e+00 -2.600389420986175537e-01 7.229477912187576294e-02 3.417175710201263428e-01 1.000000000000000000e+00 -2.567119300365447998e-01 7.138010859489440918e-02 3.356064856052398682e-01 1.000000000000000000e+00 -2.534068524837493896e-01 7.053358107805252075e-02 3.295945823192596436e-01 1.000000000000000000e+00 -2.501284480094909668e-01 6.975820660591125488e-02 3.236809968948364258e-01 1.000000000000000000e+00 -2.468822598457336426e-01 6.905364245176315308e-02 3.178699314594268799e-01 1.000000000000000000e+00 -2.436737269163131714e-01 6.841985881328582764e-02 3.121652305126190186e-01 1.000000000000000000e+00 -2.405081391334533691e-01 6.785710155963897705e-02 3.065705597400665283e-01 1.000000000000000000e+00 -2.373906224966049194e-01 6.736588478088378906e-02 3.010892271995544434e-01 1.000000000000000000e+00 -2.343305498361587524e-01 6.693559885025024414e-02 2.957400977611541748e-01 1.000000000000000000e+00 -2.313295453786849976e-01 6.657619029283523560e-02 2.905136048793792725e-01 1.000000000000000000e+00 -2.283917665481567383e-01 6.628997623920440674e-02 2.854107320308685303e-01 1.000000000000000000e+00 -2.255216389894485474e-01 6.607817113399505615e-02 2.804339826107025146e-01 1.000000000000000000e+00 -2.227270603179931641e-01 6.593379378318786621e-02 2.755971550941467285e-01 1.000000000000000000e+00 -2.200125157833099365e-01 6.585791707038879395e-02 2.709028124809265137e-01 1.000000000000000000e+00 -2.173784524202346802e-01 6.585966050624847412e-02 2.663421034812927246e-01 1.000000000000000000e+00 -2.148284316062927246e-01 6.594038754701614380e-02 2.619167566299438477e-01 1.000000000000000000e+00 -2.123741060495376587e-01 6.608502566814422607e-02 2.576516568660736084e-01 1.000000000000000000e+00 -2.100121378898620605e-01 6.630857288837432861e-02 2.535288929939270020e-01 1.000000000000000000e+00 -2.077442407608032227e-01 6.661453098058700562e-02 2.495464384555816650e-01 1.000000000000000000e+00 -2.055805176496505737e-01 6.699046492576599121e-02 2.457249760627746582e-01 1.000000000000000000e+00 -2.035200744867324829e-01 6.744418293237686157e-02 2.420557588338851929e-01 1.000000000000000000e+00 -2.015613317489624023e-01 6.798326969146728516e-02 2.385297417640686035e-01 1.000000000000000000e+00 -1.997157186269760132e-01 6.859271228313446045e-02 2.351709455251693726e-01 1.000000000000000000e+00 -1.979483366012573242e-01 6.931406259536743164e-02 2.319464683532714844e-01 1.000000000000000000e+00 -1.960826069116592407e-01 7.032122462987899780e-02 2.287467271089553833e-01 1.000000000000000000e+00 -1.941035091876983643e-01 7.160830497741699219e-02 2.255872786045074463e-01 1.000000000000000000e+00 -1.919944882392883301e-01 7.318282872438430786e-02 2.224338501691818237e-01 1.000000000000000000e+00 -1.897585391998291016e-01 7.501985877752304077e-02 2.193005084991455078e-01 1.000000000000000000e+00 -1.873922795057296753e-01 7.710209488868713379e-02 2.161887586116790771e-01 1.000000000000000000e+00 -1.848803609609603882e-01 7.942572981119155884e-02 2.130765169858932495e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/twilight_shifted b/fastplotlib/utils/colormaps/twilight_shifted deleted file mode 100644 index b57b2270b..000000000 --- a/fastplotlib/utils/colormaps/twilight_shifted +++ /dev/null @@ -1,256 +0,0 @@ -1.873922795057296753e-01 7.710209488868713379e-02 2.161887586116790771e-01 1.000000000000000000e+00 -1.897585391998291016e-01 7.501985877752304077e-02 2.193005084991455078e-01 1.000000000000000000e+00 -1.919944882392883301e-01 7.318282872438430786e-02 2.224338501691818237e-01 1.000000000000000000e+00 -1.941035091876983643e-01 7.160830497741699219e-02 2.255872786045074463e-01 1.000000000000000000e+00 -1.960826069116592407e-01 7.032122462987899780e-02 2.287467271089553833e-01 1.000000000000000000e+00 -1.979483366012573242e-01 6.931406259536743164e-02 2.319464683532714844e-01 1.000000000000000000e+00 -1.997157186269760132e-01 6.859271228313446045e-02 2.351709455251693726e-01 1.000000000000000000e+00 -2.015613317489624023e-01 6.798326969146728516e-02 2.385297417640686035e-01 1.000000000000000000e+00 -2.035200744867324829e-01 6.744418293237686157e-02 2.420557588338851929e-01 1.000000000000000000e+00 -2.055805176496505737e-01 6.699046492576599121e-02 2.457249760627746582e-01 1.000000000000000000e+00 -2.077442407608032227e-01 6.661453098058700562e-02 2.495464384555816650e-01 1.000000000000000000e+00 -2.100121378898620605e-01 6.630857288837432861e-02 2.535288929939270020e-01 1.000000000000000000e+00 -2.123741060495376587e-01 6.608502566814422607e-02 2.576516568660736084e-01 1.000000000000000000e+00 -2.148284316062927246e-01 6.594038754701614380e-02 2.619167566299438477e-01 1.000000000000000000e+00 -2.173784524202346802e-01 6.585966050624847412e-02 2.663421034812927246e-01 1.000000000000000000e+00 -2.200125157833099365e-01 6.585791707038879395e-02 2.709028124809265137e-01 1.000000000000000000e+00 -2.227270603179931641e-01 6.593379378318786621e-02 2.755971550941467285e-01 1.000000000000000000e+00 -2.255216389894485474e-01 6.607817113399505615e-02 2.804339826107025146e-01 1.000000000000000000e+00 -2.283917665481567383e-01 6.628997623920440674e-02 2.854107320308685303e-01 1.000000000000000000e+00 -2.313295453786849976e-01 6.657619029283523560e-02 2.905136048793792725e-01 1.000000000000000000e+00 -2.343305498361587524e-01 6.693559885025024414e-02 2.957400977611541748e-01 1.000000000000000000e+00 -2.373906224966049194e-01 6.736588478088378906e-02 3.010892271995544434e-01 1.000000000000000000e+00 -2.405081391334533691e-01 6.785710155963897705e-02 3.065705597400665283e-01 1.000000000000000000e+00 -2.436737269163131714e-01 6.841985881328582764e-02 3.121652305126190186e-01 1.000000000000000000e+00 -2.468822598457336426e-01 6.905364245176315308e-02 3.178699314594268799e-01 1.000000000000000000e+00 -2.501284480094909668e-01 6.975820660591125488e-02 3.236809968948364258e-01 1.000000000000000000e+00 -2.534068524837493896e-01 7.053358107805252075e-02 3.295945823192596436e-01 1.000000000000000000e+00 -2.567119300365447998e-01 7.138010859489440918e-02 3.356064856052398682e-01 1.000000000000000000e+00 -2.600389420986175537e-01 7.229477912187576294e-02 3.417175710201263428e-01 1.000000000000000000e+00 -2.633812129497528076e-01 7.328166067600250244e-02 3.479188978672027588e-01 1.000000000000000000e+00 -2.667323350906372070e-01 7.434401661157608032e-02 3.542027473449707031e-01 1.000000000000000000e+00 -2.700863778591156006e-01 7.548367232084274292e-02 3.605637550354003906e-01 1.000000000000000000e+00 -2.734373807907104492e-01 7.670280337333679199e-02 3.669961690902709961e-01 1.000000000000000000e+00 -2.767793834209442139e-01 7.800394296646118164e-02 3.734938204288482666e-01 1.000000000000000000e+00 -2.801063954830169678e-01 7.938999682664871216e-02 3.800502717494964600e-01 1.000000000000000000e+00 -2.834123969078063965e-01 8.086415380239486694e-02 3.866586983203887939e-01 1.000000000000000000e+00 -2.866915166378021240e-01 8.242987096309661865e-02 3.933118283748626709e-01 1.000000000000000000e+00 -2.899379134178161621e-01 8.409079164266586304e-02 4.000021517276763916e-01 1.000000000000000000e+00 -2.931459248065948486e-01 8.585065603256225586e-02 4.067217707633972168e-01 1.000000000000000000e+00 -2.963100075721740723e-01 8.771324902772903442e-02 4.134625792503356934e-01 1.000000000000000000e+00 -2.994248270988464355e-01 8.968225121498107910e-02 4.202162027359008789e-01 1.000000000000000000e+00 -3.024853765964508057e-01 9.176118671894073486e-02 4.269740283489227295e-01 1.000000000000000000e+00 -3.054867684841156006e-01 9.395276755094528198e-02 4.337284862995147705e-01 1.000000000000000000e+00 -3.084244430065155029e-01 9.625938534736633301e-02 4.404719471931457520e-01 1.000000000000000000e+00 -3.112943470478057861e-01 9.868477284908294678e-02 4.471931457519531250e-01 1.000000000000000000e+00 -3.140927851200103760e-01 1.012307778000831604e-01 4.538833498954772949e-01 1.000000000000000000e+00 -3.168164789676666260e-01 1.038986146450042725e-01 4.605341553688049316e-01 1.000000000000000000e+00 -3.194626271724700928e-01 1.066887974739074707e-01 4.671372771263122559e-01 1.000000000000000000e+00 -3.220288157463073730e-01 1.096011400222778320e-01 4.736849367618560791e-01 1.000000000000000000e+00 -3.245130777359008789e-01 1.126345992088317871e-01 4.801700711250305176e-01 1.000000000000000000e+00 -3.269137144088745117e-01 1.157873496413230896e-01 4.865864515304565430e-01 1.000000000000000000e+00 -3.292300403118133545e-01 1.190576404333114624e-01 4.929259419441223145e-01 1.000000000000000000e+00 -3.314615488052368164e-01 1.224424540996551514e-01 4.991827607154846191e-01 1.000000000000000000e+00 -3.336080610752105713e-01 1.259381771087646484e-01 5.053516626358032227e-01 1.000000000000000000e+00 -3.356697857379913330e-01 1.295407414436340332e-01 5.114280581474304199e-01 1.000000000000000000e+00 -3.376473188400268555e-01 1.332456171512603760e-01 5.174080729484558105e-01 1.000000000000000000e+00 -3.395416736602783203e-01 1.370480209589004517e-01 5.232879519462585449e-01 1.000000000000000000e+00 -3.413541018962860107e-01 1.409427970647811890e-01 5.290650129318237305e-01 1.000000000000000000e+00 -3.430860042572021484e-01 1.449246555566787720e-01 5.347370505332946777e-01 1.000000000000000000e+00 -3.447390198707580566e-01 1.489882022142410278e-01 5.403024554252624512e-01 1.000000000000000000e+00 -3.463150858879089355e-01 1.531280279159545898e-01 5.457599759101867676e-01 1.000000000000000000e+00 -3.478165268898010254e-01 1.573386341333389282e-01 5.511085391044616699e-01 1.000000000000000000e+00 -3.492451310157775879e-01 1.616147756576538086e-01 5.563483834266662598e-01 1.000000000000000000e+00 -3.506030440330505371e-01 1.659512966871261597e-01 5.614796280860900879e-01 1.000000000000000000e+00 -3.518924713134765625e-01 1.703432053327560425e-01 5.665028691291809082e-01 1.000000000000000000e+00 -3.531157374382019043e-01 1.747857034206390381e-01 5.714187026023864746e-01 1.000000000000000000e+00 -3.542753458023071289e-01 1.792741268873214722e-01 5.762280821800231934e-01 1.000000000000000000e+00 -3.553741574287414551e-01 1.838039308786392212e-01 5.809319019317626953e-01 1.000000000000000000e+00 -3.564138114452362061e-01 1.883711963891983032e-01 5.855320692062377930e-01 1.000000000000000000e+00 -3.573966324329376221e-01 1.929720789194107056e-01 5.900301337242126465e-01 1.000000000000000000e+00 -3.583248853683471680e-01 1.976029425859451294e-01 5.944277048110961914e-01 1.000000000000000000e+00 -3.592008948326110840e-01 2.022603750228881836e-01 5.987265110015869141e-01 1.000000000000000000e+00 -3.600268065929412842e-01 2.069412320852279663e-01 6.029284596443176270e-01 1.000000000000000000e+00 -3.608049452304840088e-01 2.116425186395645142e-01 6.070353388786315918e-01 1.000000000000000000e+00 -3.615382909774780273e-01 2.163611203432083130e-01 6.110488176345825195e-01 1.000000000000000000e+00 -3.622280955314636230e-01 2.210948914289474487e-01 6.149711012840270996e-01 1.000000000000000000e+00 -3.628764450550079346e-01 2.258414924144744873e-01 6.188041567802429199e-01 1.000000000000000000e+00 -3.634853661060333252e-01 2.305987626314163208e-01 6.225498914718627930e-01 1.000000000000000000e+00 -3.640569448471069336e-01 2.353647053241729736e-01 6.262101531028747559e-01 1.000000000000000000e+00 -3.645930886268615723e-01 2.401374727487564087e-01 6.297867894172668457e-01 1.000000000000000000e+00 -3.650957942008972168e-01 2.449153661727905273e-01 6.332817077636718750e-01 1.000000000000000000e+00 -3.655669689178466797e-01 2.496968358755111694e-01 6.366967558860778809e-01 1.000000000000000000e+00 -3.660085499286651611e-01 2.544804513454437256e-01 6.400336027145385742e-01 1.000000000000000000e+00 -3.664224445819854736e-01 2.592647969722747803e-01 6.432940959930419922e-01 1.000000000000000000e+00 -3.668108582496643066e-01 2.640485763549804688e-01 6.464799046516418457e-01 1.000000000000000000e+00 -3.671751320362091064e-01 2.688308656215667725e-01 6.495926976203918457e-01 1.000000000000000000e+00 -3.675170838832855225e-01 2.736106216907501221e-01 6.526341438293457031e-01 1.000000000000000000e+00 -3.678384423255920410e-01 2.783869802951812744e-01 6.556056737899780273e-01 1.000000000000000000e+00 -3.681410253047943115e-01 2.831590175628662109e-01 6.585088968276977539e-01 1.000000000000000000e+00 -3.684265613555908203e-01 2.879259586334228516e-01 6.613452434539794922e-01 1.000000000000000000e+00 -3.686968088150024414e-01 2.926870882511138916e-01 6.641162633895874023e-01 1.000000000000000000e+00 -3.689535558223724365e-01 2.974417507648468018e-01 6.668232083320617676e-01 1.000000000000000000e+00 -3.691984713077545166e-01 3.021893203258514404e-01 6.694675683975219727e-01 1.000000000000000000e+00 -3.694333434104919434e-01 3.069292306900024414e-01 6.720505356788635254e-01 1.000000000000000000e+00 -3.696598708629608154e-01 3.116609752178192139e-01 6.745734214782714844e-01 1.000000000000000000e+00 -3.698798120021820068e-01 3.163841068744659424e-01 6.770375370979309082e-01 1.000000000000000000e+00 -3.700948655605316162e-01 3.210981488227844238e-01 6.794440746307373047e-01 1.000000000000000000e+00 -3.703068196773529053e-01 3.258026838302612305e-01 6.817941069602966309e-01 1.000000000000000000e+00 -3.705173730850219727e-01 3.304974138736724854e-01 6.840888857841491699e-01 1.000000000000000000e+00 -3.707283437252044678e-01 3.351819515228271484e-01 6.863294839859008789e-01 1.000000000000000000e+00 -3.709415197372436523e-01 3.398559093475341797e-01 6.885170340538024902e-01 1.000000000000000000e+00 -3.711585700511932373e-01 3.445191085338592529e-01 6.906525492668151855e-01 1.000000000000000000e+00 -3.713812530040740967e-01 3.491712808609008789e-01 6.927370429039001465e-01 1.000000000000000000e+00 -3.716113269329071045e-01 3.538121283054351807e-01 6.947715282440185547e-01 1.000000000000000000e+00 -3.718506097793579102e-01 3.584414720535278320e-01 6.967569589614868164e-01 1.000000000000000000e+00 -3.721008896827697754e-01 3.630591034889221191e-01 6.986943483352661133e-01 1.000000000000000000e+00 -3.723639845848083496e-01 3.676647841930389404e-01 7.005846500396728516e-01 1.000000000000000000e+00 -3.726416528224945068e-01 3.722583353519439697e-01 7.024287581443786621e-01 1.000000000000000000e+00 -3.729357719421386719e-01 3.768396377563476562e-01 7.042275667190551758e-01 1.000000000000000000e+00 -3.732481598854064941e-01 3.814084827899932861e-01 7.059820294380187988e-01 1.000000000000000000e+00 -3.735806345939636230e-01 3.859647512435913086e-01 7.076929807662963867e-01 1.000000000000000000e+00 -3.739349842071533203e-01 3.905082643032073975e-01 7.093613147735595703e-01 1.000000000000000000e+00 -3.743131458759307861e-01 3.950389623641967773e-01 7.109879851341247559e-01 1.000000000000000000e+00 -3.747168481349945068e-01 3.995566368103027344e-01 7.125737071037292480e-01 1.000000000000000000e+00 -3.751480281352996826e-01 4.040612578392028809e-01 7.141193747520446777e-01 1.000000000000000000e+00 -3.756084740161895752e-01 4.085526764392852783e-01 7.156258225440979004e-01 1.000000000000000000e+00 -3.761000037193298340e-01 4.130308032035827637e-01 7.170939445495605469e-01 1.000000000000000000e+00 -3.766244947910308838e-01 4.174955487251281738e-01 7.185245752334594727e-01 1.000000000000000000e+00 -3.771837055683135986e-01 4.219467937946319580e-01 7.199184298515319824e-01 1.000000000000000000e+00 -3.777794837951660156e-01 4.263845086097717285e-01 7.212764024734497070e-01 1.000000000000000000e+00 -3.784136474132537842e-01 4.308086037635803223e-01 7.225993275642395020e-01 1.000000000000000000e+00 -3.790878951549530029e-01 4.352189898490905762e-01 7.238879799842834473e-01 1.000000000000000000e+00 -3.798040449619293213e-01 4.396155774593353271e-01 7.251432538032531738e-01 1.000000000000000000e+00 -3.805637955665588379e-01 4.439983665943145752e-01 7.263658642768859863e-01 1.000000000000000000e+00 -3.813688755035400391e-01 4.483672678470611572e-01 7.275567054748535156e-01 1.000000000000000000e+00 -3.822209537029266357e-01 4.527222514152526855e-01 7.287165522575378418e-01 1.000000000000000000e+00 -3.831216692924499512e-01 4.570632278919219971e-01 7.298462390899658203e-01 1.000000000000000000e+00 -3.840726912021636963e-01 4.613901972770690918e-01 7.309466600418090820e-01 1.000000000000000000e+00 -3.850755691528320312e-01 4.657030701637268066e-01 7.320185303688049316e-01 1.000000000000000000e+00 -3.861318528652191162e-01 4.700018465518951416e-01 7.330628037452697754e-01 1.000000000000000000e+00 -3.872430026531219482e-01 4.742864668369293213e-01 7.340801954269409180e-01 1.000000000000000000e+00 -3.884105384349822998e-01 4.785569012165069580e-01 7.350715994834899902e-01 1.000000000000000000e+00 -3.896358013153076172e-01 4.828131794929504395e-01 7.360378503799438477e-01 1.000000000000000000e+00 -3.909201622009277344e-01 4.870552122592926025e-01 7.369797825813293457e-01 1.000000000000000000e+00 -3.922649621963500977e-01 4.912829995155334473e-01 7.378982305526733398e-01 1.000000000000000000e+00 -3.936713635921478271e-01 4.954965710639953613e-01 7.387940883636474609e-01 1.000000000000000000e+00 -3.951405882835388184e-01 4.996958673000335693e-01 7.396681904792785645e-01 1.000000000000000000e+00 -3.966737389564514160e-01 5.038809180259704590e-01 7.405213713645935059e-01 1.000000000000000000e+00 -3.982719182968139648e-01 5.080516934394836426e-01 7.413545250892639160e-01 1.000000000000000000e+00 -3.999360799789428711e-01 5.122081637382507324e-01 7.421684265136718750e-01 1.000000000000000000e+00 -4.016671478748321533e-01 5.163504481315612793e-01 7.429640293121337891e-01 1.000000000000000000e+00 -4.034660160541534424e-01 5.204784870147705078e-01 7.437421679496765137e-01 1.000000000000000000e+00 -4.053334295749664307e-01 5.245922803878784180e-01 7.445036768913269043e-01 1.000000000000000000e+00 -4.072701930999755859e-01 5.286918878555297852e-01 7.452494502067565918e-01 1.000000000000000000e+00 -4.092769026756286621e-01 5.327773094177246094e-01 7.459803223609924316e-01 1.000000000000000000e+00 -4.113541543483734131e-01 5.368486046791076660e-01 7.466971278190612793e-01 1.000000000000000000e+00 -4.135024547576904297e-01 5.409057736396789551e-01 7.474007606506347656e-01 1.000000000000000000e+00 -4.157223403453826904e-01 5.449488162994384766e-01 7.480920553207397461e-01 1.000000000000000000e+00 -4.180141389369964600e-01 5.489778518676757812e-01 7.487719058990478516e-01 1.000000000000000000e+00 -4.203782379627227783e-01 5.529928803443908691e-01 7.494412660598754883e-01 1.000000000000000000e+00 -4.228148460388183594e-01 5.569939017295837402e-01 7.501008510589599609e-01 1.000000000000000000e+00 -4.253242313861846924e-01 5.609810352325439453e-01 7.507516741752624512e-01 1.000000000000000000e+00 -4.279065132141113281e-01 5.649542808532714844e-01 7.513944506645202637e-01 1.000000000000000000e+00 -4.305617809295654297e-01 5.689137578010559082e-01 7.520300745964050293e-01 1.000000000000000000e+00 -4.332900941371917725e-01 5.728594064712524414e-01 7.526594400405883789e-01 1.000000000000000000e+00 -4.360913932323455811e-01 5.767914056777954102e-01 7.532833814620971680e-01 1.000000000000000000e+00 -4.389656484127044678e-01 5.807096958160400391e-01 7.539027333259582520e-01 1.000000000000000000e+00 -4.419127106666564941e-01 5.846143960952758789e-01 7.545183897018432617e-01 1.000000000000000000e+00 -4.449324607849121094e-01 5.885056257247924805e-01 7.551311254501342773e-01 1.000000000000000000e+00 -4.480247199535369873e-01 5.923833250999450684e-01 7.557417750358581543e-01 1.000000000000000000e+00 -4.511891901493072510e-01 5.962476134300231934e-01 7.563512325286865234e-01 1.000000000000000000e+00 -4.544256329536437988e-01 6.000986099243164062e-01 7.569601535797119141e-01 1.000000000000000000e+00 -4.577337801456451416e-01 6.039363145828247070e-01 7.575693726539611816e-01 1.000000000000000000e+00 -4.611132740974426270e-01 6.077607870101928711e-01 7.581798434257507324e-01 1.000000000000000000e+00 -4.645637571811676025e-01 6.115720868110656738e-01 7.587924003601074219e-01 1.000000000000000000e+00 -4.680849015712738037e-01 6.153702735900878906e-01 7.594078779220581055e-01 1.000000000000000000e+00 -4.716762900352478027e-01 6.191554069519042969e-01 7.600271105766296387e-01 1.000000000000000000e+00 -4.753375351428985596e-01 6.229275465011596680e-01 7.606508731842041016e-01 1.000000000000000000e+00 -4.790681600570678711e-01 6.266867518424987793e-01 7.612800002098083496e-01 1.000000000000000000e+00 -4.828677773475646973e-01 6.304330229759216309e-01 7.619153857231140137e-01 1.000000000000000000e+00 -4.867359697818756104e-01 6.341664791107177734e-01 7.625578045845031738e-01 1.000000000000000000e+00 -4.906722605228424072e-01 6.378870606422424316e-01 7.632081508636474609e-01 1.000000000000000000e+00 -4.946761727333068848e-01 6.415948271751403809e-01 7.638671994209289551e-01 1.000000000000000000e+00 -4.987473487854003906e-01 6.452898979187011719e-01 7.645357847213745117e-01 1.000000000000000000e+00 -5.028853416442871094e-01 6.489721536636352539e-01 7.652144432067871094e-01 1.000000000000000000e+00 -5.070896744728088379e-01 6.526417136192321777e-01 7.659044861793518066e-01 1.000000000000000000e+00 -5.113599300384521484e-01 6.562985181808471680e-01 7.666066288948059082e-01 1.000000000000000000e+00 -5.156955718994140625e-01 6.599426269531250000e-01 7.673219442367553711e-01 1.000000000000000000e+00 -5.200963020324707031e-01 6.635739207267761230e-01 7.680512070655822754e-01 1.000000000000000000e+00 -5.245615243911743164e-01 6.671924591064453125e-01 7.687954306602478027e-01 1.000000000000000000e+00 -5.290908813476562500e-01 6.707981228828430176e-01 7.695555090904235840e-01 1.000000000000000000e+00 -5.336838960647583008e-01 6.743909120559692383e-01 7.703325152397155762e-01 1.000000000000000000e+00 -5.383401513099670410e-01 6.779708266258239746e-01 7.711273431777954102e-01 1.000000000000000000e+00 -5.430593490600585938e-01 6.815376877784729004e-01 7.719407677650451660e-01 1.000000000000000000e+00 -5.478409528732299805e-01 6.850914359092712402e-01 7.727738618850708008e-01 1.000000000000000000e+00 -5.526844859123229980e-01 6.886319518089294434e-01 7.736279368400573730e-01 1.000000000000000000e+00 -5.575894117355346680e-01 6.921591162681579590e-01 7.745041251182556152e-01 1.000000000000000000e+00 -5.625551342964172363e-01 6.956728100776672363e-01 7.754036188125610352e-01 1.000000000000000000e+00 -5.675811767578125000e-01 6.991727948188781738e-01 7.763274908065795898e-01 1.000000000000000000e+00 -5.726668834686279297e-01 7.026589512825012207e-01 7.772770524024963379e-01 1.000000000000000000e+00 -5.778116583824157715e-01 7.061310410499572754e-01 7.782534360885620117e-01 1.000000000000000000e+00 -5.830148458480834961e-01 7.095888853073120117e-01 7.792578339576721191e-01 1.000000000000000000e+00 -5.882757902145385742e-01 7.130321264266967773e-01 7.802914381027221680e-01 1.000000000000000000e+00 -5.935933589935302734e-01 7.164605855941772461e-01 7.813558578491210938e-01 1.000000000000000000e+00 -5.989665985107421875e-01 7.198739647865295410e-01 7.824525833129882812e-01 1.000000000000000000e+00 -6.043943166732788086e-01 7.232718467712402344e-01 7.835829854011535645e-01 1.000000000000000000e+00 -6.098754405975341797e-01 7.266539931297302246e-01 7.847483754158020020e-01 1.000000000000000000e+00 -6.154084801673889160e-01 7.300199270248413086e-01 7.859502434730529785e-01 1.000000000000000000e+00 -6.209919452667236328e-01 7.333693504333496094e-01 7.871899604797363281e-01 1.000000000000000000e+00 -6.266240477561950684e-01 7.367017269134521484e-01 7.884690165519714355e-01 1.000000000000000000e+00 -6.323027014732360840e-01 7.400166988372802734e-01 7.897889018058776855e-01 1.000000000000000000e+00 -6.380258798599243164e-01 7.433137893676757812e-01 7.911509871482849121e-01 1.000000000000000000e+00 -6.437910795211791992e-01 7.465924024581909180e-01 7.925565242767333984e-01 1.000000000000000000e+00 -6.495957374572753906e-01 7.498520016670227051e-01 7.940067648887634277e-01 1.000000000000000000e+00 -6.554369330406188965e-01 7.530921101570129395e-01 7.955027222633361816e-01 1.000000000000000000e+00 -6.613112688064575195e-01 7.563120126724243164e-01 7.970455884933471680e-01 1.000000000000000000e+00 -6.672148108482360840e-01 7.595112919807434082e-01 7.986367344856262207e-01 1.000000000000000000e+00 -6.731442213058471680e-01 7.626891136169433594e-01 8.002762794494628906e-01 1.000000000000000000e+00 -6.790955662727355957e-01 7.658447027206420898e-01 8.019646406173706055e-01 1.000000000000000000e+00 -6.850644350051879883e-01 7.689774036407470703e-01 8.037020564079284668e-01 1.000000000000000000e+00 -6.910464167594909668e-01 7.720863223075866699e-01 8.054884076118469238e-01 1.000000000000000000e+00 -6.970365643501281738e-01 7.751705050468444824e-01 8.073233366012573242e-01 1.000000000000000000e+00 -7.030297517776489258e-01 7.782290577888488770e-01 8.092061877250671387e-01 1.000000000000000000e+00 -7.090207934379577637e-01 7.812609076499938965e-01 8.111359477043151855e-01 1.000000000000000000e+00 -7.150040268898010254e-01 7.842648625373840332e-01 8.131111860275268555e-01 1.000000000000000000e+00 -7.209728360176086426e-01 7.872399687767028809e-01 8.151307106018066406e-01 1.000000000000000000e+00 -7.269217967987060547e-01 7.901846766471862793e-01 8.171921968460083008e-01 1.000000000000000000e+00 -7.328454256057739258e-01 7.930974364280700684e-01 8.192926049232482910e-01 1.000000000000000000e+00 -7.387377023696899414e-01 7.959766387939453125e-01 8.214292526245117188e-01 1.000000000000000000e+00 -7.445924878120422363e-01 7.988204956054687500e-01 8.235986828804016113e-01 1.000000000000000000e+00 -7.504034638404846191e-01 8.016269803047180176e-01 8.257973790168762207e-01 1.000000000000000000e+00 -7.561644315719604492e-01 8.043940663337707520e-01 8.280214071273803711e-01 1.000000000000000000e+00 -7.618690729141235352e-01 8.071194887161254883e-01 8.302664756774902344e-01 1.000000000000000000e+00 -7.675110697746276855e-01 8.098007440567016602e-01 8.325281739234924316e-01 1.000000000000000000e+00 -7.730841636657714844e-01 8.124352693557739258e-01 8.348017334938049316e-01 1.000000000000000000e+00 -7.785789370536804199e-01 8.150209188461303711e-01 8.370834589004516602e-01 1.000000000000000000e+00 -7.839910387992858887e-01 8.175542354583740234e-01 8.393674492835998535e-01 1.000000000000000000e+00 -7.893144488334655762e-01 8.200321197509765625e-01 8.416486382484436035e-01 1.000000000000000000e+00 -7.945430278778076172e-01 8.224511742591857910e-01 8.439218401908874512e-01 1.000000000000000000e+00 -7.996707558631896973e-01 8.248078227043151855e-01 8.461821079254150391e-01 1.000000000000000000e+00 -8.046916723251342773e-01 8.270983695983886719e-01 8.484244942665100098e-01 1.000000000000000000e+00 -8.095999956130981445e-01 8.293189406394958496e-01 8.506444096565246582e-01 1.000000000000000000e+00 -8.143898248672485352e-01 8.314656019210815430e-01 8.528375625610351562e-01 1.000000000000000000e+00 -8.190465569496154785e-01 8.335365056991577148e-01 8.550020456314086914e-01 1.000000000000000000e+00 -8.235743045806884766e-01 8.355248570442199707e-01 8.571318984031677246e-01 1.000000000000000000e+00 -8.279689550399780273e-01 8.374260067939758301e-01 8.592240214347839355e-01 1.000000000000000000e+00 -8.322270512580871582e-01 8.392348885536193848e-01 8.612756133079528809e-01 1.000000000000000000e+00 -8.363403081893920898e-01 8.409479856491088867e-01 8.632853031158447266e-01 1.000000000000000000e+00 -8.403041958808898926e-01 8.425605893135070801e-01 8.652508854866027832e-01 1.000000000000000000e+00 -8.441261649131774902e-01 8.440648317337036133e-01 8.671697378158569336e-01 1.000000000000000000e+00 -8.478071093559265137e-01 8.454546332359313965e-01 8.690403699874877930e-01 1.000000000000000000e+00 -8.513371348381042480e-01 8.467273712158203125e-01 8.708608150482177734e-01 1.000000000000000000e+00 -8.547259569168090820e-01 8.478748798370361328e-01 8.726282715797424316e-01 1.000000000000000000e+00 -8.579825758934020996e-01 8.488893508911132812e-01 8.743404150009155273e-01 1.000000000000000000e+00 -8.611024618148803711e-01 8.497675657272338867e-01 8.759924173355102539e-01 1.000000000000000000e+00 -8.640898466110229492e-01 8.505039215087890625e-01 8.775792717933654785e-01 1.000000000000000000e+00 -8.669601678848266602e-01 8.510895967483520508e-01 8.790976405143737793e-01 1.000000000000000000e+00 -8.697047233581542969e-01 8.515240550041198730e-01 8.805388212203979492e-01 1.000000000000000000e+00 -8.723313212394714355e-01 8.518016338348388672e-01 8.818970322608947754e-01 1.000000000000000000e+00 -8.748534917831420898e-01 8.519152402877807617e-01 8.831692934036254883e-01 1.000000000000000000e+00 -8.772488236427307129e-01 8.518702983856201172e-01 8.843411803245544434e-01 1.000000000000000000e+00 -8.795410394668579102e-01 8.516567349433898926e-01 8.854143619537353516e-01 1.000000000000000000e+00 -8.817223310470581055e-01 8.512759208679199219e-01 8.863805532455444336e-01 1.000000000000000000e+00 -8.837851881980895996e-01 8.507294058799743652e-01 8.872322440147399902e-01 1.000000000000000000e+00 -8.857501745223999023e-01 8.500092625617980957e-01 8.879736661911010742e-01 1.000000000000000000e+00 -8.857115507125854492e-01 8.500218391418457031e-01 8.857253789901733398e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/viridis b/fastplotlib/utils/colormaps/viridis deleted file mode 100644 index 4666f6e38..000000000 --- a/fastplotlib/utils/colormaps/viridis +++ /dev/null @@ -1,256 +0,0 @@ -2.670040130615234375e-01 4.873999860137701035e-03 3.294149935245513916e-01 1.000000000000000000e+00 -2.685100138187408447e-01 9.604999795556068420e-03 3.354269862174987793e-01 1.000000000000000000e+00 -2.699440121650695801e-01 1.462499983608722687e-02 3.413789868354797363e-01 1.000000000000000000e+00 -2.713049948215484619e-01 1.994200050830841064e-02 3.472689986228942871e-01 1.000000000000000000e+00 -2.725940048694610596e-01 2.556299977004528046e-02 3.530929982662200928e-01 1.000000000000000000e+00 -2.738089859485626221e-01 3.149700164794921875e-02 3.588530123233795166e-01 1.000000000000000000e+00 -2.749519944190979004e-01 3.775199875235557556e-02 3.645429909229278564e-01 1.000000000000000000e+00 -2.760219871997833252e-01 4.416700080037117004e-02 3.701640069484710693e-01 1.000000000000000000e+00 -2.770180106163024902e-01 5.034400150179862976e-02 3.757149875164031982e-01 1.000000000000000000e+00 -2.779409885406494141e-01 5.632400140166282654e-02 3.811909854412078857e-01 1.000000000000000000e+00 -2.787910103797912598e-01 6.214499846100807190e-02 3.865920007228851318e-01 1.000000000000000000e+00 -2.795659899711608887e-01 6.783600151538848877e-02 3.919169902801513672e-01 1.000000000000000000e+00 -2.802670001983642578e-01 7.341700047254562378e-02 3.971630036830902100e-01 1.000000000000000000e+00 -2.808940112590789795e-01 7.890699803829193115e-02 4.023289978504180908e-01 1.000000000000000000e+00 -2.814460098743438721e-01 8.432000130414962769e-02 4.074139893054962158e-01 1.000000000000000000e+00 -2.819240093231201172e-01 8.966600149869918823e-02 4.124149978160858154e-01 1.000000000000000000e+00 -2.823269963264465332e-01 9.495499730110168457e-02 4.173310101032257080e-01 1.000000000000000000e+00 -2.826560139656066895e-01 1.001959964632987976e-01 4.221599996089935303e-01 1.000000000000000000e+00 -2.829099893569946289e-01 1.053929999470710754e-01 4.269019961357116699e-01 1.000000000000000000e+00 -2.830910086631774902e-01 1.105529963970184326e-01 4.315539896488189697e-01 1.000000000000000000e+00 -2.831969857215881348e-01 1.156800016760826111e-01 4.361149966716766357e-01 1.000000000000000000e+00 -2.832289934158325195e-01 1.207770034670829773e-01 4.405840039253234863e-01 1.000000000000000000e+00 -2.831870019435882568e-01 1.258479952812194824e-01 4.449599981307983398e-01 1.000000000000000000e+00 -2.830719947814941406e-01 1.308950036764144897e-01 4.492410123348236084e-01 1.000000000000000000e+00 -2.828840017318725586e-01 1.359200030565261841e-01 4.534269869327545166e-01 1.000000000000000000e+00 -2.826229929924011230e-01 1.409260034561157227e-01 4.575169980525970459e-01 1.000000000000000000e+00 -2.822900116443634033e-01 1.459120064973831177e-01 4.615100026130676270e-01 1.000000000000000000e+00 -2.818869948387145996e-01 1.508810073137283325e-01 4.654049873352050781e-01 1.000000000000000000e+00 -2.814120054244995117e-01 1.558340042829513550e-01 4.692009985446929932e-01 1.000000000000000000e+00 -2.808679938316345215e-01 1.607709974050521851e-01 4.728989899158477783e-01 1.000000000000000000e+00 -2.802549898624420166e-01 1.656929999589920044e-01 4.764980077743530273e-01 1.000000000000000000e+00 -2.795740067958831787e-01 1.705989986658096313e-01 4.799970090389251709e-01 1.000000000000000000e+00 -2.788259983062744141e-01 1.754900068044662476e-01 4.833970069885253906e-01 1.000000000000000000e+00 -2.780120074748992920e-01 1.803669929504394531e-01 4.866969883441925049e-01 1.000000000000000000e+00 -2.771340012550354004e-01 1.852280050516128540e-01 4.898979961872100830e-01 1.000000000000000000e+00 -2.761940062046051025e-01 1.900739967823028564e-01 4.930010139942169189e-01 1.000000000000000000e+00 -2.751910090446472168e-01 1.949049979448318481e-01 4.960049986839294434e-01 1.000000000000000000e+00 -2.741279900074005127e-01 1.997209936380386353e-01 4.989109933376312256e-01 1.000000000000000000e+00 -2.730059921741485596e-01 2.045200020074844360e-01 5.017210245132446289e-01 1.000000000000000000e+00 -2.718279957771301270e-01 2.093030065298080444e-01 5.044339895248413086e-01 1.000000000000000000e+00 -2.705950140953063965e-01 2.140689939260482788e-01 5.070520043373107910e-01 1.000000000000000000e+00 -2.693080008029937744e-01 2.188179939985275269e-01 5.095769762992858887e-01 1.000000000000000000e+00 -2.679679989814758301e-01 2.235489934682846069e-01 5.120080113410949707e-01 1.000000000000000000e+00 -2.665799856185913086e-01 2.282620072364807129e-01 5.143489837646484375e-01 1.000000000000000000e+00 -2.651450037956237793e-01 2.329560071229934692e-01 5.165989995002746582e-01 1.000000000000000000e+00 -2.636629939079284668e-01 2.376309931278228760e-01 5.187619924545288086e-01 1.000000000000000000e+00 -2.621380090713500977e-01 2.422859966754913330e-01 5.208370089530944824e-01 1.000000000000000000e+00 -2.605710029602050781e-01 2.469220012426376343e-01 5.228279829025268555e-01 1.000000000000000000e+00 -2.589649856090545654e-01 2.515369951725006104e-01 5.247359871864318848e-01 1.000000000000000000e+00 -2.573220133781433105e-01 2.561300098896026611e-01 5.265629887580871582e-01 1.000000000000000000e+00 -2.556450068950653076e-01 2.607029974460601807e-01 5.283120274543762207e-01 1.000000000000000000e+00 -2.539350092411041260e-01 2.652539908885955811e-01 5.299829840660095215e-01 1.000000000000000000e+00 -2.521939873695373535e-01 2.697829902172088623e-01 5.315790176391601562e-01 1.000000000000000000e+00 -2.504250109195709229e-01 2.742899954319000244e-01 5.331029891967773438e-01 1.000000000000000000e+00 -2.486290037631988525e-01 2.787750065326690674e-01 5.345559716224670410e-01 1.000000000000000000e+00 -2.468110024929046631e-01 2.832370102405548096e-01 5.359410047531127930e-01 1.000000000000000000e+00 -2.449720054864883423e-01 2.876749932765960693e-01 5.372599959373474121e-01 1.000000000000000000e+00 -2.431129962205886841e-01 2.920919954776763916e-01 5.385159850120544434e-01 1.000000000000000000e+00 -2.412369996309280396e-01 2.964850068092346191e-01 5.397089719772338867e-01 1.000000000000000000e+00 -2.393459975719451904e-01 3.008550107479095459e-01 5.408440232276916504e-01 1.000000000000000000e+00 -2.374410033226013184e-01 3.052020072937011719e-01 5.419210195541381836e-01 1.000000000000000000e+00 -2.355259954929351807e-01 3.095270097255706787e-01 5.429440140724182129e-01 1.000000000000000000e+00 -2.336030006408691406e-01 3.138279914855957031e-01 5.439140200614929199e-01 1.000000000000000000e+00 -2.316740006208419800e-01 3.181059956550598145e-01 5.448340177536010742e-01 1.000000000000000000e+00 -2.297389954328536987e-01 3.223609924316406250e-01 5.457059741020202637e-01 1.000000000000000000e+00 -2.278019934892654419e-01 3.265939950942993164e-01 5.465319752693176270e-01 1.000000000000000000e+00 -2.258629947900772095e-01 3.308050036430358887e-01 5.473139882087707520e-01 1.000000000000000000e+00 -2.239249944686889648e-01 3.349939882755279541e-01 5.480530261993408203e-01 1.000000000000000000e+00 -2.219890058040618896e-01 3.391610085964202881e-01 5.487520098686218262e-01 1.000000000000000000e+00 -2.200569957494735718e-01 3.433069884777069092e-01 5.494130253791809082e-01 1.000000000000000000e+00 -2.181300073862075806e-01 3.474319875240325928e-01 5.500379800796508789e-01 1.000000000000000000e+00 -2.162099927663803101e-01 3.515349924564361572e-01 5.506269931793212891e-01 1.000000000000000000e+00 -2.142979949712753296e-01 3.556190133094787598e-01 5.511839985847473145e-01 1.000000000000000000e+00 -2.123949974775314331e-01 3.596830070018768311e-01 5.517100095748901367e-01 1.000000000000000000e+00 -2.105029970407485962e-01 3.637270033359527588e-01 5.522059798240661621e-01 1.000000000000000000e+00 -2.086230069398880005e-01 3.677519857883453369e-01 5.526750087738037109e-01 1.000000000000000000e+00 -2.067559957504272461e-01 3.717580139636993408e-01 5.531169772148132324e-01 1.000000000000000000e+00 -2.049030065536499023e-01 3.757460117340087891e-01 5.535330176353454590e-01 1.000000000000000000e+00 -2.030629962682723999e-01 3.797160089015960693e-01 5.539249777793884277e-01 1.000000000000000000e+00 -2.012390047311782837e-01 3.836700022220611572e-01 5.542939901351928711e-01 1.000000000000000000e+00 -1.994300037622451782e-01 3.876070082187652588e-01 5.546420216560363770e-01 1.000000000000000000e+00 -1.976359933614730835e-01 3.915280103683471680e-01 5.549690127372741699e-01 1.000000000000000000e+00 -1.958599984645843506e-01 3.954330086708068848e-01 5.552759766578674316e-01 1.000000000000000000e+00 -1.941000074148178101e-01 3.993229866027832031e-01 5.555649995803833008e-01 1.000000000000000000e+00 -1.923570036888122559e-01 4.031989872455596924e-01 5.558360219001770020e-01 1.000000000000000000e+00 -1.906310021877288818e-01 4.070610105991363525e-01 5.560889840126037598e-01 1.000000000000000000e+00 -1.889230012893676758e-01 4.109100103378295898e-01 5.563259720802307129e-01 1.000000000000000000e+00 -1.872310042381286621e-01 4.147459864616394043e-01 5.565469861030578613e-01 1.000000000000000000e+00 -1.855559945106506348e-01 4.185700118541717529e-01 5.567529797554016113e-01 1.000000000000000000e+00 -1.838980019092559814e-01 4.223830103874206543e-01 5.569440126419067383e-01 1.000000000000000000e+00 -1.822559982538223267e-01 4.261839985847473145e-01 5.571200251579284668e-01 1.000000000000000000e+00 -1.806290000677108765e-01 4.299750030040740967e-01 5.572819709777832031e-01 1.000000000000000000e+00 -1.790190041065216064e-01 4.337559938430786133e-01 5.574300289154052734e-01 1.000000000000000000e+00 -1.774230003356933594e-01 4.375270009040832520e-01 5.575649738311767578e-01 1.000000000000000000e+00 -1.758410036563873291e-01 4.412899911403656006e-01 5.576850175857543945e-01 1.000000000000000000e+00 -1.742739975452423096e-01 4.450440108776092529e-01 5.577920079231262207e-01 1.000000000000000000e+00 -1.727190017700195312e-01 4.487909972667694092e-01 5.578849911689758301e-01 1.000000000000000000e+00 -1.711760014295578003e-01 4.525299966335296631e-01 5.579649806022644043e-01 1.000000000000000000e+00 -1.696459949016571045e-01 4.562619924545288086e-01 5.580300092697143555e-01 1.000000000000000000e+00 -1.681260019540786743e-01 4.599879980087280273e-01 5.580819845199584961e-01 1.000000000000000000e+00 -1.666170060634613037e-01 4.637080132961273193e-01 5.581189990043640137e-01 1.000000000000000000e+00 -1.651169955730438232e-01 4.674229919910430908e-01 5.581409931182861328e-01 1.000000000000000000e+00 -1.636250019073486328e-01 4.711329936981201172e-01 5.581480264663696289e-01 1.000000000000000000e+00 -1.621419936418533325e-01 4.748379886150360107e-01 5.581399798393249512e-01 1.000000000000000000e+00 -1.606650054454803467e-01 4.785400032997131348e-01 5.581150054931640625e-01 1.000000000000000000e+00 -1.591939926147460938e-01 4.822370111942291260e-01 5.580729842185974121e-01 1.000000000000000000e+00 -1.577289998531341553e-01 4.859319925308227539e-01 5.580130219459533691e-01 1.000000000000000000e+00 -1.562699973583221436e-01 4.896239936351776123e-01 5.579360127449035645e-01 1.000000000000000000e+00 -1.548150032758712769e-01 4.933130145072937012e-01 5.578399896621704102e-01 1.000000000000000000e+00 -1.533640027046203613e-01 4.970000088214874268e-01 5.577239990234375000e-01 1.000000000000000000e+00 -1.519179940223693848e-01 5.006849765777587891e-01 5.575870275497436523e-01 1.000000000000000000e+00 -1.504759937524795532e-01 5.043690204620361328e-01 5.574300289154052734e-01 1.000000000000000000e+00 -1.490390002727508545e-01 5.080509781837463379e-01 5.572500228881835938e-01 1.000000000000000000e+00 -1.476069986820220947e-01 5.117329955101013184e-01 5.570489764213562012e-01 1.000000000000000000e+00 -1.461800038814544678e-01 5.154129862785339355e-01 5.568230152130126953e-01 1.000000000000000000e+00 -1.447589993476867676e-01 5.190929770469665527e-01 5.565720200538635254e-01 1.000000000000000000e+00 -1.433430016040802002e-01 5.227730274200439453e-01 5.562949776649475098e-01 1.000000000000000000e+00 -1.419350057840347290e-01 5.264530181884765625e-01 5.559909939765930176e-01 1.000000000000000000e+00 -1.405359953641891479e-01 5.301319956779479980e-01 5.556589961051940918e-01 1.000000000000000000e+00 -1.391469985246658325e-01 5.338119864463806152e-01 5.552979707717895508e-01 1.000000000000000000e+00 -1.377699971199035645e-01 5.374919772148132324e-01 5.549060106277465820e-01 1.000000000000000000e+00 -1.364080011844635010e-01 5.411729812622070312e-01 5.544829964637756348e-01 1.000000000000000000e+00 -1.350660026073455811e-01 5.448529720306396484e-01 5.540289878845214844e-01 1.000000000000000000e+00 -1.337430030107498169e-01 5.485349893569946289e-01 5.535410046577453613e-01 1.000000000000000000e+00 -1.324439942836761475e-01 5.522159934043884277e-01 5.530179738998413086e-01 1.000000000000000000e+00 -1.311720013618469238e-01 5.558990240097045898e-01 5.524590015411376953e-01 1.000000000000000000e+00 -1.299329996109008789e-01 5.595819950103759766e-01 5.518640279769897461e-01 1.000000000000000000e+00 -1.287290006875991821e-01 5.632650256156921387e-01 5.512290000915527344e-01 1.000000000000000000e+00 -1.275680065155029297e-01 5.669490098953247070e-01 5.505560040473937988e-01 1.000000000000000000e+00 -1.264529973268508911e-01 5.706329941749572754e-01 5.498409867286682129e-01 1.000000000000000000e+00 -1.253940016031265259e-01 5.743179917335510254e-01 5.490859746932983398e-01 1.000000000000000000e+00 -1.243949979543685913e-01 5.780019760131835938e-01 5.482869744300842285e-01 1.000000000000000000e+00 -1.234629973769187927e-01 5.816869735717773438e-01 5.474449992179870605e-01 1.000000000000000000e+00 -1.226060017943382263e-01 5.853710174560546875e-01 5.465570092201232910e-01 1.000000000000000000e+00 -1.218309998512268066e-01 5.890550017356872559e-01 5.456230044364929199e-01 1.000000000000000000e+00 -1.211479976773262024e-01 5.927389860153198242e-01 5.446410179138183594e-01 1.000000000000000000e+00 -1.205649971961975098e-01 5.964220166206359863e-01 5.436109900474548340e-01 1.000000000000000000e+00 -1.200919970870018005e-01 6.001039743423461914e-01 5.425300002098083496e-01 1.000000000000000000e+00 -1.197379976511001587e-01 6.037849783897399902e-01 5.414000153541564941e-01 1.000000000000000000e+00 -1.195119991898536682e-01 6.074640154838562012e-01 5.402179956436157227e-01 1.000000000000000000e+00 -1.194230020046234131e-01 6.111410260200500488e-01 5.389819741249084473e-01 1.000000000000000000e+00 -1.194830015301704407e-01 6.148170232772827148e-01 5.376920104026794434e-01 1.000000000000000000e+00 -1.196990013122558594e-01 6.184899806976318359e-01 5.363469719886779785e-01 1.000000000000000000e+00 -1.200810000300407410e-01 6.221609711647033691e-01 5.349460244178771973e-01 1.000000000000000000e+00 -1.206379979848861694e-01 6.258280277252197266e-01 5.334879755973815918e-01 1.000000000000000000e+00 -1.213800013065338135e-01 6.294919848442077637e-01 5.319730043411254883e-01 1.000000000000000000e+00 -1.223120018839836121e-01 6.331530213356018066e-01 5.303980112075805664e-01 1.000000000000000000e+00 -1.234439983963966370e-01 6.368089914321899414e-01 5.287629961967468262e-01 1.000000000000000000e+00 -1.247799992561340332e-01 6.404610276222229004e-01 5.270680189132690430e-01 1.000000000000000000e+00 -1.263259947299957275e-01 6.441069841384887695e-01 5.253109931945800781e-01 1.000000000000000000e+00 -1.280869990587234497e-01 6.477490067481994629e-01 5.234910249710083008e-01 1.000000000000000000e+00 -1.300670057535171509e-01 6.513839960098266602e-01 5.216079950332641602e-01 1.000000000000000000e+00 -1.322679966688156128e-01 6.550139784812927246e-01 5.196610093116760254e-01 1.000000000000000000e+00 -1.346919983625411987e-01 6.586359739303588867e-01 5.176489949226379395e-01 1.000000000000000000e+00 -1.373389959335327148e-01 6.622520089149475098e-01 5.155709981918334961e-01 1.000000000000000000e+00 -1.402100026607513428e-01 6.658589839935302734e-01 5.134270191192626953e-01 1.000000000000000000e+00 -1.433030068874359131e-01 6.694589853286743164e-01 5.112149715423583984e-01 1.000000000000000000e+00 -1.466159969568252563e-01 6.730499863624572754e-01 5.089359879493713379e-01 1.000000000000000000e+00 -1.501480042934417725e-01 6.766309738159179688e-01 5.065889954566955566e-01 1.000000000000000000e+00 -1.538940072059631348e-01 6.802030205726623535e-01 5.041720271110534668e-01 1.000000000000000000e+00 -1.578509956598281860e-01 6.837649941444396973e-01 5.016859769821166992e-01 1.000000000000000000e+00 -1.620160043239593506e-01 6.873160004615783691e-01 4.991289973258972168e-01 1.000000000000000000e+00 -1.663829982280731201e-01 6.908559799194335938e-01 4.965020120143890381e-01 1.000000000000000000e+00 -1.709479987621307373e-01 6.943839788436889648e-01 4.938029944896697998e-01 1.000000000000000000e+00 -1.757069975137710571e-01 6.978999972343444824e-01 4.910329878330230713e-01 1.000000000000000000e+00 -1.806530058383941650e-01 7.014020085334777832e-01 4.881890118122100830e-01 1.000000000000000000e+00 -1.857829988002777100e-01 7.048910260200500488e-01 4.852730035781860352e-01 1.000000000000000000e+00 -1.910900026559829712e-01 7.083659768104553223e-01 4.822840094566345215e-01 1.000000000000000000e+00 -1.965710073709487915e-01 7.118269801139831543e-01 4.792209863662719727e-01 1.000000000000000000e+00 -2.022189944982528687e-01 7.152720093727111816e-01 4.760839939117431641e-01 1.000000000000000000e+00 -2.080300003290176392e-01 7.187010049819946289e-01 4.728730022907257080e-01 1.000000000000000000e+00 -2.140000015497207642e-01 7.221140265464782715e-01 4.695880115032196045e-01 1.000000000000000000e+00 -2.201240062713623047e-01 7.255089879035949707e-01 4.662260115146636963e-01 1.000000000000000000e+00 -2.263969928026199341e-01 7.288879752159118652e-01 4.627889990806579590e-01 1.000000000000000000e+00 -2.328149974346160889e-01 7.322469949722290039e-01 4.592770040035247803e-01 1.000000000000000000e+00 -2.393739968538284302e-01 7.355880141258239746e-01 4.556879997253417969e-01 1.000000000000000000e+00 -2.460699975490570068e-01 7.389100193977355957e-01 4.520240128040313721e-01 1.000000000000000000e+00 -2.528989911079406738e-01 7.422109842300415039e-01 4.482840001583099365e-01 1.000000000000000000e+00 -2.598569989204406738e-01 7.454919815063476562e-01 4.444670081138610840e-01 1.000000000000000000e+00 -2.669410109519958496e-01 7.487509846687316895e-01 4.405730068683624268e-01 1.000000000000000000e+00 -2.741490006446838379e-01 7.519879937171936035e-01 4.366010129451751709e-01 1.000000000000000000e+00 -2.814770042896270752e-01 7.552030086517333984e-01 4.325520098209381104e-01 1.000000000000000000e+00 -2.889209985733032227e-01 7.583940029144287109e-01 4.284259974956512451e-01 1.000000000000000000e+00 -2.964789867401123047e-01 7.615609765052795410e-01 4.242230057716369629e-01 1.000000000000000000e+00 -3.041479885578155518e-01 7.647039890289306641e-01 4.199430048465728760e-01 1.000000000000000000e+00 -3.119249939918518066e-01 7.678220272064208984e-01 4.155859947204589844e-01 1.000000000000000000e+00 -3.198089897632598877e-01 7.709140181541442871e-01 4.111520051956176758e-01 1.000000000000000000e+00 -3.277960121631622314e-01 7.739800214767456055e-01 4.066399931907653809e-01 1.000000000000000000e+00 -3.358849883079528809e-01 7.770180106163024902e-01 4.020490050315856934e-01 1.000000000000000000e+00 -3.440740108489990234e-01 7.800289988517761230e-01 3.973810076713562012e-01 1.000000000000000000e+00 -3.523600101470947266e-01 7.830110192298889160e-01 3.926360011100769043e-01 1.000000000000000000e+00 -3.607409894466400146e-01 7.859640121459960938e-01 3.878139853477478027e-01 1.000000000000000000e+00 -3.692139983177185059e-01 7.888879776000976562e-01 3.829140067100524902e-01 1.000000000000000000e+00 -3.777790069580078125e-01 7.917810082435607910e-01 3.779389858245849609e-01 1.000000000000000000e+00 -3.864330053329467773e-01 7.946439981460571289e-01 3.728860020637512207e-01 1.000000000000000000e+00 -3.951739966869354248e-01 7.974749803543090820e-01 3.677569925785064697e-01 1.000000000000000000e+00 -4.040009975433349609e-01 8.002750277519226074e-01 3.625519871711730957e-01 1.000000000000000000e+00 -4.129129946231842041e-01 8.030409812927246094e-01 3.572689890861511230e-01 1.000000000000000000e+00 -4.219079911708831787e-01 8.057739734649658203e-01 3.519099950790405273e-01 1.000000000000000000e+00 -4.309830069541931152e-01 8.084729909896850586e-01 3.464759886264801025e-01 1.000000000000000000e+00 -4.401369988918304443e-01 8.111379742622375488e-01 3.409669995307922363e-01 1.000000000000000000e+00 -4.493680000305175781e-01 8.137680292129516602e-01 3.353840112686157227e-01 1.000000000000000000e+00 -4.586740136146545410e-01 8.163629770278930664e-01 3.297269940376281738e-01 1.000000000000000000e+00 -4.680530130863189697e-01 8.189210295677185059e-01 3.239980041980743408e-01 1.000000000000000000e+00 -4.775039851665496826e-01 8.214439749717712402e-01 3.181949853897094727e-01 1.000000000000000000e+00 -4.870260059833526611e-01 8.239290118217468262e-01 3.123210072517395020e-01 1.000000000000000000e+00 -4.966149926185607910e-01 8.263760209083557129e-01 3.063769936561584473e-01 1.000000000000000000e+00 -5.062710046768188477e-01 8.287860155105590820e-01 3.003619909286499023e-01 1.000000000000000000e+00 -5.159919857978820801e-01 8.311579823493957520e-01 2.942790091037750244e-01 1.000000000000000000e+00 -5.257760286331176758e-01 8.334910273551940918e-01 2.881270051002502441e-01 1.000000000000000000e+00 -5.356209874153137207e-01 8.357849717140197754e-01 2.819080054759979248e-01 1.000000000000000000e+00 -5.455240011215209961e-01 8.380389809608459473e-01 2.756260037422180176e-01 1.000000000000000000e+00 -5.554839968681335449e-01 8.402540087699890137e-01 2.692809998989105225e-01 1.000000000000000000e+00 -5.654979944229125977e-01 8.424299955368041992e-01 2.628769874572753906e-01 1.000000000000000000e+00 -5.755630135536193848e-01 8.445659875869750977e-01 2.564150094985961914e-01 1.000000000000000000e+00 -5.856779813766479492e-01 8.466609716415405273e-01 2.498970031738281250e-01 1.000000000000000000e+00 -5.958390235900878906e-01 8.487169742584228516e-01 2.433290034532546997e-01 1.000000000000000000e+00 -6.060450077056884766e-01 8.507329821586608887e-01 2.367119938135147095e-01 1.000000000000000000e+00 -6.162930130958557129e-01 8.527089953422546387e-01 2.300519943237304688e-01 1.000000000000000000e+00 -6.265789866447448730e-01 8.546450138092041016e-01 2.233529984951019287e-01 1.000000000000000000e+00 -6.369019746780395508e-01 8.565419912338256836e-01 2.166199982166290283e-01 1.000000000000000000e+00 -6.472569704055786133e-01 8.583999872207641602e-01 2.098609954118728638e-01 1.000000000000000000e+00 -6.576420068740844727e-01 8.602190017700195312e-01 2.030819952487945557e-01 1.000000000000000000e+00 -6.680539846420288086e-01 8.619989752769470215e-01 1.962929964065551758e-01 1.000000000000000000e+00 -6.784890294075012207e-01 8.637419939041137695e-01 1.895029991865158081e-01 1.000000000000000000e+00 -6.889439821243286133e-01 8.654479980468750000e-01 1.827249974012374878e-01 1.000000000000000000e+00 -6.994150280952453613e-01 8.671169877052307129e-01 1.759710013866424561e-01 1.000000000000000000e+00 -7.098979949951171875e-01 8.687509894371032715e-01 1.692570000886917114e-01 1.000000000000000000e+00 -7.203909754753112793e-01 8.703500032424926758e-01 1.626030057668685913e-01 1.000000000000000000e+00 -7.308890223503112793e-01 8.719159960746765137e-01 1.560290008783340454e-01 1.000000000000000000e+00 -7.413880228996276855e-01 8.734490275382995605e-01 1.495610028505325317e-01 1.000000000000000000e+00 -7.518839836120605469e-01 8.749510049819946289e-01 1.432279944419860840e-01 1.000000000000000000e+00 -7.623729705810546875e-01 8.764240145683288574e-01 1.370639950037002563e-01 1.000000000000000000e+00 -7.728520035743713379e-01 8.778679966926574707e-01 1.311089992523193359e-01 1.000000000000000000e+00 -7.833150029182434082e-01 8.792849779129028320e-01 1.254049986600875854e-01 1.000000000000000000e+00 -7.937600016593933105e-01 8.806779980659484863e-01 1.200049966573715210e-01 1.000000000000000000e+00 -8.041819930076599121e-01 8.820459842681884766e-01 1.149649992585182190e-01 1.000000000000000000e+00 -8.145760297775268555e-01 8.833929896354675293e-01 1.103470027446746826e-01 1.000000000000000000e+00 -8.249400258064270020e-01 8.847200274467468262e-01 1.062169969081878662e-01 1.000000000000000000e+00 -8.352699875831604004e-01 8.860290050506591797e-01 1.026460006833076477e-01 1.000000000000000000e+00 -8.455610275268554688e-01 8.873220086097717285e-01 9.970200061798095703e-02 1.000000000000000000e+00 -8.558099865913391113e-01 8.886010050773620605e-01 9.745199978351593018e-02 1.000000000000000000e+00 -8.660129904747009277e-01 8.898680210113525391e-01 9.595300257205963135e-02 1.000000000000000000e+00 -8.761680126190185547e-01 8.911250233650207520e-01 9.525000303983688354e-02 1.000000000000000000e+00 -8.862709999084472656e-01 8.923739790916442871e-01 9.537400305271148682e-02 1.000000000000000000e+00 -8.963199853897094727e-01 8.936160206794738770e-01 9.633500128984451294e-02 1.000000000000000000e+00 -9.063109755516052246e-01 8.948550224304199219e-01 9.812500327825546265e-02 1.000000000000000000e+00 -9.162420034408569336e-01 8.960909843444824219e-01 1.007170006632804871e-01 1.000000000000000000e+00 -9.261059761047363281e-01 8.973299860954284668e-01 1.040709987282752991e-01 1.000000000000000000e+00 -9.359040260314941406e-01 8.985700011253356934e-01 1.081309989094734192e-01 1.000000000000000000e+00 -9.456359744071960449e-01 8.998150229454040527e-01 1.128380000591278076e-01 1.000000000000000000e+00 -9.552999734878540039e-01 9.010649919509887695e-01 1.181280016899108887e-01 1.000000000000000000e+00 -9.648939967155456543e-01 9.023230075836181641e-01 1.239409968256950378e-01 1.000000000000000000e+00 -9.744169712066650391e-01 9.035900235176086426e-01 1.302150040864944458e-01 1.000000000000000000e+00 -9.838680028915405273e-01 9.048669934272766113e-01 1.368969976902008057e-01 1.000000000000000000e+00 -9.932479858398437500e-01 9.061570167541503906e-01 1.439359933137893677e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/colormaps/winter b/fastplotlib/utils/colormaps/winter deleted file mode 100644 index d97346bbe..000000000 --- a/fastplotlib/utils/colormaps/winter +++ /dev/null @@ -1,256 +0,0 @@ -0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000000e+00 -0.000000000000000000e+00 3.921568859368562698e-03 9.980391860008239746e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.843137718737125397e-03 9.960784316062927246e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.176470611244440079e-02 9.941176176071166992e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.568627543747425079e-02 9.921568632125854492e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.960784383118152618e-02 9.901960492134094238e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.352941222488880157e-02 9.882352948188781738e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.745098061859607697e-02 9.862744808197021484e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.137255087494850159e-02 9.843137264251708984e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.529411926865577698e-02 9.823529124259948730e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.921568766236305237e-02 9.803921580314636230e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.313725605607032776e-02 9.784313440322875977e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.705882444977760315e-02 9.764705896377563477e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.098039284348487854e-02 9.745097756385803223e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.490196123719215393e-02 9.725490212440490723e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.882352963089942932e-02 9.705882072448730469e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.274510174989700317e-02 9.686274528503417969e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.666667014360427856e-02 9.666666388511657715e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.058823853731155396e-02 9.647058844566345215e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.450980693101882935e-02 9.627450704574584961e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.843137532472610474e-02 9.607843160629272461e-01 1.000000000000000000e+00 -0.000000000000000000e+00 8.235294371843338013e-02 9.588235020637512207e-01 1.000000000000000000e+00 -0.000000000000000000e+00 8.627451211214065552e-02 9.568627476692199707e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.019608050584793091e-02 9.549019336700439453e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.411764889955520630e-02 9.529411792755126953e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.803921729326248169e-02 9.509803652763366699e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.019607856869697571e-01 9.490196108818054199e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.058823540806770325e-01 9.470587968826293945e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.098039224743843079e-01 9.450980424880981445e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.137254908680915833e-01 9.431372284889221191e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.176470592617988586e-01 9.411764740943908691e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.215686276555061340e-01 9.392156600952148438e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.254902034997940063e-01 9.372549057006835938e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.294117718935012817e-01 9.352940917015075684e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.333333402872085571e-01 9.333333373069763184e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.372549086809158325e-01 9.313725233078002930e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.411764770746231079e-01 9.294117689132690430e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.450980454683303833e-01 9.274509549140930176e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.490196138620376587e-01 9.254902005195617676e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.529411822557449341e-01 9.235293865203857422e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.568627506494522095e-01 9.215686321258544922e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.607843190431594849e-01 9.196078181266784668e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.647058874368667603e-01 9.176470637321472168e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.686274558305740356e-01 9.156862497329711914e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.725490242242813110e-01 9.137254953384399414e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.764705926179885864e-01 9.117646813392639160e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.803921610116958618e-01 9.098039269447326660e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.843137294054031372e-01 9.078431129455566406e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.882352977991104126e-01 9.058823585510253906e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.921568661928176880e-01 9.039215445518493652e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.960784345865249634e-01 9.019607901573181152e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.000000029802322388e-01 8.999999761581420898e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.039215713739395142e-01 8.980392217636108398e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.078431397676467896e-01 8.960784077644348145e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.117647081613540649e-01 8.941176533699035645e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.156862765550613403e-01 8.921568393707275391e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.196078449487686157e-01 8.901960849761962891e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.235294133424758911e-01 8.882352709770202637e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.274509817361831665e-01 8.862745165824890137e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.313725501298904419e-01 8.843137025833129883e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.352941185235977173e-01 8.823529481887817383e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.392156869173049927e-01 8.803921341896057129e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.431372553110122681e-01 8.784313797950744629e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.470588237047195435e-01 8.764705657958984375e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.509804069995880127e-01 8.745098114013671875e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.549019753932952881e-01 8.725489974021911621e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.588235437870025635e-01 8.705882430076599121e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.627451121807098389e-01 8.686274290084838867e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.666666805744171143e-01 8.666666746139526367e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.705882489681243896e-01 8.647058606147766113e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.745098173618316650e-01 8.627451062202453613e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.784313857555389404e-01 8.607842922210693359e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.823529541492462158e-01 8.588235378265380859e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.862745225429534912e-01 8.568627238273620605e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.901960909366607666e-01 8.549019694328308105e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.941176593303680420e-01 8.529411554336547852e-01 1.000000000000000000e+00 -0.000000000000000000e+00 2.980392277240753174e-01 8.509804010391235352e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.019607961177825928e-01 8.490195870399475098e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.058823645114898682e-01 8.470588326454162598e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.098039329051971436e-01 8.450980186462402344e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.137255012989044189e-01 8.431372642517089844e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.176470696926116943e-01 8.411764502525329590e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.215686380863189697e-01 8.392156958580017090e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.254902064800262451e-01 8.372548818588256836e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.294117748737335205e-01 8.352941274642944336e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.333333432674407959e-01 8.333333134651184082e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.372549116611480713e-01 8.313725590705871582e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.411764800548553467e-01 8.294117450714111328e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.450980484485626221e-01 8.274509906768798828e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.490196168422698975e-01 8.254901766777038574e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.529411852359771729e-01 8.235294222831726074e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.568627536296844482e-01 8.215686082839965820e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.607843220233917236e-01 8.196078538894653320e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.647058904170989990e-01 8.176470398902893066e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.686274588108062744e-01 8.156862854957580566e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.725490272045135498e-01 8.137254714965820312e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.764705955982208252e-01 8.117647171020507812e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.803921639919281006e-01 8.098039031028747559e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.843137323856353760e-01 8.078431487083435059e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.882353007793426514e-01 8.058823347091674805e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.921568691730499268e-01 8.039215803146362305e-01 1.000000000000000000e+00 -0.000000000000000000e+00 3.960784375667572021e-01 8.019607663154602051e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.000000059604644775e-01 8.000000119209289551e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.039215743541717529e-01 7.980391979217529297e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.078431427478790283e-01 7.960784435272216797e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.117647111415863037e-01 7.941176295280456543e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.156862795352935791e-01 7.921568751335144043e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.196078479290008545e-01 7.901960611343383789e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.235294163227081299e-01 7.882353067398071289e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.274509847164154053e-01 7.862744927406311035e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.313725531101226807e-01 7.843137383460998535e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.352941215038299561e-01 7.823529243469238281e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.392156898975372314e-01 7.803921699523925781e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.431372582912445068e-01 7.784313559532165527e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.470588266849517822e-01 7.764706015586853027e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.509803950786590576e-01 7.745097875595092773e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.549019634723663330e-01 7.725490331649780273e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.588235318660736084e-01 7.705882191658020020e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.627451002597808838e-01 7.686274647712707520e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.666666686534881592e-01 7.666666507720947266e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.705882370471954346e-01 7.647058963775634766e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.745098054409027100e-01 7.627450823783874512e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.784313738346099854e-01 7.607843279838562012e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.823529422283172607e-01 7.588235139846801758e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.862745106220245361e-01 7.568627595901489258e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.901960790157318115e-01 7.549019455909729004e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.941176474094390869e-01 7.529411911964416504e-01 1.000000000000000000e+00 -0.000000000000000000e+00 4.980392158031463623e-01 7.509803771972656250e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.019608139991760254e-01 7.490196228027343750e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.058823823928833008e-01 7.470588088035583496e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.098039507865905762e-01 7.450980544090270996e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.137255191802978516e-01 7.431372404098510742e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.176470875740051270e-01 7.411764860153198242e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.215686559677124023e-01 7.392156720161437988e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.254902243614196777e-01 7.372549176216125488e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.294117927551269531e-01 7.352941036224365234e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.333333611488342285e-01 7.333333492279052734e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.372549295425415039e-01 7.313725352287292480e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.411764979362487793e-01 7.294117808341979980e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.450980663299560547e-01 7.274509668350219727e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.490196347236633301e-01 7.254902124404907227e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.529412031173706055e-01 7.235293984413146973e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.568627715110778809e-01 7.215686440467834473e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.607843399047851562e-01 7.196078300476074219e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.647059082984924316e-01 7.176470756530761719e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.686274766921997070e-01 7.156862616539001465e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.725490450859069824e-01 7.137255072593688965e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.764706134796142578e-01 7.117646932601928711e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.803921818733215332e-01 7.098039388656616211e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.843137502670288086e-01 7.078431248664855957e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.882353186607360840e-01 7.058823704719543457e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.921568870544433594e-01 7.039215564727783203e-01 1.000000000000000000e+00 -0.000000000000000000e+00 5.960784554481506348e-01 7.019608020782470703e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.000000238418579102e-01 6.999999880790710449e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.039215922355651855e-01 6.980392336845397949e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.078431606292724609e-01 6.960784196853637695e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.117647290229797363e-01 6.941176652908325195e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.156862974166870117e-01 6.921568512916564941e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.196078658103942871e-01 6.901960968971252441e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.235294342041015625e-01 6.882352828979492188e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.274510025978088379e-01 6.862745285034179688e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.313725709915161133e-01 6.843137145042419434e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.352941393852233887e-01 6.823529601097106934e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.392157077789306641e-01 6.803921461105346680e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.431372761726379395e-01 6.784313917160034180e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.470588445663452148e-01 6.764705777168273926e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.509804129600524902e-01 6.745098233222961426e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.549019813537597656e-01 6.725490093231201172e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.588235497474670410e-01 6.705882549285888672e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.627451181411743164e-01 6.686274409294128418e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.666666865348815918e-01 6.666666865348815918e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.705882549285888672e-01 6.647058725357055664e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.745098233222961426e-01 6.627451181411743164e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.784313917160034180e-01 6.607843041419982910e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.823529601097106934e-01 6.588235497474670410e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.862745285034179688e-01 6.568627357482910156e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.901960968971252441e-01 6.549019813537597656e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.941176652908325195e-01 6.529411673545837402e-01 1.000000000000000000e+00 -0.000000000000000000e+00 6.980392336845397949e-01 6.509804129600524902e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.019608020782470703e-01 6.490195989608764648e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.058823704719543457e-01 6.470588445663452148e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.098039388656616211e-01 6.450980305671691895e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.137255072593688965e-01 6.431372761726379395e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.176470756530761719e-01 6.411764621734619141e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.215686440467834473e-01 6.392157077789306641e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.254902124404907227e-01 6.372548937797546387e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.294117808341979980e-01 6.352941393852233887e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.333333492279052734e-01 6.333333253860473633e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.372549176216125488e-01 6.313725709915161133e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.411764860153198242e-01 6.294117569923400879e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.450980544090270996e-01 6.274510025978088379e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.490196228027343750e-01 6.254901885986328125e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.529411911964416504e-01 6.235294342041015625e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.568627595901489258e-01 6.215686202049255371e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.607843279838562012e-01 6.196078658103942871e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.647058963775634766e-01 6.176470518112182617e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.686274647712707520e-01 6.156862974166870117e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.725490331649780273e-01 6.137254834175109863e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.764706015586853027e-01 6.117647290229797363e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.803921699523925781e-01 6.098039150238037109e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.843137383460998535e-01 6.078431606292724609e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.882353067398071289e-01 6.058823466300964355e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.921568751335144043e-01 6.039215922355651855e-01 1.000000000000000000e+00 -0.000000000000000000e+00 7.960784435272216797e-01 6.019607782363891602e-01 1.000000000000000000e+00 -0.000000000000000000e+00 8.000000119209289551e-01 6.000000238418579102e-01 1.000000000000000000e+00 -0.000000000000000000e+00 8.039215803146362305e-01 5.980392098426818848e-01 1.000000000000000000e+00 -0.000000000000000000e+00 8.078431487083435059e-01 5.960784554481506348e-01 1.000000000000000000e+00 -0.000000000000000000e+00 8.117647171020507812e-01 5.941176414489746094e-01 1.000000000000000000e+00 -0.000000000000000000e+00 8.156862854957580566e-01 5.921568870544433594e-01 1.000000000000000000e+00 -0.000000000000000000e+00 8.196078538894653320e-01 5.901960730552673340e-01 1.000000000000000000e+00 -0.000000000000000000e+00 8.235294222831726074e-01 5.882353186607360840e-01 1.000000000000000000e+00 -0.000000000000000000e+00 8.274509906768798828e-01 5.862745046615600586e-01 1.000000000000000000e+00 -0.000000000000000000e+00 8.313725590705871582e-01 5.843137502670288086e-01 1.000000000000000000e+00 -0.000000000000000000e+00 8.352941274642944336e-01 5.823529362678527832e-01 1.000000000000000000e+00 -0.000000000000000000e+00 8.392156958580017090e-01 5.803921818733215332e-01 1.000000000000000000e+00 -0.000000000000000000e+00 8.431372642517089844e-01 5.784313678741455078e-01 1.000000000000000000e+00 -0.000000000000000000e+00 8.470588326454162598e-01 5.764706134796142578e-01 1.000000000000000000e+00 -0.000000000000000000e+00 8.509804010391235352e-01 5.745097994804382324e-01 1.000000000000000000e+00 -0.000000000000000000e+00 8.549019694328308105e-01 5.725490450859069824e-01 1.000000000000000000e+00 -0.000000000000000000e+00 8.588235378265380859e-01 5.705882310867309570e-01 1.000000000000000000e+00 -0.000000000000000000e+00 8.627451062202453613e-01 5.686274766921997070e-01 1.000000000000000000e+00 -0.000000000000000000e+00 8.666666746139526367e-01 5.666666626930236816e-01 1.000000000000000000e+00 -0.000000000000000000e+00 8.705882430076599121e-01 5.647059082984924316e-01 1.000000000000000000e+00 -0.000000000000000000e+00 8.745098114013671875e-01 5.627450942993164062e-01 1.000000000000000000e+00 -0.000000000000000000e+00 8.784313797950744629e-01 5.607843399047851562e-01 1.000000000000000000e+00 -0.000000000000000000e+00 8.823529481887817383e-01 5.588235259056091309e-01 1.000000000000000000e+00 -0.000000000000000000e+00 8.862745165824890137e-01 5.568627715110778809e-01 1.000000000000000000e+00 -0.000000000000000000e+00 8.901960849761962891e-01 5.549019575119018555e-01 1.000000000000000000e+00 -0.000000000000000000e+00 8.941176533699035645e-01 5.529412031173706055e-01 1.000000000000000000e+00 -0.000000000000000000e+00 8.980392217636108398e-01 5.509803891181945801e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.019607901573181152e-01 5.490196347236633301e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.058823585510253906e-01 5.470588207244873047e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.098039269447326660e-01 5.450980663299560547e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.137254953384399414e-01 5.431372523307800293e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.176470637321472168e-01 5.411764979362487793e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.215686321258544922e-01 5.392156839370727539e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.254902005195617676e-01 5.372549295425415039e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.294117689132690430e-01 5.352941155433654785e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.333333373069763184e-01 5.333333611488342285e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.372549057006835938e-01 5.313725471496582031e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.411764740943908691e-01 5.294117927551269531e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.450980424880981445e-01 5.274509787559509277e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.490196108818054199e-01 5.254902243614196777e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.529411792755126953e-01 5.235294103622436523e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.568627476692199707e-01 5.215686559677124023e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.607843160629272461e-01 5.196078419685363770e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.647058844566345215e-01 5.176470875740051270e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.686274528503417969e-01 5.156862735748291016e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.725490212440490723e-01 5.137255191802978516e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.764705896377563477e-01 5.117647051811218262e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.803921580314636230e-01 5.098039507865905762e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.843137264251708984e-01 5.078431367874145508e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.882352948188781738e-01 5.058823823928833008e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.921568632125854492e-01 5.039215683937072754e-01 1.000000000000000000e+00 -0.000000000000000000e+00 9.960784316062927246e-01 5.019608139991760254e-01 1.000000000000000000e+00 -0.000000000000000000e+00 1.000000000000000000e+00 5.000000000000000000e-01 1.000000000000000000e+00 diff --git a/fastplotlib/utils/functions.py b/fastplotlib/utils/functions.py index 73752ba5e..64f9a94c3 100644 --- a/fastplotlib/utils/functions.py +++ b/fastplotlib/utils/functions.py @@ -1,30 +1,13 @@ from collections import OrderedDict -from pathlib import Path +from typing import * import numpy as np +from cmap import Colormap from pygfx import Texture, Color -# some funcs adapted from mesmerize - -QUALITATIVE_CMAPS = [ - "Pastel1", - "Pastel2", - "Paired", - "Accent", - "Dark2", - "Set1", - "Set2", - "Set3", - "tab10", - "tab20", - "tab20b", - "tab20c", -] - - -def get_cmap(name: str, alpha: float = 1.0) -> np.ndarray: +def get_cmap(name: str, alpha: float = 1.0, gamma: float = 1.0) -> np.ndarray: """ Get a colormap as numpy array @@ -34,6 +17,8 @@ def get_cmap(name: str, alpha: float = 1.0) -> np.ndarray: name of colormap alpha: float alpha, 0.0 - 1.0 + gamma: float + gamma, 0.0 - 1.0 Returns ------- @@ -41,24 +26,8 @@ def get_cmap(name: str, alpha: float = 1.0) -> np.ndarray: [n_colors, 4], i.e. [n_colors, RGBA] """ - - cmap_path = Path(__file__).absolute().parent.joinpath("colormaps", name) - if cmap_path.is_file(): - cmap = np.loadtxt(cmap_path) - - else: - try: - from .generate_colormaps import make_cmap - - cmap = make_cmap(name, alpha) - except (ImportError, ModuleNotFoundError): - raise ModuleNotFoundError( - "Couldn't find colormap files, matplotlib is required to generate them " - "if they aren't found. Please install `matplotlib`" - ) - + cmap = Colormap(name).lut(256, gamma=gamma) cmap[:, -1] = alpha - return cmap.astype(np.float32) @@ -84,34 +53,35 @@ def make_colors(n_colors: int, cmap: str, alpha: float = 1.0) -> np.ndarray: shape is [n_colors, 4], where the last dimension is RGBA """ - name = cmap - cmap = get_cmap(name, alpha) - - if name in QUALITATIVE_CMAPS: - max_colors = cmap.shape[0] - if n_colors > cmap.shape[0]: + cm = Colormap(cmap) + + # can also use cm.category == "qualitative", but checking for non-interpolated + # colormaps is a bit more general. (and not all "custom" colormaps will be + # assigned a category) + if cm.interpolation == "nearest": + max_colors = len(cm.color_stops) + if n_colors > max_colors: raise ValueError( f"You have requested <{n_colors}> colors but only <{max_colors}> exist for the " f"chosen cmap: <{name}>" ) - return cmap[:n_colors] + return np.asarray(cm.color_stops, dtype=np.float32)[:n_colors, 1:] cm_ixs = np.linspace(0, 255, n_colors, dtype=int) - return np.take(cmap, cm_ixs, axis=0).astype(np.float32) + return cm(cm_ixs).astype(np.float32) def get_cmap_texture(name: str, alpha: float = 1.0) -> Texture: - cmap = get_cmap(name) - return Texture(cmap, dim=1) + return Colormap(name).to_pygfx() -def make_colors_dict(labels: iter, cmap: str, **kwargs) -> OrderedDict: +def make_colors_dict(labels: Sequence, cmap: str, **kwargs) -> OrderedDict: """ Get a dict for mapping labels onto colors. Parameters ---------- - labels: Iterable[Any] + labels: Sequence[Any] labels for creating a colormap. Order is maintained if it is a list of unique elements. cmap: str @@ -276,8 +246,10 @@ def parse_cmap_values( n_colors = colormap.shape[0] - 1 - if cmap_name in QUALITATIVE_CMAPS: - # check that cmap_transform are and within the number of colors `n_colors` + # can also use cm.category == "qualitative" + if Colormap(cmap_name).interpolation == "nearest": + # check that cmap_values are and within the number of colors `n_colors` + # do not scale, use directly if not np.issubdtype(transform.dtype, np.integer): raise TypeError( diff --git a/fastplotlib/utils/generate_colormaps.py b/fastplotlib/utils/generate_colormaps.py deleted file mode 100644 index e56a9f226..000000000 --- a/fastplotlib/utils/generate_colormaps.py +++ /dev/null @@ -1,126 +0,0 @@ -import numpy as np -from matplotlib import cm - - -class ColormapNames: - perceptually_uniform = ["viridis", "plasma", "inferno", "magma", "cividis"] - sequential = [ - "Greys", - "Purples", - "Blues", - "Greens", - "Oranges", - "Reds", - "YlOrBr", - "YlOrRd", - "OrRd", - "PuRd", - "RdPu", - "BuPu", - "GnBu", - "PuBu", - "YlGnBu", - "PuBuGn", - "BuGn", - "YlGn", - ] - - sequential2 = [ - "binary", - "gist_yarg", - "gist_gray", - "gray", - "bone", - "pink", - "spring", - "summer", - "autumn", - "winter", - "cool", - "Wistia", - "hot", - "afmhot", - "gist_heat", - "copper", - ] - - diverging = [ - "PiYG", - "PRGn", - "BrBG", - "PuOr", - "RdGy", - "RdBu", - "RdYlBu", - "RdYlGn", - "Spectral", - "coolwarm", - "bwr", - "seismic", - ] - - cyclic = ["twilight", "twilight_shifted", "hsv"] - - qualitative = [ - "Pastel1", - "Pastel2", - "Paired", - "Accent", - "Dark2", - "Set1", - "Set2", - "Set3", - "tab10", - "tab20", - "tab20b", - "tab20c", - ] - - miscellaneous = [ - "flag", - "prism", - "ocean", - "gist_earth", - "terrain", - "gist_stern", - "gnuplot", - "gnuplot2", - "CMRmap", - "cubehelix", - "brg", - "gist_rainbow", - "rainbow", - "jet", - "turbo", - "nipy_spectral", - "gist_ncar", - ] - - all = ( - perceptually_uniform - + sequential - + sequential2 - + diverging - + cyclic - + qualitative - + miscellaneous - ) - - -def make_cmap(name: str, alpha: float = 1.0) -> np.ndarray: - _cm = getattr(cm, name) - - if name in ColormapNames.qualitative: - n_colors = getattr(_cm, "N") - else: - n_colors = 256 - - cmap = np.vstack([_cm(i) for i in range(n_colors)]) - cmap[:, -1] = alpha - - return cmap.astype(np.float32) - - -if __name__ == "__main__": - for name in ColormapNames().all: - np.savetxt(f"./colormaps/{name}", make_cmap(name)) diff --git a/setup.py b/setup.py index 56900c1fa..abebbb4c4 100644 --- a/setup.py +++ b/setup.py @@ -6,6 +6,7 @@ "numpy>=1.23.0", "wgpu>=0.16.0", "pygfx>=0.3.0", + "cmap>=0.1.3", ] From 68cf50b319d76196d4bed487128d36f1616706d1 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Fri, 2 Aug 2024 13:52:15 -0400 Subject: [PATCH 099/185] change basis of axes (#567) * change basis of axes, WIP * indexing bugfix * black --- fastplotlib/graphics/_axes.py | 62 +++++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/fastplotlib/graphics/_axes.py b/fastplotlib/graphics/_axes.py index 2a63183d5..9541dceeb 100644 --- a/fastplotlib/graphics/_axes.py +++ b/fastplotlib/graphics/_axes.py @@ -1,10 +1,13 @@ import numpy as np import pygfx +from pylinalg import quat_from_vecs, vec_transform_quat GRID_PLANES = ["xy", "xz", "yz"] +CANONICAL_BAIS = np.array([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]) + # very thin subclass that just adds GridMaterial properties to this world object for easier user control class Grid(pygfx.Grid): @@ -252,6 +255,9 @@ def __init__( grid_kwargs: dict = None, auto_grid: bool = True, offset: np.ndarray = np.array([0.0, 0.0, 0.0]), + basis: np.ndarray = np.array( + [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]] + ), ): self._plot_area = plot_area @@ -362,10 +368,28 @@ def __init__( self._intersection = intersection self._auto_grid = auto_grid + self._basis = None + self.basis = basis + @property def world_object(self) -> pygfx.WorldObject: return self._world_object + @property + def basis(self) -> np.ndarray: + """get or set the basis, shape is [3, 3]""" + return self._basis + + @basis.setter + def basis(self, basis: np.ndarray): + if basis.shape != (3, 3): + raise ValueError + + # apply quaternion to each of x, y, z rulers + for dim, cbasis, new_basis in zip(["x", "y", "z"], CANONICAL_BAIS, basis): + ruler: pygfx.Ruler = getattr(self, dim) + ruler.local.rotation = quat_from_vecs(cbasis, new_basis) + @property def offset(self) -> np.ndarray: """offset of the axes""" @@ -395,6 +419,19 @@ def grids(self) -> Grids | bool: """grids for each plane: xy, xz, yz""" return self._grids + @property + def colors(self) -> tuple[pygfx.Color]: + return tuple(getattr(self, dim).line.material.color for dim in ["x", "y", "z"]) + + @colors.setter + def colors(self, colors: tuple[pygfx.Color | str]): + """get or set the colors for the x, y, and z rulers""" + if len(colors) != 3: + raise ValueError + + for dim, color in zip(["x", "y", "z"], colors): + getattr(self, dim).line.material.color = color + @property def auto_grid(self) -> bool: """auto adjust the grid on each render cycle""" @@ -482,10 +519,31 @@ def update_using_camera(self): xpos, ypos, width, height = self._plot_area.get_rect() # orthographic projection, get ranges using inverse - # get range of screen space + # get range of screen space by getting the corners xmin, xmax = xpos, xpos + width ymin, ymax = ypos + height, ypos + # apply quaternion to account for rotation of axes + # xmin, _, _ = vec_transform_quat( + # [xmin, ypos + height / 2, 0], + # self.x.local.rotation + # ) + # + # xmax, _, _ = vec_transform_quat( + # [xmax, ypos + height / 2, 0], + # self.x.local.rotation, + # ) + # + # _, ymin, _ = vec_transform_quat( + # [xpos + width / 2, ymin, 0], + # self.y.local.rotation + # ) + # + # _, ymax, _ = vec_transform_quat( + # [xpos + width / 2, ymax, 0], + # self.y.local.rotation + # ) + min_vals = self._plot_area.map_screen_to_world((xmin, ymin)) max_vals = self._plot_area.map_screen_to_world((xmax, ymax)) @@ -578,7 +636,7 @@ def update(self, bbox, intersection): self.z.start_pos = world_x_10, world_y_10, world_zmin self.z.end_pos = world_x_10, world_y_10, world_zmax - self.z.start_value = self.z.start_pos[1] - self.offset[2] + self.z.start_value = self.z.start_pos[2] - self.offset[2] statsz = self.z.update( self._plot_area.camera, self._plot_area.viewport.logical_size ) From c7d7ff5adfa2e82b2e4e9193555238b4f67c4736 Mon Sep 17 00:00:00 2001 From: Caitlin Lewis <69729525+clewis7@users.noreply.github.com> Date: Fri, 23 Aug 2024 13:26:14 -0400 Subject: [PATCH 100/185] Update README.md (#594) * Update README.md Add DOI badge * Update conf.py --- README.md | 1 + docs/source/conf.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9f3f9b236..2a5e33b9c 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ [![CI](https://github.com/kushalkolar/fastplotlib/actions/workflows/ci.yml/badge.svg)](https://github.com/kushalkolar/fastplotlib/actions/workflows/ci.yml) [![PyPI version](https://badge.fury.io/py/fastplotlib.svg)](https://badge.fury.io/py/fastplotlib) [![Documentation Status](https://readthedocs.org/projects/fastplotlib/badge/?version=latest)](https://fastplotlib.readthedocs.io/en/latest/?badge=latest) +[![DOI](https://zenodo.org/badge/485481453.svg)](https://zenodo.org/doi/10.5281/zenodo.13365890) [**Installation**](https://github.com/kushalkolar/fastplotlib#installation) | [**GPU Drivers**](https://github.com/kushalkolar/fastplotlib#graphics-drivers) | diff --git a/docs/source/conf.py b/docs/source/conf.py index 64c05b82c..1b296f533 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -101,7 +101,7 @@ intersphinx_mapping = { "python": ("https://docs.python.org/3", None), "numpy": ("https://numpy.org/doc/stable/", None), - "pygfx": ("https://pygfx.org/stable", None), + "pygfx": ("https://docs.pygfx.org/stable/", None), "wgpu": ("https://wgpu-py.readthedocs.io/en/latest", None), "fastplotlib": ("https://fastplotlib.readthedocs.io/en/latest/", None), } From c30bc646e88ad4bb2a5c3bb033018e33f5342632 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Fri, 23 Aug 2024 18:26:29 +0100 Subject: [PATCH 101/185] fix `BaseSelector._move_to_pointer` (#592) * fix BaseSelector._move_to_pointer * fix pygfx intersphinx link --------- Co-authored-by: Caitlin --- .../graphics/selectors/_base_selector.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/fastplotlib/graphics/selectors/_base_selector.py b/fastplotlib/graphics/selectors/_base_selector.py index ab7bda049..fd6ed2f33 100644 --- a/fastplotlib/graphics/selectors/_base_selector.py +++ b/fastplotlib/graphics/selectors/_base_selector.py @@ -1,7 +1,6 @@ from typing import * from dataclasses import dataclass from functools import partial -import weakref import numpy as np @@ -269,30 +268,35 @@ def _move_to_pointer(self, ev): """ Calculates delta just using current world object position and calls self._move_graphic(). """ - current_position: np.ndarray = self.offset - - # middle mouse button clicks + # check for middle mouse button click if ev.button != 3: return + if self.axis == "x": + offset = self.offset[0] + elif self.axis == "y": + offset = self.offset[1] + + current_pos_world: np.ndarray = self.selection + offset + world_pos = self._plot_area.map_screen_to_world(ev) # outside this viewport if world_pos is None: return - self.delta = world_pos - current_position + self.delta = world_pos - current_pos_world self._pygfx_event = ev # use fill by default as the source, such as in region selectors if len(self._fill) > 0: self._move_info = MoveInfo( - last_position=current_position, source=self._fill[0] + last_position=current_pos_world, source=self._fill[0] ) # else use an edge, such as for linear selector else: self._move_info = MoveInfo( - last_position=current_position, source=self._edges[0] + last_position=current_pos_world, source=self._edges[0] ) self._move_graphic(self.delta) From c12b2c8db02d645fdf247f0ac5de14dd0ace0f2b Mon Sep 17 00:00:00 2001 From: Caitlin Lewis <69729525+clewis7@users.noreply.github.com> Date: Sat, 24 Aug 2024 10:21:12 -0400 Subject: [PATCH 102/185] add rectangular region selector (#576) * start working on adding rectangle region selector * rename selector tool * update line setting, remove origin * fix selection feature * fix pick write issue * rename file, fix line initialization * add fixed axis if given, make sure bounds enforced * add methods for graphics * get selected ixs and data for images * add rectangle methods * better lower bound limit for collections * add vertices * screen space instead of world space for vertices * change vertices to square, change vertex color * fix selection for line collections * add example * update api docs, add example * lint * update conf and api files * this file should be deleted * requested changes, update with better example, more comments * fix offset when adding rectangle selector so fill events happen * update api docs * requested changes --- .../RectangleSelectionFeature.rst | 35 ++ docs/source/api/graphic_features/index.rst | 1 + docs/source/api/graphics/ImageGraphic.rst | 1 + docs/source/api/graphics/LineCollection.rst | 1 + docs/source/api/graphics/LineGraphic.rst | 1 + docs/source/api/graphics/LineStack.rst | 1 + .../api/selectors/RectangleSelector.rst | 53 ++ docs/source/api/selectors/index.rst | 1 + .../desktop/selectors/rectangle_selector.py | 66 +++ .../selectors/rectangle_selector_zoom.py | 52 ++ fastplotlib/graphics/_features/__init__.py | 7 +- .../graphics/_features/_selection_features.py | 152 ++++- fastplotlib/graphics/image.py | 44 +- fastplotlib/graphics/line.py | 47 +- fastplotlib/graphics/line_collection.py | 43 +- fastplotlib/graphics/selectors/__init__.py | 3 +- .../graphics/selectors/_base_selector.py | 3 + fastplotlib/graphics/selectors/_rectangle.py | 517 ++++++++++++++++++ .../graphics/selectors/_rectangle_region.py | 355 ------------ 19 files changed, 1022 insertions(+), 361 deletions(-) create mode 100644 docs/source/api/graphic_features/RectangleSelectionFeature.rst create mode 100644 docs/source/api/selectors/RectangleSelector.rst create mode 100644 examples/desktop/selectors/rectangle_selector.py create mode 100644 examples/desktop/selectors/rectangle_selector_zoom.py create mode 100644 fastplotlib/graphics/selectors/_rectangle.py delete mode 100644 fastplotlib/graphics/selectors/_rectangle_region.py diff --git a/docs/source/api/graphic_features/RectangleSelectionFeature.rst b/docs/source/api/graphic_features/RectangleSelectionFeature.rst new file mode 100644 index 000000000..d35752a24 --- /dev/null +++ b/docs/source/api/graphic_features/RectangleSelectionFeature.rst @@ -0,0 +1,35 @@ +.. _api.RectangleSelectionFeature: + +RectangleSelectionFeature +************************* + +========================= +RectangleSelectionFeature +========================= +.. currentmodule:: fastplotlib.graphics._features + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: RectangleSelectionFeature_api + + RectangleSelectionFeature + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: RectangleSelectionFeature_api + + RectangleSelectionFeature.value + +Methods +~~~~~~~ +.. autosummary:: + :toctree: RectangleSelectionFeature_api + + RectangleSelectionFeature.add_event_handler + RectangleSelectionFeature.block_events + RectangleSelectionFeature.clear_event_handlers + RectangleSelectionFeature.remove_event_handler + RectangleSelectionFeature.set_value + diff --git a/docs/source/api/graphic_features/index.rst b/docs/source/api/graphic_features/index.rst index 87504ea8a..ea3ce8903 100644 --- a/docs/source/api/graphic_features/index.rst +++ b/docs/source/api/graphic_features/index.rst @@ -24,6 +24,7 @@ Graphic Features TextOutlineThickness LinearSelectionFeature LinearRegionSelectionFeature + RectangleSelectionFeature Name Offset Rotation diff --git a/docs/source/api/graphics/ImageGraphic.rst b/docs/source/api/graphics/ImageGraphic.rst index 1f15c6963..d4fa2e7b9 100644 --- a/docs/source/api/graphics/ImageGraphic.rst +++ b/docs/source/api/graphics/ImageGraphic.rst @@ -46,6 +46,7 @@ Methods ImageGraphic.add_event_handler ImageGraphic.add_linear_region_selector ImageGraphic.add_linear_selector + ImageGraphic.add_rectangle_selector ImageGraphic.clear_event_handlers ImageGraphic.remove_event_handler ImageGraphic.reset_vmin_vmax diff --git a/docs/source/api/graphics/LineCollection.rst b/docs/source/api/graphics/LineCollection.rst index 23e0b512d..459884fdd 100644 --- a/docs/source/api/graphics/LineCollection.rst +++ b/docs/source/api/graphics/LineCollection.rst @@ -51,6 +51,7 @@ Methods LineCollection.add_graphic LineCollection.add_linear_region_selector LineCollection.add_linear_selector + LineCollection.add_rectangle_selector LineCollection.clear_event_handlers LineCollection.remove_event_handler LineCollection.remove_graphic diff --git a/docs/source/api/graphics/LineGraphic.rst b/docs/source/api/graphics/LineGraphic.rst index 96c9ff62b..a3e1587f7 100644 --- a/docs/source/api/graphics/LineGraphic.rst +++ b/docs/source/api/graphics/LineGraphic.rst @@ -44,6 +44,7 @@ Methods LineGraphic.add_event_handler LineGraphic.add_linear_region_selector LineGraphic.add_linear_selector + LineGraphic.add_rectangle_selector LineGraphic.clear_event_handlers LineGraphic.remove_event_handler LineGraphic.rotate diff --git a/docs/source/api/graphics/LineStack.rst b/docs/source/api/graphics/LineStack.rst index 41cd3fbc8..3c14e708c 100644 --- a/docs/source/api/graphics/LineStack.rst +++ b/docs/source/api/graphics/LineStack.rst @@ -51,6 +51,7 @@ Methods LineStack.add_graphic LineStack.add_linear_region_selector LineStack.add_linear_selector + LineStack.add_rectangle_selector LineStack.clear_event_handlers LineStack.remove_event_handler LineStack.remove_graphic diff --git a/docs/source/api/selectors/RectangleSelector.rst b/docs/source/api/selectors/RectangleSelector.rst new file mode 100644 index 000000000..b2dc40d2e --- /dev/null +++ b/docs/source/api/selectors/RectangleSelector.rst @@ -0,0 +1,53 @@ +.. _api.RectangleSelector: + +RectangleSelector +***************** + +================= +RectangleSelector +================= +.. currentmodule:: fastplotlib + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: RectangleSelector_api + + RectangleSelector + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: RectangleSelector_api + + RectangleSelector.axes + RectangleSelector.axis + RectangleSelector.block_events + RectangleSelector.deleted + RectangleSelector.event_handlers + RectangleSelector.limits + RectangleSelector.name + RectangleSelector.offset + RectangleSelector.parent + RectangleSelector.rotation + RectangleSelector.selection + RectangleSelector.supported_events + RectangleSelector.visible + RectangleSelector.world_object + +Methods +~~~~~~~ +.. autosummary:: + :toctree: RectangleSelector_api + + RectangleSelector.add_axes + RectangleSelector.add_event_handler + RectangleSelector.clear_event_handlers + RectangleSelector.get_selected_data + RectangleSelector.get_selected_index + RectangleSelector.get_selected_indices + RectangleSelector.remove_event_handler + RectangleSelector.rotate + RectangleSelector.share_property + RectangleSelector.unshare_property + diff --git a/docs/source/api/selectors/index.rst b/docs/source/api/selectors/index.rst index ffa4054db..4a0caf8af 100644 --- a/docs/source/api/selectors/index.rst +++ b/docs/source/api/selectors/index.rst @@ -6,3 +6,4 @@ Selectors LinearSelector LinearRegionSelector + RectangleSelector diff --git a/examples/desktop/selectors/rectangle_selector.py b/examples/desktop/selectors/rectangle_selector.py new file mode 100644 index 000000000..48e8647ac --- /dev/null +++ b/examples/desktop/selectors/rectangle_selector.py @@ -0,0 +1,66 @@ +""" +Rectangle Selectors +=================== + +Example showing how to use a `RectangleSelector` with line collections +""" + +# test_example = false +# sphinx_gallery_pygfx_docs = 'screenshot' + +import numpy as np +import fastplotlib as fpl +from itertools import product + +# create a figure +figure = fpl.Figure( + size=(700, 560) +) + + +# generate some data +def make_circle(center, radius: float, n_points: int = 75) -> np.ndarray: + theta = np.linspace(0, 2 * np.pi, n_points) + xs = radius * np.sin(theta) + ys = radius * np.cos(theta) + + return np.column_stack([xs, ys]) + center + + +spatial_dims = (50, 50) + +circles = list() +for center in product(range(0, spatial_dims[0], 9), range(0, spatial_dims[1], 9)): + circles.append(make_circle(center, 3, n_points=75)) + +pos_xy = np.vstack(circles) + +# add image +line_collection = figure[0, 0].add_line_collection(circles, cmap="jet", thickness=5) + +# add rectangle selector to image graphic +rectangle_selector = line_collection.add_rectangle_selector() + + +# add event handler to highlight selected indices +@rectangle_selector.add_event_handler("selection") +def color_indices(ev): + line_collection.cmap = "jet" + ixs = ev.get_selected_indices() + + # iterate through each of the selected indices, if the array size > 0 that mean it's under the selection + selected_line_ixs = [i for i in range(len(ixs)) if ixs[i].size > 0] + line_collection[selected_line_ixs].colors = "w" + + +# manually move selector to make a nice gallery image :D +rectangle_selector.selection = (15, 30, 15, 30) + + +figure.show() + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.run() diff --git a/examples/desktop/selectors/rectangle_selector_zoom.py b/examples/desktop/selectors/rectangle_selector_zoom.py new file mode 100644 index 000000000..b5932d820 --- /dev/null +++ b/examples/desktop/selectors/rectangle_selector_zoom.py @@ -0,0 +1,52 @@ +""" +Rectangle Selectors +=================== +Example showing how to use a `RectangleSelector` with images +""" + +# test_example = false +# sphinx_gallery_pygfx_docs = 'screenshot' + +import imageio.v3 as iio +import fastplotlib as fpl + +# create a figure +figure = fpl.Figure( + shape=(2, 1), + size=(700, 560) +) + +# add image +image_graphic = figure[0, 0].add_image(data=iio.imread("imageio:camera.png")) + +# add rectangle selector to image graphic +rectangle_selector = image_graphic.add_rectangle_selector() + +# add a zoomed plot of the selected data +zoom_ig = figure[1, 0].add_image(rectangle_selector.get_selected_data()) + + +# add event handler to update the data of the zoomed image as the selection changes +@rectangle_selector.add_event_handler("selection") +def update_data(ev): + # get the new data + new_data = ev.get_selected_data() + + # remove the old zoomed image graphic + global zoom_ig + + figure[1, 0].remove_graphic(zoom_ig) + + # add new zoomed image of new data + zoom_ig = figure[1, 0].add_image(data=new_data) + + # autoscale the plot + figure[1, 0].auto_scale() + +figure.show() + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.run() diff --git a/fastplotlib/graphics/_features/__init__.py b/fastplotlib/graphics/_features/__init__.py index e36de089e..1d2f6ca44 100644 --- a/fastplotlib/graphics/_features/__init__.py +++ b/fastplotlib/graphics/_features/__init__.py @@ -31,7 +31,11 @@ TextOutlineThickness, ) -from ._selection_features import LinearSelectionFeature, LinearRegionSelectionFeature +from ._selection_features import ( + LinearSelectionFeature, + LinearRegionSelectionFeature, + RectangleSelectionFeature, +) from ._common import Name, Offset, Rotation, Visible, Deleted @@ -56,6 +60,7 @@ "TextOutlineThickness", "LinearSelectionFeature", "LinearRegionSelectionFeature", + "RectangleSelectionFeature", "Name", "Offset", "Rotation", diff --git a/fastplotlib/graphics/_features/_selection_features.py b/fastplotlib/graphics/_features/_selection_features.py index 71ba53425..e499e72c9 100644 --- a/fastplotlib/graphics/_features/_selection_features.py +++ b/fastplotlib/graphics/_features/_selection_features.py @@ -1,4 +1,4 @@ -from typing import Sequence +from typing import Sequence, Tuple import numpy as np @@ -190,3 +190,153 @@ def set_value(self, selector, value: Sequence[float]): # TODO: user's selector event handlers can call event.graphic.get_selected_indices() to get the data index, # and event.graphic.get_selected_data() to get the data under the selection # this is probably a good idea so that the data isn't sliced until it's actually necessary + + +class RectangleSelectionFeature(GraphicFeature): + """ + **additional event attributes:** + + +----------------------+----------+------------------------------------+ + | attribute | type | description | + +======================+==========+====================================+ + | get_selected_indices | callable | returns indices under the selector | + +----------------------+----------+------------------------------------+ + | get_selected_data | callable | returns data under the selector | + +----------------------+----------+------------------------------------+ + + **info dict:** + + +----------+------------+-------------------------------------------+ + | dict key | value type | value description | + +==========+============+===========================================+ + | value | np.ndarray | new [xmin, xmax, ymin, ymax] of selection | + +----------+------------+-------------------------------------------+ + + """ + + def __init__( + self, + value: tuple[float, float, float, float], + limits: tuple[float, float, float, float], + ): + super().__init__() + + self._limits = limits + self._value = tuple(int(v) for v in value) + + @property + def value(self) -> np.ndarray[float]: + """ + (xmin, xmax, ymin, ymax) of the selection, in data space + """ + return self._value + + def set_value(self, selector, value: Sequence[float]): + """ + Set the selection of the rectangle selector. + + Parameters + ---------- + selector: RectangleSelector + + value: (float, float, float, float) + new values (xmin, xmax, ymin, ymax) of the selection + """ + if not len(value) == 4: + raise TypeError( + "Selection must be an array, tuple, list, or sequence in the form of `(xmin, xmax, ymin, ymax)`, " + "where `xmin`, `xmax`, `ymin`, `ymax` are numeric values." + ) + + # convert to array + value = np.asarray(value, dtype=np.float32) + + # clip values if they are beyond the limits + value[:2] = value[:2].clip(self._limits[0], self._limits[1]) + # clip y + value[2:] = value[2:].clip(self._limits[2], self._limits[3]) + + xmin, xmax, ymin, ymax = value + + # make sure `selector width >= 2` and selector height >=2 , left edge must not move past right edge! + # or bottom edge must not move past top edge! + if not (xmax - xmin) >= 0 or not (ymax - ymin) >= 0: + return + + # change fill mesh + # change left x position of the fill mesh + selector.fill.geometry.positions.data[mesh_masks.x_left] = xmin + + # change right x position of the fill mesh + selector.fill.geometry.positions.data[mesh_masks.x_right] = xmax + + # change bottom y position of the fill mesh + selector.fill.geometry.positions.data[mesh_masks.y_bottom] = ymin + + # change top position of the fill mesh + selector.fill.geometry.positions.data[mesh_masks.y_top] = ymax + + # change the edge lines + + # each edge line is defined by two end points which are stored in the + # geometry.positions + # [x0, y0, z0] + # [x1, y1, z0] + + # left line + z = selector.edges[0].geometry.positions.data[:, -1][0] + selector.edges[0].geometry.positions.data[:] = np.array( + [[xmin, ymin, z], [xmin, ymax, z]] + ) + + # right line + selector.edges[1].geometry.positions.data[:] = np.array( + [[xmax, ymin, z], [xmax, ymax, z]] + ) + + # bottom line + selector.edges[2].geometry.positions.data[:] = np.array( + [[xmin, ymin, z], [xmax, ymin, z]] + ) + + # top line + selector.edges[3].geometry.positions.data[:] = np.array( + [[xmin, ymax, z], [xmax, ymax, z]] + ) + + # change the vertex positions + + # bottom left + selector.vertices[0].geometry.positions.data[:] = np.array([[xmin, ymin, 1]]) + + # bottom right + selector.vertices[1].geometry.positions.data[:] = np.array([[xmax, ymin, 1]]) + + # top left + selector.vertices[2].geometry.positions.data[:] = np.array([[xmin, ymax, 1]]) + + # top right + selector.vertices[3].geometry.positions.data[:] = np.array([[xmax, ymax, 1]]) + + self._value = value + + # send changes to GPU + selector.fill.geometry.positions.update_range() + + for edge in selector.edges: + edge.geometry.positions.update_range() + + for vertex in selector.vertices: + vertex.geometry.positions.update_range() + + # send event + if len(self._event_handlers) < 1: + return + + event = FeatureEvent("selection", {"value": self.value}) + + event.get_selected_indices = selector.get_selected_indices + event.get_selected_data = selector.get_selected_data + + # calls any events + self._call_event_handlers(event) diff --git a/fastplotlib/graphics/image.py b/fastplotlib/graphics/image.py index 6730e86cb..299c682b3 100644 --- a/fastplotlib/graphics/image.py +++ b/fastplotlib/graphics/image.py @@ -1,10 +1,11 @@ +import math from typing import * import pygfx from ..utils import quick_min_max from ._base import Graphic -from .selectors import LinearSelector, LinearRegionSelector +from .selectors import LinearSelector, LinearRegionSelector, RectangleSelector from ._features import ( TextureArray, ImageCmap, @@ -393,3 +394,44 @@ def add_linear_region_selector( selector.offset = selector.offset + (0.0, 0.0, self.offset[-1] + 1) return selector + + def add_rectangle_selector( + self, + selection: tuple[float, float, float, float] = None, + fill_color=(0, 0, 0.35, 0.2), + **kwargs, + ) -> RectangleSelector: + """ + Add a :class:`.RectangleSelector`. Selectors are just ``Graphic`` objects, so you can manage, + remove, or delete them from a plot area just like any other ``Graphic``. + + Parameters + ---------- + selection: (float, float, float, float), optional + initial (xmin, xmax, ymin, ymax) of the selection + """ + # default selection is 25% of the diagonal + if selection is None: + diagonal = math.sqrt( + self._data.value.shape[0] ** 2 + self._data.value.shape[1] ** 2 + ) + + selection = (0, int(diagonal / 4), 0, int(diagonal / 4)) + + # min/max limits are image shape + limits = (0, self._data.value.shape[0], 0, self._data.value.shape[1]) + + selector = RectangleSelector( + selection=selection, + limits=limits, + fill_color=fill_color, + parent=self, + **kwargs, + ) + + self._plot_area.add_graphic(selector, center=False) + + # place above this graphic + selector.offset = selector.offset + (0.0, 0.0, self.offset[-1] + 1) + + return selector diff --git a/fastplotlib/graphics/line.py b/fastplotlib/graphics/line.py index f352dfde5..1574587fe 100644 --- a/fastplotlib/graphics/line.py +++ b/fastplotlib/graphics/line.py @@ -5,7 +5,7 @@ import pygfx from ._positions_base import PositionsGraphic -from .selectors import LinearRegionSelector, LinearSelector +from .selectors import LinearRegionSelector, LinearSelector, RectangleSelector from ._features import Thickness @@ -216,6 +216,51 @@ def add_linear_region_selector( # so we should only work with a proxy on the user-end return selector + def add_rectangle_selector( + self, + selection: tuple[float, float, float, float] = None, + **kwargs, + ) -> RectangleSelector: + """ + Add a :class:`.RectangleSelector`. Selectors are just ``Graphic`` objects, so you can manage, + remove, or delete them from a plot area just like any other ``Graphic``. + + Parameters + ---------- + selection: (float, float, float, float), optional + initial (xmin, xmax, ymin, ymax) of the selection + """ + # computes args to create selectors + n_datapoints = self.data.value.shape[0] + value_25p = int(n_datapoints / 4) + + # remove any nans + data = self.data.value[~np.any(np.isnan(self.data.value), axis=1)] + + x_axis_vals = data[:, 0] + y_axis_vals = data[:, 1] + + ymin = np.floor(y_axis_vals.min()).astype(int) + ymax = np.ceil(y_axis_vals.max()).astype(int) + + # default selection is 25% of the image + if selection is None: + selection = (x_axis_vals[0], x_axis_vals[value_25p], ymin, ymax) + + # min/max limits + limits = (x_axis_vals[0], x_axis_vals[-1], ymin * 1.5, ymax * 1.5) + + selector = RectangleSelector( + selection=selection, + limits=limits, + parent=self, + **kwargs, + ) + + self._plot_area.add_graphic(selector, center=False) + + return selector + # TODO: this method is a bit of a mess, can refactor later def _get_linear_selector_init_args( self, axis: str, padding diff --git a/fastplotlib/graphics/line_collection.py b/fastplotlib/graphics/line_collection.py index 666d441e4..c4af5dddc 100644 --- a/fastplotlib/graphics/line_collection.py +++ b/fastplotlib/graphics/line_collection.py @@ -7,7 +7,7 @@ from ..utils import parse_cmap_values from ._collection_base import CollectionIndexer, GraphicCollection, CollectionFeature from .line import LineGraphic -from .selectors import LinearRegionSelector, LinearSelector +from .selectors import LinearRegionSelector, LinearSelector, RectangleSelector class _LineCollectionProperties: @@ -447,6 +447,47 @@ def add_linear_region_selector( # so we should only work with a proxy on the user-end return selector + def add_rectangle_selector( + self, + selection: tuple[float, float, float, float] = None, + **kwargs, + ) -> RectangleSelector: + """ + Add a :class:`.RectangleSelector`. Selectors are just ``Graphic`` objects, so you can manage, + remove, or delete them from a plot area just like any other ``Graphic``. + + Parameters + ---------- + selection: (float, float, float, float), optional + initial (xmin, xmax, ymin, ymax) of the selection + """ + bbox = self.world_object.get_world_bounding_box() + + xdata = np.array(self.data[:, 0]) + xmin, xmax = (np.nanmin(xdata), np.nanmax(xdata)) + value_25px = (xmax - xmin) / 4 + + ydata = np.array(self.data[:, 1]) + ymin = np.floor(ydata.min()).astype(int) + + ymax = np.ptp(bbox[:, 1]) + + if selection is None: + selection = (xmin, value_25px, ymin, ymax) + + limits = (xmin, xmax, ymin - (ymax * 1.5 - ymax), ymax * 1.5) + + selector = RectangleSelector( + selection=selection, + limits=limits, + parent=self, + **kwargs, + ) + + self._plot_area.add_graphic(selector, center=False) + + return selector + def _get_linear_selector_init_args(self, axis, padding): # use bbox to get size and center bbox = self.world_object.get_world_bounding_box() diff --git a/fastplotlib/graphics/selectors/__init__.py b/fastplotlib/graphics/selectors/__init__.py index 4f28f571c..9133192e9 100644 --- a/fastplotlib/graphics/selectors/__init__.py +++ b/fastplotlib/graphics/selectors/__init__.py @@ -1,6 +1,7 @@ from ._linear import LinearSelector from ._linear_region import LinearRegionSelector from ._polygon import PolygonSelector +from ._rectangle import RectangleSelector -__all__ = ["LinearSelector", "LinearRegionSelector"] +__all__ = ["LinearSelector", "LinearRegionSelector", "RectangleSelector"] diff --git a/fastplotlib/graphics/selectors/_base_selector.py b/fastplotlib/graphics/selectors/_base_selector.py index fd6ed2f33..3729adda5 100644 --- a/fastplotlib/graphics/selectors/_base_selector.py +++ b/fastplotlib/graphics/selectors/_base_selector.py @@ -68,6 +68,9 @@ def __init__( self._edges + self._fill + self._vertices ) + for wo in self._world_objects: + wo.material.pick_write = True + self._hover_responsive: Tuple[WorldObject, ...] = hover_responsive if hover_responsive is not None: diff --git a/fastplotlib/graphics/selectors/_rectangle.py b/fastplotlib/graphics/selectors/_rectangle.py new file mode 100644 index 000000000..aae99803e --- /dev/null +++ b/fastplotlib/graphics/selectors/_rectangle.py @@ -0,0 +1,517 @@ +import warnings +from numbers import Real +from typing import * +import numpy as np + +import pygfx +from .._collection_base import GraphicCollection + +from .._base import Graphic +from .._features import RectangleSelectionFeature +from ._base_selector import BaseSelector + + +class RectangleSelector(BaseSelector): + @property + def parent(self) -> Graphic | None: + """Graphic that selector is associated with.""" + return self._parent + + @property + def selection(self) -> Sequence[float] | List[Sequence[float]]: + """ + (xmin, xmax, ymin, ymax) of the rectangle selection + """ + return self._selection.value + + @selection.setter + def selection(self, selection: Sequence[float]): + # set (xmin, xmax, ymin, ymax) of the selector in data space + graphic = self._parent + + if isinstance(graphic, GraphicCollection): + pass + + self._selection.set_value(self, selection) + + @property + def limits(self) -> Tuple[float, float, float, float]: + """Return the limits of the selector.""" + return self._limits + + @limits.setter + def limits(self, values: Tuple[float, float, float, float]): + if len(values) != 4 or not all(map(lambda v: isinstance(v, Real), values)): + raise TypeError("limits must be an iterable of two numeric values") + self._limits = tuple( + map(round, values) + ) # if values are close to zero things get weird so round them + self._selection._limits = self._limits + + def __init__( + self, + selection: Sequence[float], + limits: Sequence[float], + parent: Graphic = None, + resizable: bool = True, + fill_color=(0, 0, 0.35), + edge_color=(0.8, 0.6, 0), + edge_thickness: float = 8, + vertex_color=(0.7, 0.4, 0), + vertex_thickness: float = 8, + arrow_keys_modifier: str = "Shift", + name: str = None, + ): + """ + Create a RectangleSelector graphic which can be used to select a rectangular region of data. + Allows sub-selecting data from a ``Graphic`` or from multiple Graphics. + + Parameters + ---------- + selection: (float, float, float, float) + the initial selection of the rectangle, ``(x_min, x_max, y_min, y_max)`` + + limits: (float, float, float, float) + limits of the selector, ``(x_min, x_max, y_min, y_max)`` + + parent: Graphic, default ``None`` + associate this selector with a parent Graphic + + resizable: bool, default ``True`` + if ``True``, the edges can be dragged to resize the selection + + fill_color: str, array, or tuple + fill color for the selector, passed to pygfx.Color + + edge_color: str, array, or tuple + edge color for the selector, passed to pygfx.Color + + edge_thickness: float, default 8 + edge thickness + + arrow_keys_modifier: str + modifier key that must be pressed to initiate movement using arrow keys, must be one of: + "Control", "Shift", "Alt" or ``None`` + + name: str + name for this selector graphic + """ + + if not len(selection) == 4 or not len(limits) == 4: + raise ValueError() + + # lots of very close to zero values etc. so round them + selection = tuple(map(round, selection)) + limits = tuple(map(round, limits)) + + self._parent = parent + self._limits = np.asarray(limits) + self._resizable = resizable + + selection = np.asarray(selection) + + # world object for this will be a group + # basic mesh for the fill area of the selector + # line for each edge of the selector + group = pygfx.Group() + + xmin, xmax, ymin, ymax = selection + + width = xmax - xmin + height = ymax - ymin + + if width < 0 or height < 0: + raise ValueError() + + self.fill = pygfx.Mesh( + pygfx.box_geometry(width, height, 1), + pygfx.MeshBasicMaterial(color=pygfx.Color(fill_color), pick_write=True), + ) + + self.fill.world.position = (0, 0, -2) + + group.add(self.fill) + + # position data for the left edge line + left_line_data = np.array( + [ + [xmin, ymin, 0], + [xmin, ymax, 0], + ] + ).astype(np.float32) + + left_line = pygfx.Line( + pygfx.Geometry(positions=left_line_data.copy()), + pygfx.LineMaterial(thickness=edge_thickness, color=edge_color), + ) + + # position data for the right edge line + right_line_data = np.array( + [ + [xmax, ymin, 0], + [xmax, ymax, 0], + ] + ).astype(np.float32) + + right_line = pygfx.Line( + pygfx.Geometry(positions=right_line_data.copy()), + pygfx.LineMaterial(thickness=edge_thickness, color=edge_color), + ) + + # position data for the left edge line + bottom_line_data = np.array( + [ + [xmin, ymax, 0], + [xmax, ymax, 0], + ] + ).astype(np.float32) + + bottom_line = pygfx.Line( + pygfx.Geometry(positions=bottom_line_data.copy()), + pygfx.LineMaterial(thickness=edge_thickness, color=edge_color), + ) + + # position data for the right edge line + top_line_data = np.array( + [ + [xmin, ymin, 0], + [xmax, ymin, 0], + ] + ).astype(np.float32) + + top_line = pygfx.Line( + pygfx.Geometry(positions=top_line_data.copy()), + pygfx.LineMaterial(thickness=edge_thickness, color=edge_color), + ) + + self.edges: Tuple[pygfx.Line, pygfx.Line, pygfx.Line, pygfx.Line] = ( + left_line, + right_line, + bottom_line, + top_line, + ) # left line, right line, bottom line, top line + + # add the edge lines + for edge in self.edges: + edge.world.z = -0.5 + group.add(edge) + + # vertices + top_left_vertex_data = (xmin, ymax, 1) + top_right_vertex_data = (xmax, ymax, 1) + bottom_left_vertex_data = (xmin, ymin, 1) + bottom_right_vertex_data = (xmax, ymin, 1) + + top_left_vertex = pygfx.Points( + pygfx.Geometry(positions=[top_left_vertex_data], sizes=[vertex_thickness]), + pygfx.PointsMarkerMaterial( + marker="square", + size=vertex_thickness, + color=vertex_color, + size_mode="vertex", + edge_color=vertex_color, + ), + ) + + top_right_vertex = pygfx.Points( + pygfx.Geometry(positions=[top_right_vertex_data], sizes=[vertex_thickness]), + pygfx.PointsMarkerMaterial( + marker="square", + size=vertex_thickness, + color=vertex_color, + size_mode="vertex", + edge_color=vertex_color, + ), + ) + + bottom_left_vertex = pygfx.Points( + pygfx.Geometry( + positions=[bottom_left_vertex_data], sizes=[vertex_thickness] + ), + pygfx.PointsMarkerMaterial( + marker="square", + size=vertex_thickness, + color=vertex_color, + size_mode="vertex", + edge_color=vertex_color, + ), + ) + + bottom_right_vertex = pygfx.Points( + pygfx.Geometry( + positions=[bottom_right_vertex_data], sizes=[vertex_thickness] + ), + pygfx.PointsMarkerMaterial( + marker="square", + size=vertex_thickness, + color=vertex_color, + size_mode="vertex", + edge_color=vertex_color, + ), + ) + + self.vertices: Tuple[pygfx.Points, pygfx.Points, pygfx.Points, pygfx.Points] = ( + bottom_left_vertex, + bottom_right_vertex, + top_left_vertex, + top_right_vertex, + ) + + for vertex in self.vertices: + vertex.world.z = -0.25 + group.add(vertex) + + self._selection = RectangleSelectionFeature(selection, limits=self._limits) + + # include parent offset + if parent is not None: + offset = (parent.offset[0], parent.offset[1], 0) + else: + offset = (0, 0, 0) + + BaseSelector.__init__( + self, + edges=self.edges, + fill=(self.fill,), + vertices=self.vertices, + hover_responsive=(*self.edges, *self.vertices), + arrow_keys_modifier=arrow_keys_modifier, + parent=parent, + name=name, + offset=offset, + ) + + self._set_world_object(group) + + self.selection = selection + + def get_selected_data( + self, graphic: Graphic = None, mode: str = "full" + ) -> Union[np.ndarray, List[np.ndarray]]: + """ + Get the ``Graphic`` data bounded by the current selection. + Returns a view of the data array. + + If the ``Graphic`` is a collection, such as a ``LineStack``, it returns a list of views of the full array. + Can be performed on the ``parent`` Graphic or on another graphic by passing to the ``graphic`` arg. + + Parameters + ---------- + graphic: Graphic, optional, default ``None`` + if provided, returns the data selection from this graphic instead of the graphic set as ``parent`` + mode: str, default 'full' + One of 'full', 'partial', or 'ignore'. Indicates how selected data should be returned based on the + selectors position over the graphic. Only used for ``LineGraphic``, ``LineCollection``, and ``LineStack`` + | If 'full', will return all data bounded by the x and y limits of the selector even if partial indices + along one axis are not fully covered by the selector. + | If 'partial' will return only the data that is bounded by the selector, missing indices not bounded by the + selector will be set to NaNs + | If 'ignore', will only return data for graphics that have indices completely bounded by the selector + + Returns + ------- + np.ndarray or List[np.ndarray] + view or list of views of the full array, returns empty array if selection is empty + """ + source = self._get_source(graphic) + ixs = self.get_selected_indices(source) + + # do not need to check for mode for images, because the selector is bounded by the image shape + # will always be `full` + if "Image" in source.__class__.__name__: + col_ixs = slice(ixs[0][0], ixs[0][-1] + 1) + row_ixs = slice(ixs[1][0], ixs[1][-1] + 1) + + return source.data[row_ixs, col_ixs] + + if mode not in ["full", "partial", "ignore"]: + raise ValueError( + f"`mode` must be one of 'full', 'partial', or 'ignore', you have passed {mode}" + ) + if "Line" in source.__class__.__name__: + + if isinstance(source, GraphicCollection): + data_selections: List[np.ndarray] = list() + + for i, g in enumerate(source.graphics): + # want to keep same length as the original line collection + if ixs[i].size == 0: + data_selections.append( + np.array([], dtype=np.float32).reshape(0, 3) + ) + else: + # s gives entire slice of data along the x + s = slice( + ixs[i][0], ixs[i][-1] + 1 + ) # add 1 because these are direct indices + # slices n_datapoints dim + + # calculate missing ixs using set difference + # then calculate shift + missing_ixs = ( + np.setdiff1d(np.arange(ixs[i][0], ixs[i][-1] + 1), ixs[i]) + - ixs[i][0] + ) + + match mode: + # take all ixs, ignore missing + case "full": + data_selections.append(g.data[s]) + # set missing ixs data to NaNs + case "partial": + if len(missing_ixs) > 0: + data = g.data[s].copy() + data[missing_ixs] = np.nan + data_selections.append(data) + else: + data_selections.append(g.data[s]) + # ignore lines that do not have full ixs to start + case "ignore": + if len(missing_ixs) > 0: + data_selections.append( + np.array([], dtype=np.float32).reshape(0, 3) + ) + else: + data_selections.append(g.data[s]) + return data_selections + else: # for lines + if ixs.size == 0: + # empty selection + return np.array([], dtype=np.float32).reshape(0, 3) + + s = slice( + ixs[0], ixs[-1] + 1 + ) # add 1 to end because these are direct indices + # slices n_datapoints dim + # slice with min, max is faster than using all the indices + + # get missing ixs + missing_ixs = np.setdiff1d(np.arange(ixs[0], ixs[-1] + 1), ixs) - ixs[0] + + match mode: + # return all, do not care about missing + case "full": + return source.data[s] + # set missing to NaNs + case "partial": + if len(missing_ixs) > 0: + data = source.data[s].copy() + data[missing_ixs] = np.nan + return data + else: + return source.data[s] + # missing means nothing will be returned even if selector is partially over data + # warn the user and return empty + case "ignore": + if len(missing_ixs) > 0: + warnings.warn( + "You have selected 'ignore' mode. Selected graphic has incomplete indices. " + "Move the selector or change the mode to one of `partial` or `full`." + ) + return np.array([], dtype=np.float32) + else: + return source.data[s] + + def get_selected_indices( + self, graphic: Graphic = None + ) -> Union[np.ndarray, List[np.ndarray]]: + """ + Returns the indices of the ``Graphic`` data bounded by the current selection. + + These are the data indices which correspond to the data under the selector. + + Parameters + ---------- + graphic: Graphic, default ``None`` + If provided, returns the selection indices from this graphic instrad of the graphic set as ``parent`` + + Returns + ------- + Union[np.ndarray, List[np.ndarray]] + data indicies of the selection + | list of [x_indices_array, y_indices_array] if the graphic is an image + | list of indices along the x-dimension for each line if graphic is a line collection + | array of indices along the x-dimension if graphic is a line + """ + # get indices from source + source = self._get_source(graphic) + + # selector (xmin, xmax, ymin, ymax) values + bounds = self.selection + + # image data does not need to check for mode because the selector is always bounded + # to the image + if "Image" in source.__class__.__name__: + xs = np.arange(bounds[0], bounds[1], dtype=int) + ys = np.arange(bounds[2], bounds[3], dtype=int) + return [xs, ys] + + if "Line" in source.__class__.__name__: + if isinstance(source, GraphicCollection): + ixs = list() + for g in source.graphics: + data = g.data.value + g_ixs = np.where( + (data[:, 0] >= bounds[0] - g.offset[0]) + & (data[:, 0] <= bounds[1] - g.offset[0]) + & (data[:, 1] >= bounds[2] - g.offset[1]) + & (data[:, 1] <= bounds[3] - g.offset[1]) + )[0] + ixs.append(g_ixs) + else: + # map only this graphic + data = source.data.value + ixs = np.where( + (data[:, 0] >= bounds[0]) + & (data[:, 0] <= bounds[1]) + & (data[:, 1] >= bounds[2]) + & (data[:, 1] <= bounds[3]) + )[0] + + return ixs + + def _move_graphic(self, delta: np.ndarray): + + # new selection positions + xmin_new = self.selection[0] + delta[0] + xmax_new = self.selection[1] + delta[0] + ymin_new = self.selection[2] + delta[1] + ymax_new = self.selection[3] + delta[1] + + # move entire selector if source is fill + if self._move_info.source == self.fill: + if self.selection[0] == self.limits[0] and xmin_new < self.limits[0]: + return + if self.selection[1] == self.limits[1] and xmax_new > self.limits[1]: + return + if self.selection[2] == self.limits[2] and ymin_new < self.limits[2]: + return + if self.selection[3] == self.limits[3] and ymax_new > self.limits[3]: + return + # set thew new bounds + self._selection.set_value(self, (xmin_new, xmax_new, ymin_new, ymax_new)) + return + + # if selector not resizable return + if not self._resizable: + return + + xmin, xmax, ymin, ymax = self.selection + + if self._move_info.source == self.vertices[0]: # bottom left + self._selection.set_value(self, (xmin_new, xmax, ymin_new, ymax)) + if self._move_info.source == self.vertices[1]: # bottom right + self._selection.set_value(self, (xmin, xmax_new, ymin_new, ymax)) + if self._move_info.source == self.vertices[2]: # top left + self._selection.set_value(self, (xmin_new, xmax, ymin, ymax_new)) + if self._move_info.source == self.vertices[3]: # top right + self._selection.set_value(self, (xmin, xmax_new, ymin, ymax_new)) + # if event source was an edge and selector is resizable, move the edge that caused the event + if self._move_info.source == self.edges[0]: + self._selection.set_value(self, (xmin_new, xmax, ymin, ymax)) + if self._move_info.source == self.edges[1]: + self._selection.set_value(self, (xmin, xmax_new, ymin, ymax)) + if self._move_info.source == self.edges[2]: + self._selection.set_value(self, (xmin, xmax, ymin_new, ymax)) + if self._move_info.source == self.edges[3]: + self._selection.set_value(self, (xmin, xmax, ymin, ymax_new)) diff --git a/fastplotlib/graphics/selectors/_rectangle_region.py b/fastplotlib/graphics/selectors/_rectangle_region.py deleted file mode 100644 index bc2cad5b1..000000000 --- a/fastplotlib/graphics/selectors/_rectangle_region.py +++ /dev/null @@ -1,355 +0,0 @@ -from typing import * -import numpy as np - -import pygfx - -from ...utils import mesh_masks -from .._base import Graphic -from .._features import GraphicFeature -from ._base_selector import BaseSelector - - -class RectangleBoundsFeature(GraphicFeature): - """ - Feature for a linearly bounding region - - **event pick info** - - +--------------------+-------------------------------+--------------------------------------------------------------------------------------+ - | key | type | description | - +====================+===============================+======================================================================================+ - | "selected_indices" | ``numpy.ndarray`` or ``None`` | selected graphic data indices | - | "selected_data" | ``numpy.ndarray`` or ``None`` | selected graphic data | - | "new_data" | ``(float, float)`` | current bounds in world coordinates, NOT necessarily the same as "selected_indices". | - +--------------------+-------------------------------+--------------------------------------------------------------------------------------+ - - """ - - def __init__( - self, parent, bounds: Tuple[int, int], axis: str, limits: Tuple[int, int] - ): - super().__init__(parent, data=bounds) - - self._axis = axis - self.limits = limits - - self._set(bounds) - - @property - def axis(self) -> str: - """one of "x" | "y" """ - return self._axis - - def _set(self, value: Tuple[float, float, float, float]): - """ - - Parameters - ---------- - value: Tuple[float] - new values: (xmin, xmax, ymin, ymax) - - Returns - ------- - - """ - xmin, xmax, ymin, ymax = value - - # TODO: make sure new values do not exceed limits - - # change fill mesh - # change left x position of the fill mesh - self._parent.fill.geometry.positions.data[mesh_masks.x_left] = xmin - - # change right x position of the fill mesh - self._parent.fill.geometry.positions.data[mesh_masks.x_right] = xmax - - # change bottom y position of the fill mesh - self._parent.fill.geometry.positions.data[mesh_masks.y_bottom] = ymin - - # change top position of the fill mesh - self._parent.fill.geometry.positions.data[mesh_masks.y_top] = ymax - - # change the edge lines - - # each edge line is defined by two end points which are stored in the - # geometry.positions - # [x0, y0, z0] - # [x1, y1, z0] - - # left line - z = self._parent.edges[0].geometry.positions.data[:, -1][0] - self._parent.edges[0].geometry.positions.data[:] = np.array( - [[xmin, ymin, z], [xmin, ymax, z]] - ) - - # right line - self._parent.edges[1].geometry.positions.data[:] = np.array( - [[xmax, ymin, z], [xmax, ymax, z]] - ) - - # bottom line - self._parent.edges[2].geometry.positions.data[:] = np.array( - [[xmin, ymin, z], [xmax, ymin, z]] - ) - - # top line - self._parent.edges[3].geometry.positions.data[:] = np.array( - [[xmin, ymax, z], [xmax, ymax, z]] - ) - - self._data = value # (value[0], value[1]) - - # send changes to GPU - self._parent.fill.geometry.positions.update_range() - - for edge in self._parent.edges: - edge.geometry.positions.update_range() - - # calls any events - self._feature_changed(key=None, new_data=value) - - # TODO: feature_changed - def _feature_changed(self, key: Union[int, slice, Tuple[slice]], new_data: Any): - return - # if len(self._event_handlers) < 1: - # return - # - # if self._parent.parent is not None: - # selected_ixs = self._parent.get_selected_indices() - # selected_data = self._parent.get_selected_data() - # else: - # selected_ixs = None - # selected_data = None - # - # pick_info = { - # "index": None, - # "collection-index": self._collection_index, - # "world_object": self._parent.world_object, - # "new_data": new_data, - # "selected_indices": selected_ixs, - # "selected_data": selected_data - # "graphic", - # "delta", - # "pygfx_event" - # } - # - # event_data = FeatureEvent(type="bounds", pick_info=pick_info) - # - # self._call_event_handlers(event_data) - - -class RectangleRegionSelector(Graphic, BaseSelector): - feature_events = "bounds" - - def __init__( - self, - bounds: Tuple[int, int, int, int], - limits: Tuple[int, int], - origin: Tuple[int, int], - axis: str = "x", - parent: Graphic = None, - resizable: bool = True, - fill_color=(0, 0, 0.35), - edge_color=(0.8, 0.8, 0), - arrow_keys_modifier: str = "Shift", - name: str = None, - ): - """ - Create a LinearRegionSelector graphic which can be moved only along either the x-axis or y-axis. - Allows sub-selecting data from a ``Graphic`` or from multiple Graphics. - - bounds[0], limits[0], and position[0] must be identical - - Parameters - ---------- - bounds: (int, int, int, int) - the initial bounds of the rectangle, ``(x_min, x_max, y_min, y_max)`` - - limits: (int, int, int, int) - limits of the selector, ``(x_min, x_max, y_min, y_max)`` - - origin: (int, int) - initial position of the selector - - axis: str, default ``None`` - Restrict the selector to the "x" or "y" axis. - If the selector is restricted to an axis you cannot change the bounds along the other axis. For example, - if you set ``axis="x"``, then the ``y_min``, ``y_max`` values of the bounds will stay constant. - - parent: Graphic, default ``None`` - associate this selector with a parent Graphic - - resizable: bool - if ``True``, the edges can be dragged to resize the selection - - fill_color: str, array, or tuple - fill color for the selector, passed to pygfx.Color - - edge_color: str, array, or tuple - edge color for the selector, passed to pygfx.Color - - name: str - name for this selector graphic - """ - - # lots of very close to zero values etc. so round them - bounds = tuple(map(round, bounds)) - limits = tuple(map(round, limits)) - origin = tuple(map(round, origin)) - - Graphic.__init__(self, name=name) - - self.parent = parent - - # world object for this will be a group - # basic mesh for the fill area of the selector - # line for each edge of the selector - group = pygfx.Group() - self._set_world_object(group) - - xmin, xmax, ymin, ymax = bounds - - width = xmax - xmin - height = ymax - ymin - - self.fill = pygfx.Mesh( - pygfx.box_geometry(width, height, 1), - pygfx.MeshBasicMaterial(color=pygfx.Color(fill_color), pick_write=True), - ) - - self.fill.position.set(*origin, -2) - self.world_object.add(self.fill) - - # position data for the left edge line - left_line_data = np.array( - [ - [origin[0], (-height / 2) + origin[1], 0.5], - [origin[0], (height / 2) + origin[1], 0.5], - ] - ).astype(np.float32) - - left_line = pygfx.Line( - pygfx.Geometry(positions=left_line_data), - pygfx.LineMaterial(thickness=3, color=edge_color), - ) - - # position data for the right edge line - right_line_data = np.array( - [ - [bounds[1], (-height / 2) + origin[1], 0.5], - [bounds[1], (height / 2) + origin[1], 0.5], - ] - ).astype(np.float32) - - right_line = pygfx.Line( - pygfx.Geometry(positions=right_line_data), - pygfx.LineMaterial(thickness=3, color=edge_color), - ) - - # position data for the left edge line - bottom_line_data = np.array( - [ - [(-width / 2) + origin[0], origin[1], 0.5], - [(width / 2) + origin[0], origin[1], 0.5], - ] - ).astype(np.float32) - - bottom_line = pygfx.Line( - pygfx.Geometry(positions=bottom_line_data), - pygfx.LineMaterial(thickness=3, color=edge_color), - ) - - # position data for the right edge line - top_line_data = np.array( - [ - [(-width / 2) + origin[0], bounds[1], 0.5], - [(width / 2) + origin[0], bounds[1], 0.5], - ] - ).astype(np.float32) - - top_line = pygfx.Line( - pygfx.Geometry(positions=top_line_data), - pygfx.LineMaterial(thickness=3, color=edge_color), - ) - - self.edges: Tuple[pygfx.Line, ...] = ( - left_line, - right_line, - bottom_line, - top_line, - ) # left line, right line, bottom line, top line - - # add the edge lines - for edge in self.edges: - edge.position.set(*origin, -1) - self.world_object.add(edge) - - self._resizable = resizable - self._bounds = RectangleBoundsFeature(self, bounds, axis=axis, limits=limits) - - BaseSelector.__init__( - self, - edges=self.edges, - fill=(self.fill,), - hover_responsive=self.edges, - arrow_keys_modifier=arrow_keys_modifier, - axis=axis, - ) - - @property - def bounds(self) -> RectangleBoundsFeature: - """ - (xmin, xmax, ymin, ymax) The current bounds of the selection in world space. - - These bounds will NOT necessarily correspond to the indices of the data that are under the selection. - Use ``get_selected_indices()` which maps from world space to data indices. - """ - return self._bounds - - def _move_graphic(self, delta): - # new left bound position - xmin_new = Vector3(self.bounds()[0]).add(delta).x - - # new right bound position - xmax_new = Vector3(self.bounds()[1]).add(delta).x - - # new bottom bound position - ymin_new = Vector3(0, self.bounds()[2]).add(delta).y - - # new top bound position - ymax_new = Vector3(0, self.bounds()[3]).add(delta).y - - # move entire selector if source was fill - if self._move_info.source == self.fill: - # set the new bounds - self.bounds = (xmin_new, xmax_new, ymin_new, ymax_new) - return - - # if selector is not resizable do nothing - if not self._resizable: - return - - # if resizable, move edges - - xmin, xmax, ymin, ymax = self.bounds() - - # change only left bound - if self._move_info.source == self.edges[0]: - xmin = xmin_new - - # change only right bound - elif self._move_info.source == self.edges[1]: - xmax = xmax_new - - # change only bottom bound - elif self._move_info.source == self.edges[2]: - ymin = ymin_new - - # change only top bound - elif self._move_info.source == self.edges[3]: - ymax = ymax_new - else: - return - - # set the new bounds - self.bounds = (xmin, xmax, ymin, ymax) From 71a6dfa553031ffe23fa9e97b53160abbea792cf Mon Sep 17 00:00:00 2001 From: Caitlin Lewis <69729525+clewis7@users.noreply.github.com> Date: Sun, 1 Sep 2024 13:21:51 -0400 Subject: [PATCH 103/185] add lorenz attractor example (#595) * add lorenz example * requested changes --- examples/desktop/misc/lorenz_animation.py | 92 +++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 examples/desktop/misc/lorenz_animation.py diff --git a/examples/desktop/misc/lorenz_animation.py b/examples/desktop/misc/lorenz_animation.py new file mode 100644 index 000000000..cf7a77b38 --- /dev/null +++ b/examples/desktop/misc/lorenz_animation.py @@ -0,0 +1,92 @@ +""" +Lorenz System Animation +======================= + +Example of the Lorenz attractor. +""" + +# test_example = false +# sphinx_gallery_pygfx_docs = 'animate 10s' + +import fastplotlib as fpl +import numpy as np + + +# generate data +def lorenz(xyz, *, s=10, r=28, b=2.667): + """ + Parameters + ---------- + xyz : array-like, shape (3,) + Point of interest in three-dimensional space. + s, r, b : float + Parameters defining the Lorenz attractor. + + Returns + ------- + xyz_dot : array, shape (3,) + Values of the Lorenz attractor's partial derivatives at *xyz*. + """ + x, y, z = xyz + x_dot = s * (y - x) + y_dot = r * x - y - x * z + z_dot = x * y - b * z + return np.array([x_dot, y_dot, z_dot]) + + +dt = 0.01 +num_steps = 3_000 + +lorenz_data = np.empty((5, num_steps + 1, 3)) + +for i in range(5): + xyzs = np.empty((num_steps + 1, 3)) # Need one more for the initial values + xyzs[0] = (0., (i * 0.3) + 1, 1.05) # Set initial values + # Step through "time", calculating the partial derivatives at the current point + # and using them to estimate the next point + for j in range(num_steps): + xyzs[j + 1] = xyzs[j] + lorenz(xyzs[j]) * dt + + lorenz_data[i] = xyzs + +figure = fpl.Figure( + cameras="3d", + controller_types="fly" +) + +lorenz_line = figure[0, 0].add_line_collection(data=lorenz_data, thickness=.1, cmap="tab10") + +scatter_markers = list() + +for graphic in lorenz_line: + marker = figure[0, 0].add_scatter(graphic.data.value[0], sizes=8, colors=graphic.colors[0]) + scatter_markers.append(marker) + +# initialize time +time = 0 + + +def animate(supblot): + global time + + time += 2 + + if time >= xyzs.shape[0]: + time = 0 + + for scatter, g in zip(scatter_markers, lorenz_line): + scatter.data = g.data.value[time] + + +figure[0, 0].add_animations(animate) + +figure.show() + +# set initial camera position to make animation in gallery render better +figure[0, 0].camera.world.z = 75 + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.run() From 4170de614057fe7a49dd7d3e9b90103a3b719a90 Mon Sep 17 00:00:00 2001 From: Caitlin Lewis <69729525+clewis7@users.noreply.github.com> Date: Tue, 3 Sep 2024 14:10:06 -0400 Subject: [PATCH 104/185] add FAQ (#559) * add faq * requested changes * more faq * Update faq.rst * fix intersphinx link, fix typo * add more faq questions * small edits and remove dropdowns * requested changes * requested changes * revert commit * should be gtg --------- Co-authored-by: Caitlin Lewis --- docs/source/index.rst | 3 +- docs/source/user_guide/faq.rst | 125 +++++++++++++++++++++++++++++++ docs/source/user_guide/guide.rst | 2 +- 3 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 docs/source/user_guide/faq.rst diff --git a/docs/source/index.rst b/docs/source/index.rst index cf752a83b..f855569e3 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -3,10 +3,11 @@ Welcome to fastplotlib's documentation! .. toctree:: :caption: User Guide - :maxdepth: 2 + :maxdepth: 1 Guide GPU Info + FAQ .. toctree:: :maxdepth: 1 diff --git a/docs/source/user_guide/faq.rst b/docs/source/user_guide/faq.rst new file mode 100644 index 000000000..aa4f3dc87 --- /dev/null +++ b/docs/source/user_guide/faq.rst @@ -0,0 +1,125 @@ +fastplotlib FAQ +=============== + +What is `fastplotlib`? +---------------------- + + `fastplotlib` is a scientific plotting library built on top of the `pygfx `_ rendering engine + that leverages new graphics APIs and modern GPU hardware to create fast and interactive visualizations. + + +What can I do with `fastplotlib`? +--------------------------------- + + `fastplotlib` allows for: + - GPU accelerated visualization + - interactive visualization via an intuitive and expressive API + - rapid prototyping and algorithm design + - easy exploration and fast rendering of large-scale data + - design, develop, evaluate and ship machine learning models + - create visualizations for real-time acquisition systems for scientific instruments (cameras, etc.) + +Do I need a GPU? +---------------- + + Integrated GPUs, such as those found in modern laptops, are sufficient for many use cases. + + For the best performance you will require a dedicated GPU. You can think of it like running a game, a more complex visualization or faster rendering will require a better GPU. + + Limited software rendering using just the CPU is supported on linux using lavapipe, but this is mostly only useful for testing purposes. + +How does `fastplotlib` relate to `matplotlib`? +---------------------------------------------- + + `fastplotlib` is **not** related to `matplotlib` in any way. + + These are two completely different libraries with their own APIs and use-cases. The `fastplotlib` library is primarily for *interactive* + visualization that runs on the GPU using WGPU. The `fastplotlib` architecture is completely different from `matplotlib`. Using `fastplotlib` + is more akin to using `numpy`. + + To expand on this a bit more, the `pygfx` buffer interface is really unlike anything in`matplotlib` and other libraries which is a major reason + why `fastplotlib` can have an array-like API for plotting. We believe that these design choices make it much easier to learn how to use the library + and provide fine-grained control over your visualizations. See the "How can I learn to use `fastplotlib`?" section below. + +How can I learn to use `fastplotlib`? +------------------------------------- + + We want `fastplotlib` to be easy to learn and use. To get started with the library we recommend taking a look at our `guide `_ and + `examples gallery `_. + + In general, if you are familiar with numpy and array notation you will already have a intuitive understanding of interacting + with your data in `fastplotlib`. If you have any questions, please do not hesitate to post an issue or discussion forum post. + +Should I use `fastplotlib` for making publication figures? +---------------------------------------------------------- + + While `fastplotlib` figures can be exported to PNG using ``figure.export()``, `fastplotlib` is not intended for creating *static* + publication figures. There are many other libraries that are well-suited for this task. + +How does `fastplotlib` handle data loading? +------------------------------------------- + + `fastplotlib` is a plotting library and not a data handling or data loading library. These tasks are outside of the scope of + the library. + + In general, if your data is an array-like object, `fastplotlib` should be able to use it. However, if you have any problems using your data objects, + please do not hesitate to post an issue! See this `issue `_ for more details. + +What is the scope of `fastplotlib`? +----------------------------------- + + While the capabilities are very far-reaching, we would like to emphasize that `fastplotlib` is a general-purpose plotting library focused on scientific visualization. + More specifically, we aim to develop the tools necessary for users to build fast and interactive visualizations for a variety of scientific domains including but not limited to + neuroscience, astronomy, biology, computer vision, signal processing, and more. If you have a particular feature in mind that you feel is missing, please post an issue and we will respond + accordingly letting you know if it fits within the scope of the project. + +What types of PRs are we willing to accept? +------------------------------------------- + + Primarily the features of `fastplotlib` have been developed as they relate to the core-developers research use cases (mostly neuroscience, algorithm development, and machine learning). With that being said, there are many domains in which + we do not have the knowledge to best-implement the tools needed for proper visualization. We welcome all PRs that address these types of missing functionality. We + recommend taking a look at our `Roadmap `_ to get a better idea of what those items might be :D + + Closely related to this, we would love to add more examples to our repo for different types of scientific visualizations. We welcome all PRs that showcase using `fastplotlib` for + your given research domain! :D + + Lastly, documentation is a critical part of open-source software and makes learning/using our tool much easier. We welcome all PRs that add missing or needed documentation of the + codebase. If you find a piece of the codebase that is confusing or does not have proper documentation, please also feel free to post an issue on the repo! + +What frameworks does `fastplotlib` support? +------------------------------------------- + + The short answer is that `fastplotlib` can run on anything that `pygfx` runs on. This includes, + - `jupyter lab` using `jupyter_rfb` + - `PyQt` and `PySide` + - `glfw` + - `wxPython` + + Note: Use in Google Colab is not highly functional. We recommend using an inexpensive alternative cloud provider + such as CodeOcean or Lambda Cloud. We have tested these and `fastplotlib` works very well. + +How can I use `fastplotlib` interactively? +------------------------------------------ + + There are multiple ways to use fastplotlib interactively. + + 1. Jupyter + + On jupyter lab the jupyter backend (i.e. jupyter_rfb) is normally selected. This works via client-server rendering. + Images generated on the server are streamed to the client (Jupyter) via a jpeg byte stream. Events (such as mouse or keyboard events) + are then streamed in the opposite direction prompting new images to be generated by the server if necessary. + This remote-frame-buffer approach makes the rendering process very fast. `fastplotlib` viusalizations can be displayed + in cell output or on the side using sidecar. + + A Qt backend can also optionally be used as well. If %gui qt is selected before importing `fastplotlib` then this + backend will be used instead. + + Lastly, users can also force using glfw by specifying this as an argument when instantiating a + Figure (i.e. Figure(canvas="gflw"). + + **Note:** Do not mix between gui backends. For example, if you start the notebook using Qt, do not attempt to + force using another backend such as jupyter_rfb later. + + 2. IPython + + Users can select between using a Qt backend or glfw using the same methods as above. \ No newline at end of file diff --git a/docs/source/user_guide/guide.rst b/docs/source/user_guide/guide.rst index 4ee374389..df0c54c78 100644 --- a/docs/source/user_guide/guide.rst +++ b/docs/source/user_guide/guide.rst @@ -20,7 +20,7 @@ or install the bleeding edge from Github: What is ``fastplotlib``? ------------------------ -``fastplotlib`` is a cutting-edge plotting library built using the ```pygfx`` `_ rendering engine. +``fastplotlib`` is a cutting-edge plotting library built using the `pygfx `_ rendering engine. The lower-level details of the rendering process (i.e. defining a scene, camera, renderer, etc.) are abstracted away, allowing users to focus on their data. The fundamental goal of ``fastplotlib`` is to provide a high-level, expressive API that promotes large-scale explorative scientific visualization. We want to make it easy and intuitive to produce interactive visualizations that are as performant and vibrant as a modern video game 😄 From 9605d066c40203d8b5b72bfd48324839b77f1ae5 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Thu, 12 Sep 2024 11:57:38 -0400 Subject: [PATCH 105/185] Fix move to pointer for selectors again (#601) * LinearSelectionFeature uses np.float32 for selection type * move_to_pointer which works for both linear and linear region selectors * skip move_to_pointer for rectangle selector for now --- fastplotlib/graphics/_features/_selection_features.py | 4 ++-- fastplotlib/graphics/selectors/_base_selector.py | 11 ++++++++++- fastplotlib/graphics/selectors/_rectangle.py | 3 +++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/fastplotlib/graphics/_features/_selection_features.py b/fastplotlib/graphics/_features/_selection_features.py index e499e72c9..c385f820f 100644 --- a/fastplotlib/graphics/_features/_selection_features.py +++ b/fastplotlib/graphics/_features/_selection_features.py @@ -48,7 +48,7 @@ def __init__(self, axis: str, value: float, limits: tuple[float, float]): self._value = value @property - def value(self) -> float: + def value(self) -> np.float32: """ selection, data x or y value """ @@ -56,7 +56,7 @@ def value(self) -> float: def set_value(self, selector, value: float): # clip value between limits - value = np.clip(value, self._limits[0], self._limits[1]) + value = np.clip(value, self._limits[0], self._limits[1], dtype=np.float32) # set position if self._axis == "x": diff --git a/fastplotlib/graphics/selectors/_base_selector.py b/fastplotlib/graphics/selectors/_base_selector.py index 3729adda5..30643bbe4 100644 --- a/fastplotlib/graphics/selectors/_base_selector.py +++ b/fastplotlib/graphics/selectors/_base_selector.py @@ -280,7 +280,16 @@ def _move_to_pointer(self, ev): elif self.axis == "y": offset = self.offset[1] - current_pos_world: np.ndarray = self.selection + offset + if self.selection.size > 1: + # linear region selectors + # TODO: get center for rectangle and polygon selectors + center = self.selection.mean(axis=0) + + else: + # linear selectors + center = self.selection + + current_pos_world: np.ndarray = center + offset world_pos = self._plot_area.map_screen_to_world(ev) diff --git a/fastplotlib/graphics/selectors/_rectangle.py b/fastplotlib/graphics/selectors/_rectangle.py index aae99803e..38bb0d2f9 100644 --- a/fastplotlib/graphics/selectors/_rectangle.py +++ b/fastplotlib/graphics/selectors/_rectangle.py @@ -515,3 +515,6 @@ def _move_graphic(self, delta: np.ndarray): self._selection.set_value(self, (xmin, xmax, ymin_new, ymax)) if self._move_info.source == self.edges[3]: self._selection.set_value(self, (xmin, xmax, ymin, ymax_new)) + + def _move_to_pointer(self, ev): + pass From 662108c3baf3a507adcdd106b08a9ddffde12a1c Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Tue, 17 Sep 2024 11:44:55 -0400 Subject: [PATCH 106/185] pin pygfx>=0.4.0 (#607) --- setup.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/setup.py b/setup.py index abebbb4c4..6f7f64468 100644 --- a/setup.py +++ b/setup.py @@ -4,8 +4,7 @@ install_requires = [ "numpy>=1.23.0", - "wgpu>=0.16.0", - "pygfx>=0.3.0", + "pygfx>=0.4.0", "cmap>=0.1.3", ] From 1d38861aefa97831079bba824fd1f2a7c77b5409 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Tue, 17 Sep 2024 12:15:59 -0400 Subject: [PATCH 107/185] Fix rect selector (#609) * fix rectangle selector get_selected_data() for images --- fastplotlib/graphics/selectors/_rectangle.py | 37 ++++++++++---------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/fastplotlib/graphics/selectors/_rectangle.py b/fastplotlib/graphics/selectors/_rectangle.py index 38bb0d2f9..02e5ec6b3 100644 --- a/fastplotlib/graphics/selectors/_rectangle.py +++ b/fastplotlib/graphics/selectors/_rectangle.py @@ -18,7 +18,7 @@ def parent(self) -> Graphic | None: return self._parent @property - def selection(self) -> Sequence[float] | List[Sequence[float]]: + def selection(self) -> np.ndarray[float]: """ (xmin, xmax, ymin, ymax) of the rectangle selection """ @@ -319,10 +319,11 @@ def get_selected_data( # do not need to check for mode for images, because the selector is bounded by the image shape # will always be `full` if "Image" in source.__class__.__name__: - col_ixs = slice(ixs[0][0], ixs[0][-1] + 1) - row_ixs = slice(ixs[1][0], ixs[1][-1] + 1) + row_ixs, col_ixs = ixs + row_slice = slice(row_ixs[0], row_ixs[-1] + 1) + col_slice = slice(col_ixs[0], col_ixs[-1] + 1) - return source.data[row_ixs, col_ixs] + return source.data[row_slice, col_slice] if mode not in ["full", "partial", "ignore"]: raise ValueError( @@ -414,7 +415,7 @@ def get_selected_data( def get_selected_indices( self, graphic: Graphic = None - ) -> Union[np.ndarray, List[np.ndarray]]: + ) -> np.ndarray | tuple[np.ndarray]: """ Returns the indices of the ``Graphic`` data bounded by the current selection. @@ -429,7 +430,7 @@ def get_selected_indices( ------- Union[np.ndarray, List[np.ndarray]] data indicies of the selection - | list of [x_indices_array, y_indices_array] if the graphic is an image + | tuple of [row_indices, y_indices_array] if the graphic is an image | list of indices along the x-dimension for each line if graphic is a line collection | array of indices along the x-dimension if graphic is a line """ @@ -437,14 +438,14 @@ def get_selected_indices( source = self._get_source(graphic) # selector (xmin, xmax, ymin, ymax) values - bounds = self.selection + xmin, xmax, ymin, ymax = self.selection # image data does not need to check for mode because the selector is always bounded # to the image if "Image" in source.__class__.__name__: - xs = np.arange(bounds[0], bounds[1], dtype=int) - ys = np.arange(bounds[2], bounds[3], dtype=int) - return [xs, ys] + col_ixs = np.arange(xmin, xmax, dtype=int) + row_ixs = np.arange(ymin, ymax, dtype=int) + return row_ixs, col_ixs if "Line" in source.__class__.__name__: if isinstance(source, GraphicCollection): @@ -452,20 +453,20 @@ def get_selected_indices( for g in source.graphics: data = g.data.value g_ixs = np.where( - (data[:, 0] >= bounds[0] - g.offset[0]) - & (data[:, 0] <= bounds[1] - g.offset[0]) - & (data[:, 1] >= bounds[2] - g.offset[1]) - & (data[:, 1] <= bounds[3] - g.offset[1]) + (data[:, 0] >= xmin - g.offset[0]) + & (data[:, 0] <= xmax - g.offset[0]) + & (data[:, 1] >= ymin - g.offset[1]) + & (data[:, 1] <= ymax - g.offset[1]) )[0] ixs.append(g_ixs) else: # map only this graphic data = source.data.value ixs = np.where( - (data[:, 0] >= bounds[0]) - & (data[:, 0] <= bounds[1]) - & (data[:, 1] >= bounds[2]) - & (data[:, 1] <= bounds[3]) + (data[:, 0] >= xmin) + & (data[:, 0] <= xmax) + & (data[:, 1] >= ymin) + & (data[:, 1] <= ymax) )[0] return ixs From fe6bcf89b02f6ecf557d2cc980e398fc7cd02d88 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Wed, 18 Sep 2024 11:39:47 -0400 Subject: [PATCH 108/185] do get_ipython() only once in import (#611) * get_ipython() only once in imports * black --- fastplotlib/layouts/_plot_area.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/fastplotlib/layouts/_plot_area.py b/fastplotlib/layouts/_plot_area.py index e5cf1a74b..133526115 100644 --- a/fastplotlib/layouts/_plot_area.py +++ b/fastplotlib/layouts/_plot_area.py @@ -15,11 +15,13 @@ try: - ip = get_ipython() + get_ipython() except NameError: IS_IPYTHON = False + IPYTHON = None else: IS_IPYTHON = True + IPYTHON = get_ipython() class PlotArea: @@ -673,15 +675,13 @@ def delete_graphic(self, graphic: Graphic): if IS_IPYTHON: # remove any references that ipython might have made - ip = get_ipython() - # check both namespaces - for namespace in [ip.user_ns, ip.user_ns_hidden]: + for namespace in [IPYTHON.user_ns, IPYTHON.user_ns_hidden]: # find the reference for ref, obj in namespace.items(): if graphic is obj: # we found the reference, remove from ipython - ip.del_var(ref) + IPYTHON.del_var(ref) break def clear(self): From ab5b787fb7bd1280b18b5d2724ef3b838ba03a6d Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Sun, 29 Sep 2024 13:18:43 -0400 Subject: [PATCH 109/185] start imgui implementation (#571) * ndarray.ptp -> np.ptp for numpy v2 * use cmap library for colormaps (#390) * example with cmap * add to deps * fix shape * use full map * remove break * undo changes to screenshots * fix parse * remove diffs * remove x.py * minimize diff * add docstring * remove colormaps --------- Co-authored-by: Kushal Kolar * fix pygfx docs move * black * start imgui implementation * right click menu, hard-code toolbar height * happy with right click menu * black * remove old id counter global * cmap picker menu working, going to use cmap next to get textures working * remove unused texture sampler * black * use selectable instead of menu_item * bug and filter out wheel events too * sort cmaps by category * imgui imagewidget sliders working * reset vmin vmax * image widget with imgui done * image widget video example * better organization of kwargs for EdgeWindow * add custom imgui with imagewidget example * remove old stuff from subplot * canvas kwargs to set initial size * test examples works with imgui, but image widget sliders cut off for some reason * screenshot tests work with imgui * reset viewports when EdgeWindows added * no more output contexts in Figure * no more output contexts :D * cleanup imagewidget.show() * rename standard right click menu * update examples * docstrings * better edge window * simplify example * cleanup * more cleanup * modify for docs * modify API docs * docstrings * docstrings * add UI To docs * docs * add UI to docs, more * UI section in docs gallery * docs gallery dir in conf * add imgui-bundle to setup.py * black * bugfix * use cmap library for colormaps (#390) * example with cmap * add to deps * fix shape * use full map * remove break * undo changes to screenshots * fix parse * remove diffs * remove x.py * minimize diff * add docstring * remove colormaps --------- Co-authored-by: Kushal Kolar * fix pygfx docs move * black * start imgui implementation * right click menu, hard-code toolbar height * happy with right click menu * black * remove old id counter global * cmap picker menu working, going to use cmap next to get textures working * remove unused texture sampler * black * use selectable instead of menu_item * bug and filter out wheel events too * sort cmaps by category * imgui imagewidget sliders working * reset vmin vmax * image widget with imgui done * image widget video example * better organization of kwargs for EdgeWindow * add custom imgui with imagewidget example * remove old stuff from subplot * canvas kwargs to set initial size * test examples works with imgui, but image widget sliders cut off for some reason * screenshot tests work with imgui * reset viewports when EdgeWindows added * no more output contexts in Figure * no more output contexts :D * cleanup imagewidget.show() * rename standard right click menu * update examples * docstrings * better edge window * simplify example * cleanup * more cleanup * modify for docs * modify API docs * docstrings * docstrings * add UI To docs * docs * add UI to docs, more * UI section in docs gallery * docs gallery dir in conf * add imgui-bundle to setup.py * black * bugfix * basic imgui example * finish basic custom imgui example * start with zero noise so screenshots always match * reset button * comments * plain Figure.get_pygfx_render_area() * add get_pygfx_render_area() to basic Figure * option to use basic Figure even if imgui installed * image widget examples in regular examples * black, cleanup * image widget event when index changes * imgui toolbar can be hidden * black * more black * update api docs * doc * comments iw imgui sliders * comment * fix * comments * black * update iw test nb w.r.t. imgui changes * add iw dir to tests * api docs * edit api docs stuff * readme to iw examples for docs * docs conf.py * stuff to make sure gui examples render in docs * image[pyav] for docs * fix iw movie example * move stuff in docstring * black * smaller vid example for iw * delete other heatmap examples for now * smaller heatmap * very small heatmap * new screenshots with imgui * add imgui-bundle to desktop tests * are selectors causing OOM issues with docs * actually bring back guis * remove selectors * remove misc also * settable texture limit for 2D textures, texture test with smaller RAM footprint * heatmap with wgpu limits set * for examples gallery limit must be set in docs conf.py * set texture limit for examples tests * wgpu limits set in conftest.py * fix linked controllers change with imgui * autouse texture limit setup for pytest session * correct way to set wgpu limits for pytest session * can we now get away with phree github actions * random seed so test screenshot looks the same * update heatmap and iw videos test screenshots * remove heatmap nb, add note in heatmap example * add back selector examples * remove linear selectors example see if docs build * only lines for linear selector example * separate example for image selector to see if it doesn't overrun RAM * use real image for linear seletor * figuring out wtf is wrong with image linear selctor on rtd * remove zoom, IDK know anymore, wtf is up with rtd * idk wtf is going on, makes no senze, just going to show code and not render image linear selector example for no * bring back misc examples * heavily subsample cockatoo vid because sphinx gallery has a memory leak :/ * iw video examples play * add imagewidget grid to docs gallery * proper imgui render for docs * add subsampled iw video screenshots * add image widget grid example * tweak lorenz example * added the wrong screenshot * bring back linear selector image example * update examples * update setup.py * I a comma * slider tweaks * update user guide * update doc for image widget example * fix guide * update guide images * other update docs --------- Co-authored-by: Talley Lambert --- .github/workflows/ci.yml | 10 +- .github/workflows/screenshots.yml | 2 +- docs/source/_static/click_event.gif | 3 - docs/source/_static/guide_animation.gif | 3 - docs/source/_static/guide_animation.webp | Bin 0 -> 959738 bytes docs/source/_static/guide_click_event.webp | Bin 0 -> 289476 bytes docs/source/_static/guide_hello_world.png | 4 +- .../source/_static/guide_hello_world_data.png | 3 - .../guide_hello_world_fancy_slicing.png | 3 + .../guide_hello_world_simple_slicing.png | 3 + .../source/_static/guide_hello_world_vmax.png | 4 +- docs/source/_static/guide_image_widget.gif | 3 - docs/source/_static/guide_image_widget.webp | Bin 0 -> 731496 bytes docs/source/_static/guide_imgui.png | 3 + docs/source/_static/guide_linear_selector.gif | 3 - .../source/_static/guide_linear_selector.webp | Bin 0 -> 372418 bytes docs/source/api/graphics/ImageGraphic.rst | 1 + docs/source/api/graphics/LineCollection.rst | 1 + docs/source/api/graphics/LineGraphic.rst | 1 + docs/source/api/graphics/LineStack.rst | 1 + docs/source/api/graphics/ScatterGraphic.rst | 1 + docs/source/api/graphics/TextGraphic.rst | 1 + docs/source/api/layouts/figure.rst | 6 +- docs/source/api/layouts/imgui_figure.rst | 49 +++ docs/source/api/layouts/subplot.rst | 2 + .../api/selectors/LinearRegionSelector.rst | 1 + docs/source/api/selectors/LinearSelector.rst | 1 + .../api/selectors/RectangleSelector.rst | 1 + docs/source/api/ui/BaseGUI.rst | 30 ++ docs/source/api/ui/EdgeWindow.rst | 38 +++ docs/source/api/ui/Popup.rst | 33 ++ docs/source/api/ui/Window.rst | 30 ++ docs/source/api/ui/index.rst | 10 + docs/source/api/widgets/ImageWidget.rst | 5 +- docs/source/conf.py | 6 + docs/source/generate_api.py | 46 ++- docs/source/index.rst | 16 +- docs/source/user_guide/faq.rst | 7 +- docs/source/user_guide/guide.rst | 83 ++++-- examples/desktop/guis/README.rst | 2 + examples/desktop/guis/image_widget_imgui.py | 82 +++++ examples/desktop/guis/imgui_basic.py | 123 ++++++++ examples/desktop/heatmap/heatmap.py | 13 +- examples/desktop/heatmap/heatmap_cmap.py | 33 -- examples/desktop/heatmap/heatmap_data.py | 35 --- examples/desktop/heatmap/heatmap_square.py | 31 -- examples/desktop/heatmap/heatmap_vmin_vmax.py | 33 -- examples/desktop/heatmap/heatmap_wide.py | 30 -- examples/desktop/image_widget/README.rst | 2 + .../{image => image_widget}/image_widget.py | 12 +- .../desktop/image_widget/image_widget_grid.py | 41 +++ .../image_widget/image_widget_single_video.py | 47 +++ .../image_widget/image_widget_videos.py | 43 +++ examples/desktop/misc/lorenz_animation.py | 9 +- examples/desktop/screenshots/gridplot.png | 4 +- .../screenshots/gridplot_non_square.png | 4 +- examples/desktop/screenshots/heatmap.png | 4 +- examples/desktop/screenshots/image_cmap.png | 4 +- examples/desktop/screenshots/image_rgb.png | 4 +- .../desktop/screenshots/image_rgbvminvmax.png | 4 +- examples/desktop/screenshots/image_simple.png | 4 +- examples/desktop/screenshots/image_small.png | 4 +- .../desktop/screenshots/image_vminvmax.png | 4 +- examples/desktop/screenshots/image_widget.png | 3 + .../desktop/screenshots/image_widget_grid.png | 3 + .../screenshots/image_widget_imgui.png | 3 + .../screenshots/image_widget_single_video.png | 3 + .../screenshots/image_widget_videos.png | 3 + examples/desktop/screenshots/imgui_basic.png | 3 + examples/desktop/screenshots/line.png | 4 +- examples/desktop/screenshots/line_cmap.png | 4 +- .../desktop/screenshots/line_collection.png | 4 +- .../line_collection_cmap_values.png | 4 +- ...ine_collection_cmap_values_qualitative.png | 4 +- .../screenshots/line_collection_colors.png | 4 +- .../screenshots/line_collection_slicing.png | 4 +- .../desktop/screenshots/line_colorslice.png | 4 +- .../desktop/screenshots/line_dataslice.png | 4 +- examples/desktop/screenshots/line_stack.png | 4 +- .../desktop/screenshots/scatter_cmap_iris.png | 4 +- .../screenshots/scatter_colorslice_iris.png | 4 +- .../screenshots/scatter_dataslice_iris.png | 4 +- examples/desktop/screenshots/scatter_iris.png | 4 +- examples/desktop/screenshots/scatter_size.png | 4 +- examples/desktop/selectors/README.rst | 2 +- examples/desktop/selectors/linear_selector.py | 59 +--- .../selectors/linear_selector_image.py | 73 +++++ examples/notebooks/heatmap.ipynb | 118 +++++++- examples/notebooks/image_widget_test.ipynb | 61 ++-- .../notebooks/screenshots/nb-astronaut.png | 4 +- .../screenshots/nb-astronaut_RGB.png | 4 +- examples/notebooks/screenshots/nb-camera.png | 4 +- .../nb-image-widget-movie-set_data.png | 4 +- .../nb-image-widget-movie-single-0-reset.png | 4 +- .../nb-image-widget-movie-single-0.png | 4 +- .../nb-image-widget-movie-single-279.png | 4 +- ...e-widget-movie-single-50-window-max-33.png | 4 +- ...-widget-movie-single-50-window-mean-13.png | 4 +- ...-widget-movie-single-50-window-mean-33.png | 4 +- ...ge-widget-movie-single-50-window-reset.png | 4 +- .../nb-image-widget-movie-single-50.png | 4 +- .../nb-image-widget-single-gnuplot2.png | 4 +- .../screenshots/nb-image-widget-single.png | 4 +- ...et-zfish-frame-50-frame-apply-gaussian.png | 4 +- ...idget-zfish-frame-50-frame-apply-reset.png | 4 +- ...ge-widget-zfish-frame-50-max-window-13.png | 4 +- ...e-widget-zfish-frame-50-mean-window-13.png | 4 +- ...ge-widget-zfish-frame-50-mean-window-5.png | 4 +- .../nb-image-widget-zfish-frame-50.png | 4 +- .../nb-image-widget-zfish-frame-99.png | 4 +- ...ish-grid-frame-50-frame-apply-gaussian.png | 4 +- ...-zfish-grid-frame-50-frame-apply-reset.png | 4 +- ...dget-zfish-grid-frame-50-max-window-13.png | 4 +- ...get-zfish-grid-frame-50-mean-window-13.png | 4 +- ...dget-zfish-grid-frame-50-mean-window-5.png | 4 +- .../nb-image-widget-zfish-grid-frame-50.png | 4 +- .../nb-image-widget-zfish-grid-frame-99.png | 4 +- ...e-widget-zfish-grid-init-mean-window-5.png | 4 +- ...fish-grid-set_data-reset-indices-false.png | 4 +- ...zfish-grid-set_data-reset-indices-true.png | 4 +- ...-image-widget-zfish-init-mean-window-5.png | 4 +- ...dget-zfish-mixed-rgb-cockatoo-frame-50.png | 4 +- ...dget-zfish-mixed-rgb-cockatoo-set-data.png | 4 +- ...get-zfish-mixed-rgb-cockatoo-windowrgb.png | 4 +- .../notebooks/screenshots/nb-lines-3d.png | 4 +- .../nb-lines-cmap-jet-values-cosine.png | 4 +- .../screenshots/nb-lines-cmap-jet-values.png | 4 +- .../screenshots/nb-lines-cmap-jet.png | 4 +- .../screenshots/nb-lines-cmap-tab-10.png | 4 +- .../nb-lines-cmap-viridis-values.png | 4 +- .../screenshots/nb-lines-cmap-viridis.png | 4 +- .../screenshots/nb-lines-cmap-white.png | 4 +- .../notebooks/screenshots/nb-lines-colors.png | 4 +- .../notebooks/screenshots/nb-lines-data.png | 4 +- .../screenshots/nb-lines-underlay.png | 4 +- examples/notebooks/screenshots/nb-lines.png | 4 +- examples/tests/test_examples.py | 19 ++ examples/tests/testutils.py | 4 +- fastplotlib/__init__.py | 10 +- fastplotlib/graphics/_base.py | 43 ++- fastplotlib/graphics/_features/__init__.py | 1 - fastplotlib/graphics/_features/_base.py | 3 - fastplotlib/graphics/_features/_image.py | 17 +- fastplotlib/layouts/__init__.py | 14 +- fastplotlib/layouts/_figure.py | 125 ++++---- fastplotlib/layouts/_imgui_figure.py | 243 +++++++++++++++ fastplotlib/layouts/_plot_area.py | 16 +- fastplotlib/layouts/_subplot.py | 155 ++++++---- fastplotlib/layouts/_utils.py | 40 ++- fastplotlib/layouts/output/__init__.py | 0 .../layouts/output/_ipywidget_toolbar.py | 202 ------------- fastplotlib/layouts/output/_qt_toolbar.py | 125 -------- .../layouts/output/_qtoolbar_template.py | 61 ---- fastplotlib/layouts/output/_toolbar.py | 45 --- fastplotlib/layouts/output/jupyter_output.py | 83 ------ fastplotlib/layouts/output/qt_output.py | 57 ---- fastplotlib/layouts/output/qtoolbar.ui | 89 ------ fastplotlib/tools/__init__.py | 1 + .../_histogram_lut.py} | 16 +- fastplotlib/ui/__init__.py | 3 + fastplotlib/ui/_base.py | 282 ++++++++++++++++++ fastplotlib/ui/_subplot_toolbar.py | 73 +++++ fastplotlib/ui/right_click_menus/__init__.py | 2 + .../ui/right_click_menus/_colormap_picker.py | 180 +++++++++++ .../ui/right_click_menus/_standard_menu.py | 181 +++++++++++ fastplotlib/utils/functions.py | 58 +++- fastplotlib/widgets/__init__.py | 2 +- .../_image_widget_ipywidget_toolbar.py | 135 --------- .../widgets/_image_widget_qt_toolbar.py | 127 -------- fastplotlib/widgets/image_widget/__init__.py | 1 + fastplotlib/widgets/image_widget/_sliders.py | 177 +++++++++++ .../{image.py => image_widget/_widget.py} | 221 ++++++++------ setup.py | 13 +- tests/conftest.py | 8 + tests/test_texture_array.py | 47 +-- 175 files changed, 2783 insertions(+), 1661 deletions(-) delete mode 100644 docs/source/_static/click_event.gif delete mode 100644 docs/source/_static/guide_animation.gif create mode 100644 docs/source/_static/guide_animation.webp create mode 100644 docs/source/_static/guide_click_event.webp delete mode 100644 docs/source/_static/guide_hello_world_data.png create mode 100644 docs/source/_static/guide_hello_world_fancy_slicing.png create mode 100644 docs/source/_static/guide_hello_world_simple_slicing.png delete mode 100644 docs/source/_static/guide_image_widget.gif create mode 100644 docs/source/_static/guide_image_widget.webp create mode 100644 docs/source/_static/guide_imgui.png delete mode 100644 docs/source/_static/guide_linear_selector.gif create mode 100644 docs/source/_static/guide_linear_selector.webp create mode 100644 docs/source/api/layouts/imgui_figure.rst create mode 100644 docs/source/api/ui/BaseGUI.rst create mode 100644 docs/source/api/ui/EdgeWindow.rst create mode 100644 docs/source/api/ui/Popup.rst create mode 100644 docs/source/api/ui/Window.rst create mode 100644 docs/source/api/ui/index.rst create mode 100644 examples/desktop/guis/README.rst create mode 100644 examples/desktop/guis/image_widget_imgui.py create mode 100644 examples/desktop/guis/imgui_basic.py delete mode 100644 examples/desktop/heatmap/heatmap_cmap.py delete mode 100644 examples/desktop/heatmap/heatmap_data.py delete mode 100644 examples/desktop/heatmap/heatmap_square.py delete mode 100644 examples/desktop/heatmap/heatmap_vmin_vmax.py delete mode 100644 examples/desktop/heatmap/heatmap_wide.py create mode 100644 examples/desktop/image_widget/README.rst rename examples/desktop/{image => image_widget}/image_widget.py (57%) create mode 100644 examples/desktop/image_widget/image_widget_grid.py create mode 100644 examples/desktop/image_widget/image_widget_single_video.py create mode 100644 examples/desktop/image_widget/image_widget_videos.py create mode 100644 examples/desktop/screenshots/image_widget.png create mode 100644 examples/desktop/screenshots/image_widget_grid.png create mode 100644 examples/desktop/screenshots/image_widget_imgui.png create mode 100644 examples/desktop/screenshots/image_widget_single_video.png create mode 100644 examples/desktop/screenshots/image_widget_videos.png create mode 100644 examples/desktop/screenshots/imgui_basic.png create mode 100644 examples/desktop/selectors/linear_selector_image.py create mode 100644 fastplotlib/layouts/_imgui_figure.py delete mode 100644 fastplotlib/layouts/output/__init__.py delete mode 100644 fastplotlib/layouts/output/_ipywidget_toolbar.py delete mode 100644 fastplotlib/layouts/output/_qt_toolbar.py delete mode 100644 fastplotlib/layouts/output/_qtoolbar_template.py delete mode 100644 fastplotlib/layouts/output/_toolbar.py delete mode 100644 fastplotlib/layouts/output/jupyter_output.py delete mode 100644 fastplotlib/layouts/output/qt_output.py delete mode 100644 fastplotlib/layouts/output/qtoolbar.ui create mode 100644 fastplotlib/tools/__init__.py rename fastplotlib/{widgets/histogram_lut.py => tools/_histogram_lut.py} (95%) create mode 100644 fastplotlib/ui/__init__.py create mode 100644 fastplotlib/ui/_base.py create mode 100644 fastplotlib/ui/_subplot_toolbar.py create mode 100644 fastplotlib/ui/right_click_menus/__init__.py create mode 100644 fastplotlib/ui/right_click_menus/_colormap_picker.py create mode 100644 fastplotlib/ui/right_click_menus/_standard_menu.py delete mode 100644 fastplotlib/widgets/_image_widget_ipywidget_toolbar.py delete mode 100644 fastplotlib/widgets/_image_widget_qt_toolbar.py create mode 100644 fastplotlib/widgets/image_widget/__init__.py create mode 100644 fastplotlib/widgets/image_widget/_sliders.py rename fastplotlib/widgets/{image.py => image_widget/_widget.py} (90%) create mode 100644 tests/conftest.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bc223ae27..23bfc8ae7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ on: jobs: docs-build: name: Docs - runs-on: bigmem + runs-on: ubuntu-latest timeout-minutes: 10 if: ${{ !github.event.pull_request.draft }} strategy: @@ -48,8 +48,8 @@ jobs: make html SPHINXOPTS="-W --keep-going" test-build-full: - name: Test Linux, notebook + glfw - runs-on: bigmem + name: Test Linux, notebook + offscreen + runs-on: ubuntu-latest timeout-minutes: 10 if: ${{ !github.event.pull_request.draft }} strategy: @@ -105,8 +105,8 @@ jobs: examples/notebooks/diffs test-build-desktop: - name: Test Linux, only glfw - runs-on: bigmem + name: Test Linux, only offscreen + runs-on: ubuntu-latest timeout-minutes: 10 if: ${{ !github.event.pull_request.draft }} strategy: diff --git a/.github/workflows/screenshots.yml b/.github/workflows/screenshots.yml index c1ed81644..0686b4445 100644 --- a/.github/workflows/screenshots.yml +++ b/.github/workflows/screenshots.yml @@ -13,7 +13,7 @@ on: jobs: screenshots: name: Regenerate - runs-on: bigmem + runs-on: ubuntu-latest timeout-minutes: 10 if: ${{ !github.event.pull_request.draft }} steps: diff --git a/docs/source/_static/click_event.gif b/docs/source/_static/click_event.gif deleted file mode 100644 index 81a334318..000000000 --- a/docs/source/_static/click_event.gif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b45c8aba01ba9557ee707e11952798df151d111a144cc09432323d98a3e2ee17 -size 110505 diff --git a/docs/source/_static/guide_animation.gif b/docs/source/_static/guide_animation.gif deleted file mode 100644 index 6328dbefc..000000000 --- a/docs/source/_static/guide_animation.gif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0b69ad02a527f4c7353cb94ce22deedd92134e84b2cc0776bf81f3d0083d0e37 -size 4095880 diff --git a/docs/source/_static/guide_animation.webp b/docs/source/_static/guide_animation.webp new file mode 100644 index 0000000000000000000000000000000000000000..f204fa1179a9883d22fa67a4050e105846f2e16e GIT binary patch literal 959738 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%0Ajm7A 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 z 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

p;VSAA58 znRJILQ%DaDrJ&Sd&k8~=Z7m=Q3G}w+e1KFZcT#z~Hwid4e4O$!jcq$jhK2<_HPRmU zeUP)(flS~tm;~dx%7a%r`@Pf;WhdBdGU=ZE>3GO)$8t%~HHC=zCJ!gg$LR>S2W~gI zEVxCkk6u(3t!=W`^vsbVPzv0szY1WN4aT$5oY9F2`^KLA5sk1y*1Ab87J0@CojwmmlFhw5gaEH8Mzh zN`(cP49*0E;eMt+FR766|Bwpt{&y;L|1%Ym-%{i(Ah+_{`(1 zsBn$98L73U_-PCmiT^7cd?}>Ji`#NrHzlpcj_ZP?;hA! zUAY$_i^)li2EgoEOaPaL(uKt}-@+GerAoF)j1~}lji?#N=4%F22ZW2SpC>M&j7=8x z@<@~u0edPUwYJCQqv3^1iHbPjGeXE`^DP$b&RxX8sL%7F1JtPF>>zbJmRtmT}7 z{Y-}tkU>B$UH?gkm?A%Ua%%rchxAfjF5|fb6JC)Zt2kHB#p;=~SiXv(1&nQrySg3G z@7DZ4WI&u8nNBIi`@(0&r`B}+1mos%1@f%^Bv%+a3f|xIJaen*jX$gTkh;?h4){V8 zSueYa@S!oYiBpdg^FB{rzMp*ICOUK(0+LFufn~d;|Lpql@ZPdNj?X7O+f}zWNzFT>eTqqR-XewGXVi{%qTwbd95!{JYG5?r=%ZANxQ5%$ka%e*DT`syoHfDwfY|eh+LlX)0R$+n|5uOR#j0@GGV=edW7I zL8fcyl<_-h5%At;<@gezoRakS4lg5$V4KCkM;%89Hgth}38&zbK#1*5kA9|9HNWDa zdwBak*^HAv-9|L>;Z2x)spFa7 zWnd~o$fOnh0}wOoY-!hn(AV$}$%R}viUdufE9l<`xF&e-)lrc0JKuR3&7uYknarD` zgx@W4{YtGAp+)m?;D3)DBCDGCKCXAQRP-OHk2?zMlNQ*;FB#6ydB(?6f^byQy8TG| zueqm&J$&DP!j8!B>vdf`0EJb)<=TG)UN|>a(r95mG$Stm9*nd4V6cZlj_k1I&ry*6 zY0P>s=_rSzzUkLtvt^te_;?QCy&xw1Pj^F^ZGtS?&^+%j5 zL`JMSO4~eBOgbZcLeL@oqc}J`$Ux)o4D(;r2-BYFDJB5QpN+fETzsS_)p_z5`(HKv zvogS=a92X;uCDO}AJR^E`d)E;jE{Wjk>bbCa7f*Zb0q-F+qZ##5_FrQax30Y%cbdK zg3vZc6%z*Tbz0WrT4-6yru9}%b+zb~iO*$d#g3G!1JVVbTqhA|m7)7;N`-Vm<|_F; zDwWsnN!U}LO1^CLkL$rtD97ZehxCk_plEBwpdLx@GdOTyi7#Gj*`BtE_#+0m3)&f! zs@=!#W3ll>SEvJSgMCYu5{`(_6;t+bb)qUf2h?2!5|{ z-`#ToP8K7_3{_8RoTN|;1U&lIsxb`t$U>x6xhz;FBf8AZiGuLvY2~R^AGYa(7HoBh z`~&Ase1=apX*~ERSr-rZC|Ng*M=W)|0$w*H8*Gs^n#3QAo8Yff)qH=CrZ2Kw>(A2x z=Vtn}VCWVBir2C*4I43(A<}?4paJJOSLRACOKOR<$Hk}Byo{MTzv@u9%jk4GbYD-l zbyIWnV|`XDJ2WOQY+NX6LQ1A;JKa zNFN>Gu%B|`d%m{&WA4}++%>P{#t36y8MMtvMDxP#bGp6Bc-iZ$g*eUw za1wFkrbfTF*>{v3pB_h!yCe|;#}XmY7H;WP;LLu&yI)GrS!?+Tq~Bda)R!3x@>V9< z8;uK}+bi>~9Gslgir~b78NA+eNM%HH<;<(m5IU&LgoUdG*>H9(QPl&bWlNN2+y=UKP&^=uP z#*!(#-+irYl_`2AU>;FCjNFK2VQx_+-Hli4{a8qIphCBg=rfK4KM?4{1}b%6jmIv3 zFCd(+5=56#1NXyfa&g1$@FBkl;>{+t##HXQdPOqz9*=(ViKHRKDmWSJ87{Xc^>LW0_c*wQw+aqxTq}pCRF1G_J(zlhO!dM%r}adYoV9~v)f{W?Ble;Ge!qZOV( z4jf$tKM%s@Qs^mh65)HQ6BA5J$a#KKY#}N-4`qGq5p*ZHW+v|^Z}y|6nC%Arbnzih zF3>A#7S{g4@Z*9I#!$bQY-$%q8XNvYlZ&v6RSpk1Ejx>Uq>(0$Ci8Sjff{=e!S?jt z@P~?pj&EL9vE9PxZ^Qs5s8M%~wwj@UN7tnA7BHrV^NMv7%HB^TtetGcq^t1S2f7n zp%#G%O2(-(Hb3*F2J}S)*GpVz_L#h}uoYf|q&(mmt|GCIGj>hnQVRk_1 zvYB+nFW+Ei>0pT(%j$yM#RnVh1|1N9@4ljHL&%r$QIV}_8WeG`&o{RBlQFYP_HjGz zE|dP3r)BlT)ZM#@soU7vmceB%Fpshs_eaPu7Gvf+QcCQhW^Y-f|3 z(;QyLj76!XaJiAVjSz)CEw_&&cBaILpCT9XUvYl_tQbgr$zI|E-W!&F1CF70Z>~6E zCzKIX+{Dg8o&YZ~Q}|=nQv&g`7-5lV&(~KsfWh>!^b^1{&w74yGXv>c9=m10BONK} zP!UJ#D3c`EyWK!a21eLn6;$W=o6ulotkicWY)com?4QadK_{PLK-W4DBM_ZuR_R;K zAY%EpX_v##YRNA(Bln2h?YpB137?Se&aLUw5T`Rpxp#u76B6Ssi#)|YbWYSeK@ge| z8Iw3r-&MamRthjHAi$X(Rs}muc9?)sB!xs8iZ?E9m{pLgTS5*zsJ0a1(bg#$`4lYKG_^+S;jq*96_SpVts3K z=jNQ1?_&GCpj)cXU$r+k{`iP-_bDn8uw>TaVa3=~6Gc)`rPw{tn>5J7!gQ-fXIoB;PFlT%s9jtmLBp zy!-uA&Q~r+%Ah>yjdQfRu>!+f!N_}EW{8tpO8STHl9@SJxOxNJQD6#aJ zXFR+OyHj05@zm#vLDnSf5xUOTb?Z_X9B+j&-?{wG$3eI827Tvfz$rD@R%WYRZChRB zJVKJT*#M0LBMl1f8$#x-z?%IS z4b>Bbjhi#;aASF$XW*(}_;MNb$#@^jf}*U)I!GlvBND_>N1oF+cDygS^YSZ}^65n* z3?725h_3UDVsqmQl`?P>2qwCgqxz4XiGK6Ul*{;o?Zw~9lRhYnvMbKPygqCD!c|*B zf@^Ix3bxtF;0CB`3z>OODq3&zCYZwTeGXjeBW_)5$^;$Eqk34IK@39#af71=!QABh ztBTkoWs)BEIB#+I-4EuN+ABJoYpID;W~3tttPfZL z<=40U>5k7F)CLV1KGI6-?Nf$thG%Zf!P{qF@}xHLUJNMbmp{9sd@ONz>Q2wwkI3j; z4kmce7?i~wR@wpga>LOQHZPK;rxRetl5-R6lWp(#21+IxGn-EeFB>2sJCjMRxnQV) zt+O8IvEkVxbi1-ka#vf|GH`L{+K$u#craaXV!v-ojBn*oJQJgoA`E5r@xfKd$WaiRO`+(u^$#C?t-0EK^78l8 zNUa|=1$gt&aw$H^##oXEgp2HEW~ijC>F9#IyipK_Kfo(f=Ay&PX&m&rHL5|ZQl?)<_|fwm(_iOFK*52A{;}3hmS|-~dcpA_ z3f@(h`ScEN_8e#12QKDyu}~Zx7+ftLDxL?aa6w1J6(BP12TM*3QuxAo(#3S~GYflL zd`$&;z?Z7j+xWAAAqCz%EQTnP?Q!nZqeS)Sx)! zV{>!RqFJWl`qU1?7uqWCh~N6#5e z*!|kgtsW37KCD_vsyTS#GpPaoz4RiDQsV@Cb_%_}I8XSP9F&pYgM?j=#ic{2TW!kO zb;MhP+gry$=K`=17-QZZ8WwY%D@h$c`3!C^%ZR45MY_pOZE%~snINn6Jz@Qg>9`~jKj9_@&vPQO%fB8?70*hBW~sja@b=n*+h4Rdqns`|eO%r!b|R$u?RuUyJY+Jxrv zpJ@}j{GYVxOUSRZNyzD(ehKh~d$}YvdA)l-72#q@!X>UH^+D!FU>|%C{E9CVG7fPI zf6Gm`>GU1EBZwQ#b37om6#OM`>bMSE-MNN*{(f&}p!vY4UoeL1Z-yOm2TMeEGs72J z@S2%1R2Q80G>7kl2@Qm|27`YR5fhshD#r&0iu3>yDVO4w5;dKqP;KH`y~h;g*R+dm z5J&?5u_4|bO(e{JqNCVc$ieGo5v++2DB-yRWE<2U6#vIIrl54l_2&F~+c|`z0jcfP z_CV)7<3}`^|JYE-2W6wSaG@M_QXCosLJZ5=;{SUO-g%1?$%5d!9vq7vOhLS=soamr z6C*qq|K3n?(yfh$MXg>&3jT67RgACWJ`{oHKO6q7$Y7h+lQzvIbm552jQZD{X|Ui3 zUHyqt34N!(hb`cokA%y_`TKQl9X;n0o4ecpG(zCE3Zo@jv1)>;{?dfaj4I`o=a|Y8 z3J2Tu?^b_4IvrvrJOvvMHF)!TCtb&nH5a&9|2in{3}~a??u2jIzqYJpTD~5x=4%uD znm=QybV4VFh`4@Bs&~$@5%O!(kv;`QXjgeb`n_$FR&^$j;j8kQeM$J$lPdi66%-Gd z!k<>A3T+_{r~c_xobTlpm^Cw)su|b62KzEh)k((!`tYBszc=#a*OCPn9WQn8b4y4M z^L(D^{j2|v9`w{O?CzsD-FhO=G|)eNSJ?iXSk*-?x7by<9c)&kjGQqh_pM9714jmaO7kbFgZ+ zk+Q+x!~FdPrbrjw{V~$Z^V6R7mIiGmZg2lOvY#{aYnyw+#5`hjO_Lr#Mj%tzwxxbr z{0IFv{mVytZ?#)OE#>E-K`7J-n)3-b(BY*2u6QtLmv&i2SP}!CZ%Iwr2-p=dRG)qh z?C*|#f06Fe+kGzu+n)Mn*fyCtXf+m~6G4b$(e?A@QKh<5{5BI5<2Ivp&T*Kz24FQG zYum$P%aJO( z?*g&O;sM#5htkJ{!DRP93CD5ry_ZGHp5_sKEAC?TGE29X%MpY_*q(xod_a=qbh#u1 zA%vbVZC?5NX$W>&NYmwj#wSR%8v3~V0Pp+~heut=QxLR$xWp+`B0f|w0NC`#qimL> zgtV$k1gN43np8jI`Bf&=L$}EX-xUp{hb|bUA_C!MW0vJ9DmsT) z;~AW@u=5rpj1%?GVu>KQE0-k=aNIuD=N3#N41`rt>ic;g5 z8Ke@yrUJyt$R0yCltQ9|wo`-;7*$zHM8v*C;h>a)zD9}w#_v_OJ7`+!Oh>P1^J4`E zIpRE}P8B(tcil1zq9G|6>k{P{$W?oVU{3pn*vzOQIE`~6b4L(KQ@KYGac7jYA*Ty- ze#l(dT^&<~APBjLE>eRy-In0LM$NlI?A!tyxLBWI7`mm4W=Xr1z6k2V9#mZUS~JQC z&NZs_U=)PJmo(MILpjqvf_k)K=K|pp@~IF|cNz*sys)+{&k;nY-YRqA3<;R}A0W_=lQ{TdR&F@_YAu$qC^;_7~@uV01-Hi`D_XW;4cz%5y~BCBrS)i6Nt<)BhM^^m@u@dQh=J@Lv)+8MgN(Txj+QXnTk z&yW5@en~pKsCvg_3Elu+T_0{x|)arziCGz4J>+)u^#*nk5B4Hms5HGDBZ18 z>ETaVB(eF8#$HNtraW??wgNfM9xROBZ@id8k0mwcGj<0&gVr#z5lW{XvKMsY^>Q3u zV~^CDIUJH>GK<;ZLz_xk-MN{*IhA75mTC4Ea$NRv_3S@8jUmwJ3O`$goSUBzpMdPz zIXebCojo0lalO~xd#k zylHHv8oG6A`JvQhj>}=1;5Pb2@%c(FV-?@Nkw<-S9Pkcb$OU&MaAY=qW{P6sQNgER zmmCQsl^D9Pg~rg5!RE~_?4Fdo=;%E9e1*f!`Mg$C;+7RNgLPdDV;%??;Rs>R=Im=B zM$Rb-qFBBb75I_u051K8Dv>Y*Au1mbBNN8EzG(9=b`H72@#}mS$6HiS!C15?5!V9} zE&eHZ6Ta33uUoa`2vgEk(OSk=ZfEM|RzQ z8r!#@?JfmBa%-ei!^<4E8ASYq>GWM|<@2)V#8Oj33NHu7#Y_n+ODu5R?zt> z6a5)YqT%`MH0>$m(}n)>rZmU_8GeHtYRCw?c0fNd#oRgET`$@uwUox;0Fu8J=d(*y z=bA*ky@=QJ{7Et`**r1!hgH#xoEXS}r&wm$FRqO$2y-J$I#Bl?XINbNf|?M;u1!HN zeVL`c)}*|z7r=|fq|`w#!EAK2ib;YD8JOq`lp6mQ9*r3f>0g1U7a*4WHlsY=ZXVckU;M4C!+*ax}=6o#2i0Jpl^pFza>e2wgEB zRxL8Cm~=(TZFq9-hv-p%i8y>KEQcW-vp((fp^_rc0Bc&XHMPD(I?PGLus;=ku>^FicSjU(nroQ!tU&)4i zGFscN9Cplea^|uP!MIHgjhCn7%lDM)xSy(t+$Q!#i36g_x7r4kQjKcC@VH$ zs=dTG!HIZt>tqy_ZwSls65ZFoPc@$U2%lQr@mhzK271yOZ%3$HaZy`|R%({a(#DW0 z8ogs}M)$US+c{2q8k4t+9h6#2TPXYLey}wCMxQklUh;`D;^Lz4EK%0@`u*242IADu zHRQ-B$Cp8zb0#*+=&K~-Fo2R0Vk_E>wU3lOU-q z(I{EhDU2fdp4m66b5<*)IB^gEc6XSXBoXDnn@tdU$Gq-kMcd><20i;IVyAc=b}K4} z)Z#tyTB9&by#CauG@=+7yX?O9;M4+e4r;pBel7PHMsni37f%UL+n@j9>J%OlzY+O9 z`x?V=w{zf?8|0fM6f{`o2wMJ z@Hv`V7s7(gOi5ddb$1O7EN7&VOU_tK?B#;3Tx<&al3A@s!d^lg$Q-0 z8j)86?`mS^vyeOyeU$3*-S#hzB{=&sa2||4cKf!)Rz9rECcR0_9-l!jy^PTj<O_&85wEyryCoVH?^#KW~?h(!P?2NL8bgeRuO+n!);L zL-+Fi#C}{4wzUmR@ugmwaEqa%m8s}TE}2wpClaWC9r;}W$Zd}Gj#({(RZ7msk>!_V zJ$Op8jU;i#`*Ih<$5JhVV|s6;qd`A^#ljdq|NLNXcJQr>ZLT8=Oi4ET#Phv^1CySX zhuXq$6$}TsYK1Bspo5A;L$OHuR5!+(nbGSmGA7#gjFoDF2Zt?Y z&^3ya4@`?7h)OfYbo2s@5cQ#SLohd!e#Pi5jnS6xt%;G^YCgk(;9HmBOlqx!U55~m zoteSDD(3C=7RNGtRRpH{)G{uLgH9kiM(0QYZ#FkN1A5k~4RRKShLNd09;7`fowkTusWB|tR z|7u@AVLd222I}gxl3CB*&`LgxA_{Kuie4lwWwTW}5gaKG$xWiAWS`o!eg>c5V{V!8 zKKgW93fQN}yF06ZDQQrJK>gWDhoCfsE{J(y*eq<YP%u?UM-tsHg;v}!w5eP(1K z#YO7isqU57vC}vpX$B&J0^s}foqwfm`@1!<1jG1#0*Y`Vp97O*-=}nrGPqVe`V0;x zd^g19UxBH&b$~t~$);B}@GT}EVVL3J%Nx36NXE3DZT+~Ut7g`jnIeA5${m=r?cdyw z_wv^=v2j<>S1JSh@L3E^smf!s!Yf~k_U|4?M``@Npp}pP7n#DVC-ucQDhvuT^GF6jV?_7QRCYKeg9O+K`Mb z@MgPegQ1`GQYar$x=}AcT~@_OH)u?Y>`>L!Q3!en277}7uxQ7)&=K+m&oMsZ!lSQ% z=B}@^WENQuELM;fYVmm2W080Ay=mn!gXYvA?KOh5*zs7al2U91tGu+gs-kolU2f1}z8}TS z?O7y$8RW|DX^~Nb!tJ%x^o2PsqhSe4)zg5!%&N7}ZsAfiW+_uL5%o}|OEi)AZpAYO zK1Y``JWyajD6b?;g%vJYhr4MvlGb2cd8RM5N&+#~N+r54HRe^UHD`2pf@o#Z;+|5Z zJ!edJfJimY(%uc#nk8E(2B;N7a1Gv#d(6(;GfOVHKtIluXR!`7i0l(drL(^zTGckB z+8}?PbA|FV1^d?(c|Xs&Vwe6a1>^p6&ei(M6?sA)GZL~qns`czF5p*?)W)%MTDO2& z)6tiU@$&Ht-+kd}b)9}5yr#MYdFFd^D&&4iz*+#9cOzacb5aj{+s_busx22?q0Fuu z>3Xyq7Asgc(2R~*14mJOlV+stCzdI798V4?^3j|l;bkkt?Zm|T$jshC!Xj1@bncUa zmZ^`n&`GgXQ7n|I0q%q|8)5$yh5ZbhCYU~3FDvxxqe=+mV%r`PPtlP732FYMHQK5s z_m*)WUU39&R}k`+jMjjc3-SIWJHNjOEKvS2P$*kUVM1fRN`d>CaoJ=Hunt;axz)4W{E0+1N8J^M15jtU2!Dl9zj6`vp5dNE$5)seePSUypDd(^B ziQJkbd>!=tzm6%elqB63((Pk3x;5utLD!N%C9n4Q%awP3ayk67UN5;xf=si8zv7h4 zb!n3#!RmkE(Dlp~5@r08^>r&^OImCN{W`ptgP9JTphpkyxM27@qU1Z;vG+r;@cuhx z6!~%gV?TQ?Dd+Ef5O0l5Cy`yoivH?#I&cRbUz=%*`%nJ%E4%s-BXd^1`~L5|4Ql*@ zF;q#~5X*$bU)vDQUOR5WU#)!2qJDh2LHOLCIsf $`muC&U!Ux(}gNa$RuKmMZs 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%0A8{ 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πwgO4<<@X6RSxE7 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

PepXJdJ}2&z}fBLww#=n5}r` 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%0AY-x@ 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%0Dh!9$aoyGM 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%0A~SfVRMale+K+xV_{ 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 Mt?BN=7*S9F06Zax-2eap literal 0 HcmV?d00001 diff --git a/docs/source/_static/guide_imgui.png b/docs/source/_static/guide_imgui.png new file mode 100644 index 000000000..6c17e36b3 --- /dev/null +++ b/docs/source/_static/guide_imgui.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:262dfd4e83abba504a3630c74ba873fbe6471fdb69b32f250cd372fa67c4a44c +size 63997 diff --git a/docs/source/_static/guide_linear_selector.gif b/docs/source/_static/guide_linear_selector.gif deleted file mode 100644 index 383c3ef43..000000000 --- a/docs/source/_static/guide_linear_selector.gif +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:acf1cab72eb4d268581b26295e47cd0ca9aebe08beaf80e1424b100086ed59ca -size 331446 diff --git a/docs/source/_static/guide_linear_selector.webp b/docs/source/_static/guide_linear_selector.webp new file mode 100644 index 0000000000000000000000000000000000000000..c60ec6c035865a0b9085ad9a743a3bc80a72ec71 GIT binary patch literal 372418 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≀Ni$wm%mZ;?s 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

53)E2tRsKOb%<+dJWo=uQkngLRtmZ6J1|f`iK4N zL5lneX$VSoiU6f9u44panQAK&E>0L!$7T+fi+!a$xzXc-;x;q+YGfy3UlRK^=Fd;Y zJ^#P>9^jTA#|ysE%vJ~OQAFlWapdN8X3q1gdXRtl5j3&(2jLPIHHf z;{*JdqCR$q$~3uxJQ*27?f{UUd-aW?>y33X1d{ zpYtZpw1RY`&HLJrE_bHzS{2uv{F(xjJ{$H1$5^sTYz+&ytY@m%*>N25xQ_w@hA_ZteB#4PUH z_WW7dlQ(WNCwOE=fLIoeFQqkF@Jw)(SXwjA6Tx7P!YvY_Sl~J)MS;+vYRe){-+ne?9G*cyhJ%yHpqp)!e|RbKfDBrjiRPBKy)IvOY|S-1A4*^ zwkU`n!Z~`@C^do*z+C1AAOc(iDlnS~C5_6UeI%rU+gwcgnNNxsPcp1dG`D1X&GLNC z{^l5--zv4i0P{d3n=s&qVJbjVvx$W;doEJ?QrLmYrp<(=8JxZiYL1yTglNT+sk1n{ zG$8&~!%eZjXuNSTDp&JJ>o4GXFR?;7p}Q3!zg^wx)OQ~_a)v+(lbRPQ8zcpCM0QNu zVk|E9mrMp-uqVi~t6rbVYu%9@K1>crjaH~Dju+)w{BZj!71R`5{^eha;Y76^auOG9 zq2JYHOG!S)8zFHnaefeNI|FrDf1LSJ=cV_19+_gqsO!f!dAK%uoc(--#30uH?eJY` zgUk?=Yn&}mD=r)Ny#1e?2lzdq;rMTQnCJhN9{z`6c+YwKhhd0=dZ!m?p}SG?sIzgd z2cJo)p#M#JG{297NBmFHgZDq_;dgrBJvs6ZykNDMbm$EQfv`ir!AS01>eb5p^$Y*o zJm$9y>T$*ZrQbIU5FQ4cc0Rv=0-}y&POe`4%YnzlVkCJnj*!DmTDfu|ZO5c5NuiCZ zq;d=NlG%q?(wKCa)fLh+4jw2Eo(a zUP_F!YMmXB+VQN55Z+|A{)u5R?qZQ!)a2WXe89R?4g3-|avgD3Wg+K@hp-RR8$D&R zf3)0^(}G>S*=JW7B5&F(B=7K|#Hh=iC6v+#l18$r+nO!{`FwizLjX-ih#F{8_L<~k|uuP<^jqM7&U z`izG72mXHoeZ6m0;htMfLt8xyPz1?!+tRV@k+T!lbC!LFn6H`d887-F1|A^2rOW*K z?BO&)NN^Z7W^|)}GJ0()vWGf>5E;z(J(gO zP;nGZ&nM41b*6)DN2Wb|!tZHFsNgr2mEqj4{gL8QXiBl>!wvx^&uejoP$nfly1^gn zI^M`3Tu;vz>ry7I0ZQegY%X9;EGM z7GYNgpl{ldoQ2s=tHT~pEbqRO_d)sRlk%aHduHlow)Teu7q#dgUL-xn;Wa-RT&i(x z4*buAaTe3*eF;L~k34-NFd{|))oY5IO&@cfCBjMRRJWN`4viU7R=g| zd_R@tqVdzRZLvxY^Ng^370OEq5iEt=CV$$fS{~RS++ObVh8%4vCJGeHN0CO)+iXRP z=i8anRZQU;%b@7?Lpp{gI*VF}X!IS}t+CB3G)bm`7x$z*HBsi#B(l3f9{O>_r~$vJ zArBWXOHRwx!-kMvgHTSf_lU?pX|L<|+3WfLAel(y#`GXkrm~o-Um$~@A{!qg%FD|a zpya=Jd<57Olq z!u=zlfu+l>zfSQ1v2Q&Gl;Djcz?2&3O4NVHJ2#+f8wx}_5?2MbFq~>yyxu(ZSY+-8 zhIV9I#-&-5!i2su`n<@MehV9Gn)Om=@C5$*!S?ng=t6lu-8M zzG1E{pNyPwjOQm?Y2O&|!K3>sM~xCnwq;x=zCkMQU+MCCbp9MZ6}c9U(J{lV^#wgC zR4-E}y{Hd*2(6M!>=j6OZ>q-{86GUU0GgY}$C1g2m}g>;$vQ z0gq#0k)3B++zMdli1q5*_q6=zQIFxr9A+}i*h*^ZKNl>W`w@Yui5GI){H(Nf%Jt1q zFF%RT(G!oj#u*$wd`lxiFZ(v69OHsk$yw!(6RMaeRbUm%ah*`=*>rdTAsr(f$W;8?;x zJ^vnIQ$pZ^q`kZ#Yr6JpS9^1dzHy#SiJV-N`lJjX>yQsEHLTMg?-$=&{f-@i=i#qdY9smCggqk2)ayh@>~#jM>SVYt?5UGZ^0 z3?v+0yYra$Suqt=1r3c;+;#)DT0Yi1zWYUF z(erMJdC&@dA)-pvp!ae!I*)@evD>DrXt+}^Y2ZmbtZ~L%W_U2&cGt^wt{i2iOee|z z5q7R-sbvdBH}x6qQ#y@sF&xP0%s03pCx$Q}?Xw)V4@L;@{H*+!=qH6+?-dKuAKp}! z7`i(@^XhaWD^oKpY(Y zx}7+j#Mw)~V~hXH`%HxQi2T3vKJ&k%>)$*4|M!$4~tUO&}iyB-I|zXjvKFATi(@^Ufz8(T%>hknKHkhtOd3c9DVk#hy|LU z{kt+SzJDGb(?3;0*1x%W)_7SMi+`#F6?8#2FoV_%tTU_@SwsM1b=K=syFR@{eG_u za<#P+VPmlb$D_watb}@Rvv1iCdo+xBI(0(?-d4DaWZ5%FR~W zXnD%j?v$2>no@oP{B!l(%KAO8e2&&Ko1mX*L5C$GiVSFVocFqtXBgjLo0)b#Q%@gE#tP zKEo0&O9i%Ly%k4ac}O;M)$UrAyCMI9|A_IZ_45}Jjd70++`894e-YfM+whc%I?ZMVw~~bG=!541X=@is0`-PZ8HO z_jZ^lE!)YA9hokK4m=QR@Gl(5qz!Xh@T?D?7lSHWH+Ez+x`DqRICNZ_8_i!^|F3Y# z&Ya*Y-5>Pbvt!xLv5dP#=(jM)F7PdqBHql3>u#mjUi7tXr>dqSpC#!_RID6_FlSl@ z2e~S$))76)rR&Ru#ump#hMpZ@x5zhEhpiVsjK8yrTr+{XDb6+T-hE=|Wvpng9p|jB zW_-0QwN%t33!|4o5OyiL-wX@rM#!%)zaE5O^7;rd)O)>S64tuW@B1??NuY7*F%4F# zLM^iKnnUFLYO-NnILeU|YYu)_7c~Bf2Mvjoe)gFgGjrdHfgr_nwTkC*qu0mrn9+o?$Ms1CM|E6OL zs;PWjltd<0`OsMMry)*=mE=!D7M3O@$4qM&g627moOH=prqrsVY+<+#ZE)364Ay`+ zOWz_#FFwLK<47EWHP=MEMRmZfR<}#VQQiqroVDokM=sg$@sLTwy_vWCfMy18;8(=G z!G;6j!%tHPD8xxsvd&GMH@lJjH`w|BnfR;qA2bv871yB^yNxm4!^4jlqPsFv9yi*; z{YCJT-__f30z|J3w_9kekKgEh>(;M5+V(bLfExjB*NaAL-hye6JHcNKNz2uYE}+4ur_M<_5 zfTLzr&fpJ!B@1W2m-w@@h`i z19_wNzwgY$^<_!GShmi)WA^QYO$!Y21LyT=UJX;Nd~7yAky9?PMf>S`OhhOj?Aw`Z zlk_)mjZT^Gw-==JP0X%1+rAbP#Uq3$vUdh@adlwO;{w@$jJEBR8Wv9s^R7`H(l@%QSYM%#pI)@{OQ>3f+T}+cVIHXdiVp_mL5z z&L9*Xs|>pnznVz(_UNB~wd92x{8Jd!R!=s8++*OfN6H=w`5Hkr(z8i`@2Exn zTG$}1^k(#;-+Kh_r>aR#vXe;LZW$o*-xnV$7;UHnAuJ8OME{`^-p6+R*EJ~eAId99 zDg-()@SpvN676Cg))BrlQVdh4MhumH$@T5&roVvX?|Qn!_qXyFjrt_uMx)0r;;)(l zhkM8<#NIO;k_Bl1MnB&}I46(hDIP#Qknf^@{cW6z{PW$eoGdYPN{~7}-^!>MyIu5% zQi8#3a6F%hc7}iU^sBM@$N=$#uW6Vr6@_s5sNB@Qh8^i^g)lNFMJ*?omECIo4v9!D zk93(UO-sp*Al|NtFXP>nFS~aAd;#7`Cf1nsZ1jm51=S2r+@r3>r7>OQEPmAr;tAM@1b*0=T`=qZl;aHPxg9kxq+6y|7C%H))- zCkhruB)6UWLHWXnDx75f9|WvG!65IVH=~K0o~jYv+Us64yU$y?XR)6fU__onq3A!Z zRn@*vuQlWD%9L)-IDNO}y)_*Wp7zvNc6BC2a+ksKHnpzH zD1H|_Ewf(VonM3CRakAj53TSBJ0q&+&5b49%|Ug2qaY7`KUd&#^pFmJmrD+(Q^Td( z5YRzNw&OOhTqU^k2t7YQ`lM&fDLU}>q9eg78EL(z&Jw7cNHd>`(@=+YY8G1R_$0~@ zRF>*DmDv-Ie*iDX;~3hj%$*w5<_)&-P;w)9KB48}eek&0`$~sFGu?KC<=Ugq`NoXS zQjrHgd*uN1HCPUu=47K5=4`P$+Ji~IA7U=3oOHEaY>-vcumi_4tcF5#jK!EAx#3R( z*!%tUKef`$BQxS3$00ut%wOAGTLu>HakXd;9&S$3o<;X>8YgW&V-nHHu#74$-k-t! zVc=-E`^{~xY#MAcjaXfF9r^mI{v%me+NjM`Zsw9f@#?!;5-#~RGOcO>9* z4eH*ObX?pNSxr}0#J;1KQRuV{I)F!P>;-Ti6XJoCKW+*yp6X0r9M$O1E&)3b73LB- z&elJUj|oBAHG&|NXcFb$#6erXK2M9P@qhpLE1JYJ?8rmNy*7N?LaO02lc}b)B*${t4Qwx6_B2T z&p}}jK$$+XElBzkkZ|fvYW(gxbK_#hFqK@C@tZ=4=G@vKy|eX4a%{nqKlPZ#M-8w% z8>VA@K=rh^cob1k9peyoS%9r$qF7#Wx#9TvYyVmN$BLNmp6bP_NOblCk^-M6U3kLg zP_9L?1!`cl&%y_+mizIkt+!z9)b`myD=p2z(@5aVl#(H{@lK?#~#L=QS#Au0*-0^w}}ul<3{dDW{cSRp)L-X<^cH zZ>*blS`|B=YAtq`cL_(dP4#J)l6G=1SVOvCi1`$#nenyY!vS+Hq{;*C@~oY2~1wC3j&g=}KrbcbgV1PKxjQ28L@Q0KpmbQ9Rw@44o0xI@K; zmRY5dU#L#^1uCqvz2*lkF2y2<#sBCIE+`@HGy*L~WAf4nU_xW)$Hf+FQ)U->eB7S>y_OrpmsM!xDFoaO4pi0lUwk3~D3upJo z%&!ubfadq_AW}7%jApk6a_k0XQki&PwEsQt;Gy{E=%2@brBle?U!VCu=oBidIwY}w z%^O?SY%=e*9q5mRYk&JiY6|n7iJ~Y@TcSXEozWm#hh^{KmF2tXQH%})-SdWG(f&5cP;b$mGT7MI-X9XST{_lo_44tP*J*T*GCfbE^O=-Bd$UOR9q7e{;{Y zEbCe(ry=P^Tqem{wNnRA-|q26`jCTnQr}(nvp|W^JuXZLViXEZExs4w4cqseQ>9V0 zLu=}^_kk#ME!DT9Va0V{?;89NQ9REOt<`2pTYnUE0)8vb;R#}#IZ9cRwv^p>?M;N< z+|G{!JX*d;-#ab7hcZ;~ByMS4k6!sy2*nJl7Hd9oP*HEP@zFp_8iRC-iJyG5Rkp;m z8BY?#VaW>l-J5EtPUaqHDnleM5-T_EqK=t{!)>rkq`eBpn;1RwLkES$VfwVJv6*s8@qm*jJY+;nXP_INkPkaPnXd z$kYXO*IX_uA!jWx8}>gdUON+eW=nKSx`Z&KwZ^-3E5zZ-4hwejxPN*n{Y2Sm?pI#t zj$InXi@xRZMKn>P-A_e`QGkkwB1ZjGKC%>#MJz%fRK?kn)KZ*OY*RHd z@~!>DA2}>t zhY?m2Ha17XJ0I*vyiZmU$b?pytDqhc|Gq-40QxqYW8dgH)(WXp`VGvRVXMu*n1`FX z=ghOoIz=g;oaB{RZTE{Az%RLV&^be0pTNs(7gM3J=`zo8w09@fFj6H%tIXxJCBJf@BMxt5N8MQawmophDUyV_B=2No zQ|=L}bFxJ^+^lC>`ofP5d%bCPe6|k$*5hk8zVx@je*2Fod#5@CVEKXG1)slpNThpC zWZ?~|y`Z7rIsn>ekf0|pyBhvPl5MX2qsb>*Whp698Ry@!QYqkz`F4j0jvO2wK^T>H zOvPosKW;WDS~3CqqZoctE@EqCawmi3&erTXs;Eb*{}nT6Gc&35V2m!gcN%sbRf;lg zR8#YFQqT0Uz)elJ)9x&(zt1P*Rklkg-XJKxlC1oVragUucSxnPA4i|OO7^HSU{T4x z^gna`e@y#o1-|}{7TB#GS)s2DHB9$lBjgR@+g)3O45Y!21(OVkjDTU3Ye7TYBJAf1 zgo5v49zs8A$KPY4XgG_ks#CLn1RLOM11~-2jZ=AvKE8`M^onwdzIj-n8bhz&lngay z9KQy>-*e!b(R7#dkA@gS+GxQk*^TlMpZq8gEp4QQ~I) zC44#^0Cp-qk?d47)Ij4cn!5#=H_MdB{9fp4{mIpqH<-Wm9r_9~z8f$)WZkz2sR`O_ z&=y4nD@o8%C*ag&T|57Ah!)4J4svwIe;2qId>%;0o#(`a?2>k!uZn#Mh)HwuyJBj3 z`!;-+#dvcK-HAI&lHf^d@X(pFo0a?UN_#lAzVMp06#6Uk9GIbVPHcf5B<}Ylu@jyD z#YG1|teqCJ`1yz{d6}24(57h)?7J;GxL3VmTqZ!Wad%njTto4luD%0Mg2)h+?br|r zJ77TJ*9gf?awEvu*s6FQ!0I)M9pq2)Chy_%m!AO4DxxHPurnps)A|0PROr2H4_`_X z-WEqVi~@eDkT;|uhY#U#hQd>n-cCi%4_vD!t>s)Rc0&3y)4zICO!6!Jy2V&l(WBs+ z)XE(i%1pSZ0{5O=NjSyO)dW)0YuU=%MjO~)gxB8ZDXJ6fF8-Y9Xj`;(`(E|sUGHli za_n?9AClA4>$TkDR}&nfx_Dw9(ceh3s61`bL@kEtzrr9iKYkBoQDx&F?sAR+Pr!5# z7G~fKONQl@ly)>BdJ7JD-5D3r+Nuj~I9)gnthJN5_(v3K&89LRRVx;}gJ;wA%8^in z5_wH9o7fc^C$67Ywj5(eyN_g z-a->BM)I0BMJL4O9sXcQOoKy(DL~V1 zmzP(Uq?EbP{@IEOM_@1iyna3J7!r_-g4YTMc zG#nz7VY&6URbi{6z0By=>CdgoiD69Fuq_=~ur6x!>QHdNkJU`+t6m>vhn002MR`m` z;=Y+|aE0~;O3#@N%zrVpP9J&meYRpNK!#rAl}(%rdd$~0%>#X9Rcpo}ClU2m5Ou{; z;Ca7TIhw(}f^V?Ay3#g_dXz6N+1%Z->*zi8NVZAieFRJR>K}TSe@%F^zU34?f2maMNkC!+!24 z!^T2f3hM*kh@z(S=vAnV28h)`6l*5wBXBF3#DpRfcX5$s_tEmLYz7%vOK!il_@+>_u!2Ws7MbW=-%#kcx}u|Ym`V0j~j zvJtv+bOJw-uhW$qdlpw~{8}I{lc%)wi)G8EcBsrtzDZsw$jM%1${pk0^ryFaoANgQ z2*I~ZbHhK-Kg^l5SVIvOn~(`39=$y?=yDbK%`m-R=R%FG*v|PA@#2~;!A9}T%)~m^ ze-o$JiT_UFLc>_1uVyKyaQ`SM_@m2)2D!~w#988j*je9>r5Hk9s`3U&&BlDu27%D5 zqxc)bV-15ZVQQ62#l|&a6v;tFC&AbhCW9_MwSLp7&~3tCW>W)?Sd?Zikcm zDM|vhWL$dIGd71xX&qZqTkIN*m0&(Kw4Wo(mc(kVWH{iJ@aV4Wc^$N4jEU^2zI8bR z(pMzXxFdJ9L_yo}eU5~Vr!wB{;YBQI zvrg?8V3^qVN%|&-D>U(F`rP~(Dq^b$ATRoCTgH$5vA4H^#$)d7cMTbNg>k9#e!IsH zv-$SLA(XO49q@eowA=ZG@(&&=e{Zs(F~dVk{CJQ=D`!dt8mb+3-(q~AP-BF|4#%-- z{yMms(ZKx3)+&$W(Cyi(C8{^XVORgV)N1;)Yl<5E-9lNabd9UhwAHD?Uu-youd}{5 z>GlO;LaafW(z%v&$MM;4rTXfL3)sl}+_*5aXe+b6uC<{;8ayNDW@gh zvPity$IUfd9wnMX1T=QYq?CCwbI^}T=6(GA{xchQVGBR zIEC>-*AEm+CjX(_v>jp3g8+ zRQeH*?Mtqk9)Z*FzBBotN>@-2-R-1McKnP51HwYmLF+zha)_T-07Uc5U&JAeouDLD zzkA{!dGu?|8OExb!;hnHqUxd_%BG9$U@GhpC3_N;aS@d{H<(VgUqAI zUoj7vhs2b_f`4zV_P&uTp_M{1@o9Ov-yI}>w0_gqgW<_3fwtIvs1d%B*Gp@1B;;Om zv1I!fYBVE|0i}3Mu(x3#6J7d8)YT_oC38#|1h0Mv2hL-l}WXSVK&1c1@}2 zTz;Qw&gUc5oEoKV~2e3`JZ!248aOQd$(8_hNQ z(%THC$t67DaOe!_83%G!5Hp)-3Sp~?;DPd87x)n$@YrUeUU;&VWGAW*8P&MM+RYLj zny^s`n9O-(S8jE8a(7+ zD5!`hJz=#^gcZ(tooH6u_-<5$Dk;4G9bUq11y#BeqZ`X$J{wn@O8^Zfr4+1dGmx*Qw)#(IB(SNlwe_2>~w*=IK<9=kV2sV_>}B7d^0P`?2Z>W zxgcs)i?1#uSvFsszO|(^hiF!!x;pSJxLJBFZtuC)hKqtJ$g5hpF(^c>RrsiV>k*0~ z@dI=RKudCH=4>w-;WapaO=H5fXqXq%L`()N9XyFx_Ms3Ut~E%18yAaEhTgAeECfkp z94#H~qZD)zmDhI#E0=J=1@6N_%5ICfdTJTt~;`dwiiKYmoewdkf-ec%2n~qXi zOwYc~5E5zog5}E%ihizCL`{2cJc>K|U6`|+=OG*7xaezRNb+ZgO$NKo0d7AYIA%D$ z7swp9G~1uJ{LS*)O2JI!6=;|jS>1=6;f_#lDE{e<@|?8Jw12cvrE2s%QH}B*a3)o& z%v=FJ(9RZeaJS+kQkvm(6S)-ajr0?dqdu|Hu91rAb%Vz27hNiImcG7s3jrnNOJS)N zt)+bM%5N~#u?OJcldMsfFq$EBi#}=ao$)FqFl>A@`Pi4qa90fRay0Zr^Sb+m&>j36sf>Kom9q2!^;w47lf^7LCm#(m47Yig4}%?jT5Aa<)H{UH4V zZ$EG|gD7)IOyQCOOs;~Xx&63*xfU)s5asKEt4dPS9;s_wL@80(limFmqV#cU?W4N4 z34|#By+w80WQB1A3DA~jmNrQyHlPM4GVy7jJ_vzYyIB(LPo;KqGC?L08)jCX?h$S? zCo!$Y;Muu0pczegBbahTC-Clj>a8#P>W}aDJM%b=MjagQWY+xV2#2ANYl^oiz=sWM za>*QmdiKi4%v2i<4<0+c6=KMs%|R3ovj4Rzs^DdcIx0at@Z166=zgrB zXljtJDT3;7yAF`y-|i4e?GjV$tSQJb2W{~@PY;jVb$G3C_oY8t$R9MB>o+zgHJpw9 z8ft}x-ws+NNS4bqFa87640^cv^ymisZB^Fhz5JfTLT5IfA*-89`AufLD(=kE!=>TX zA#tGPt#~x?L;TuT{SE^1?A}OS3pY$sLL{>pZ@F8V3@h%V^hF0AzCMAXyQ@9-NmP*20n?BrHM6O`(cVk4O4DAnLV(xVVWZA`b7B z%VvSv#c$CQsF{i@uKZt*xnEpdEe2)ceop$wv+LZ2v0Ys=90871x$1y+)oy|rQNy*n z;XP&0B_XbqB0d@v4s3UMWP@@Swqk`X;v1Q`iU4n(Qsx zWC{%sg%Hygk+kpDAe>N>CDyf6{>ieY&_d6qzph}P#&0P(u(y~xH~fj=^wl3=ikjhC zmvpUBQP80ZMdCHhRzkirRcEYN3Hn%eCWkXmD_LEcPFLiVg}2HU_qS*qibwRwes_G~ zf&DDC*99aK`&xUe(krfQh5Pl7MTq+*j1%}u1xXdmHFeTP3hQEk^SzwN_PQU!D6HzR zvNGrlf)-6Zs<8TTKyp*p1#c%r|H%w1Ol)rkun~;c7RyE_)%uIR12(N<`OL8AO$Z!D zOyWNL%BqXcV``zVSCzuLURxJOhaiO{QFI9zE~*}Uv)hJ(*%q%b9@*hmD_3S2K~?V{ z=n_yG4lj0HnC@US3QrD>Q(Q>#YQ&QMpe0n!<`N^DFI0YjB!?+ z^D*qud;TggIoeS&{(B4}Zo0b1NIYL!CFBF&sWV?zDJXROoPuMC+(b>vgT`Y#Dv3`g zESjy8R%)?ryZ8Zc9rw-UWKY-RFV~eB$H)8cz0dckd15FPFxBqRj&^5sZrXRjgdsJG z2}@XgF!W&>49Y?<+LcK{Sy__P#=!zt%4(%WoYtAdBJH8~v%}*&%jwjugSrpffP*+H z9?&nwB_o9`0GIeIG8gu$4STs$Ct(0SAp;xa?EM$(y=G<*QRU%fPv{gXA3N3iZ@m3w zR9s8cHH@OcEy02Zmn0C}-3bz$5Fj`~g1a<<;7)=B*Weo58rR_N?$Wq4-Mug8ocDX5 z`;2ja-23O&kM2FXderXGqxP(|s^(miio)?H`)?ssbcv}=g2<22^6R5m$B5^|sOqsR zcIj!Gh&AhD!BMS`ON$_^GQ5tFIn0%-@#CniU)kIU)9U?m!w$Xa)p!ywe9b6vB%af> z`#XE)8DE~Y`@ z_o!fC$?%nQ97~W#93o~n4;F?haCTqf>zL>z-sR(`MLlK-G5r*-p7tQa>Ty~=^Xmv0 z)Q4)YZHST0zeeTn^gGV=bRVtRYB#_LQT<>Ee05#hk)J)@Ws={W$L&S|(QMzNpF19c z`=*X3-7=&d>@G1hnA-*O2t$6h<3u zGS1{6Dr{{@w6s~=y$f3M^}mh7^6>zCpRc?%bxMU+#ThtJD(J8 zzv#pWbMP;Kkqt9z1;6&e->ck9|Ul(JX2XmI!LoAGnv)eCZ5TZ z{A!KkB%Ml|A7{#>V21o-?OY0pWO$kIiaZyyW7+6-JU5{udv)uGGKgU&$hLSWZl1`D zOF*nz*;wvX0d~c%9?sd#OImhIcIgTEkKLND4$_0aVzg9p~JAF{1Y6uTvLhO;R8VE>UQ}CJvAxb`)v@5`m;vj=ruSI?fKz2LNAMg9! z0U(F=-tHdMduAoJ;^dRF>JEq8>HRUzLEM@RB!9IBfDa*@Yxw}(I7Bh?2pVADb;q$B z-;y2&K#}jmdLf{qT#LV9VKy8SCF4Lx4I)Z}1d;VdS&s>Hjd~44tYoVX$!yVVy4`&y z%%4hH+b`27$^%jJoG@K>oBdM2%3-mx=^f%6^=!=PRA8^6^udvdIs~xSi9mk?q-O1h zLe-Qg6<_1z0hQ`45?8cw0UelHj%)d62>I?G0=Ke84-cO;YPxzjg>0*|lDFEo{{JD% zY3rA|vZza5SI1|FcYOxxOPCVpg|!ZN<=gCSzAp~=UXe0i=lPqh=_{&la?EVLpiIDb zfn1tP^SgWgR5sy4cJ8l*oeLM_Viv&?yK}j>jVq~B?~O>^Y*?c)qOm?vz&H0A;Eezv^A{-bo=P0mr7DNBLUyO`p>6T%53cuiH$~*Dru{9fF)SFcjf7H) zY;|}R!~+MihZoV&hTB?Y&vCQ1Iu?eV$gXYhw zO8;I+Qa#R}8OS%m0|Abp1)vi+E01kE2C1R{22F9evcR{iZZH=%@;Cw8;}II5(+~K* zaWy0d9GW9`;vO9R7|=qdKx4iF}PdkyM%L=IT$GYx^}7nB@?IsT|P?e3oS=o zA!6F!6nxFJ15y?Nfz~FlU=3vmHt=XN?UxR^SG5h=+=8CJ;er1yL;&Ye5<3ji)=S_g zUCu9v+gVF`ZaKmT=4j#x+?zanrr6aibDiJ)Si>%$Y5jajxNJ1OUB8>Vt5$_tMnylz zqJesAWE`<3q-U~*2d}8b+gbqyjwEfP(NS|sRpp>K>V*j=@`2)Cesw=@@Th$Jr{%68EDMX&5dhEkW~yFY7U`T#7Uvt6KJ%5&_yx5@U`sB#dT^wj_6Vu_JsBt z;=3~qTRLp{{Mz5Gop)uWo<7hke7Li?M25*zWDnywz73=-N&c{vR_po9UU(r2MI7yo zgI$Z22l6)=KI<-^?Fk@vmaSZP@Tm&P+jfC(8}i*)88*W$fRYXX#|XGh*#eF_ zf`FZRHt+dEeX*b1=Wu8hN85)Krh=D6{c{$*Zo^`WbTi+H|3nDU^HT8hxMEvc7bkq1 z)S6!vK2Buepr_g$cnpCk%FYosw!(q^ikm6L1jNF*iH*%i&;hY%X2lgbBaRgx7#ws0 zEQj#A`Qn#l`{(Yj!B|BcT;07&FwoY8JBdz&|LRn5;t64)T)!h_uH;RHBwnX5r!3&F zY5tvhcvX&%Wq+D6_`{HB)#IG-!S7-2A4dTGU0?#Y>QR@gbUnlBGlI!ufk2I|UtX=w z@G-^Uwt5vnlGPneUw?=a00tES7q~iIm$$+60uKbT0lkW({T#Nmqf&(%V=^@Er;PC; z0RB1Fq&?%2cX4Vla2y8ONvFikRnU7;;jVVqKc92PA@qzK#N&rap5$ix0dHF8x1IP- zGMdb795duwd%Z(?x_!wTiw2sR{S@K)G@EJT!C((j(!1Nj##m%3BUC};LuAnlCFuJ7CQ8cIpyNY%j7N(Ym- z@!;-RcN^UVmbgI$KIoj0US{r{09h;wzRI zfdaR9(QQ2*OFC8B^*6Q1r1-v381Z0If^B1;Vd&}BExtUegL{J#tcS7LbzY^6?s1?P zl_?HYtIz&w@Uv)JbXgq%Ge=7Lu*4cIDmGBo`JI6xWbhmQ(r;>yJu zcC+eZL6MdEn4)2`Uc?X5a8kETo|&ELuCFwAA)AC#X`W4xj~RU{sL$h?%z!kfTHfxC z4L`8iY@xEgd_M&?A@X_~LO;0+Dl`yR0c?jHE7spn`Dz+OKvi z@J)^+YOx4-JnztiC=cb1w^fN1Uv7ak_UwnhTWMLCP9FS+JG@Wq{jh&==Lu;R|EDIj z>Jy^;Ke$7$6idn_pc)L zMhrt%Rz=Jpb$$G$p*rc_oohb{(qlIDzwFHZCw3<9tA7wj2Wjc4b~M)$;^_V#t-z81 z0M!8o!uwiSj`qcoXFCS<<~VB~MH|nDF>*PfoZ^1yFpe=YSe$y*4bbKz0riA;q&_>- zAs1mRla*BK7Fs<}C-z)Ph_{^tGTtv)$t+=xYQBudLcuQa!3lC~NO361O+1YzpvlZB zIt=BTEA4l8TXXazj2Lm4L5dC&65;>Ni+!X>sp+hezNyXYyNtbVA7{rIQ9u<^fUCSj zp-MSRjQw-`!dKQUzPW$p>iyYKy#V{t8loOSA7+2u$8Q_bR`mA15BymC&{+2pC76;R z%l4NV-P!Ni`~>5_dql6IPOtF|I4S9^E|`B!eUblIRAogVne{}VLfmv5Km>4Kc+af` zaLj(6L@lgJ`71mH(v*znLZ!W}Mj<0gULo9?8NK>i5(2ac{du2PH(*NRdwl<5Mc{7S zeB@1*hKEaq-z0C=-Zs^Oi3tJ^81wvr$i6ry5OX!h)4X;>kp2eq>(Hj?ZFwFTXcr9t z^_J`KGEvZlNcDpwd(WXcNe8>+aBi47D?`OL2H2d-47?T-f#NLhoVN_=IIX}HMPO^= z3~owAK6Gnmza^BbOw6iHn0kIqwO2+zZKMrXxYQv42`u6g_L?iYg1}n z=NFqiNbVprlmKdA*uyxJbOQcB@$ zyw#hIUwzh1cbP2&_BP%TYP*PjjHDP!$AV-P&GABjB%uP5C$l$Hfe-mAuJ#`?T^)HQ z2t`LC9>_^c+2{@DvWqz5!na?ZY0Lyf{K_?97TLSR%jlo2=RXK(?!_#V zjifJ!YIxm2)3ut=INl*EV=7>DAB;(hMwrHh6sU30GEnBErSwXV-N)d7$+|~!mzwG` zi@UxDBtLH6)5;CHIan^w8tPJ1+yyfjR<-o`n5yHtu+K`SMWU{H-MV_VSEdvHQ zzrM@_V~=4uZ?aXPg}I)Ol$rnb=$Ez9KP{C0*w8-pKqZHH24IlCnQL<7jRyyZu(j)b zh^Zr8C!n#NAnym&&FjA{X;gV5F{Db!K!SM(Al^Eqz<@@p4w&zR_ET}POGaL`vPint z`w<$^K_j#x6T(Q7(eI`tqYe!-y8*PiUnrRd_&V;a!bBlQ8LYH;LbYb}I%f6EvhDp+ zQOWR8BNWx?=PB*qk!aWd?D%{5Licc1wJ2$o91`>3spn^oCG6UPFeLDh%(W{OQ(3iCDH9@Wpib2 zYk+;tNx&{|%Pn@(OpLh7raeq9$$bX5EZn;P>fU}i;etV6w()TM=gvkeeC<=3@D<(6 z5PV{P4-^MU67Kb>FWwhbkPXRiCwO0eZ^1{=>pSSU6F1{ zZ;#9}O{+FMf{Xg)t^nooRpZs{pT$q6xwWg7^hFZTtev&qK_}PiwC0KQnGOaDcf!~Q zTB+7Ba}yk+cLxi6%HN2x{(i=we1CNWN|g98Qt29w+SB?Vdo#9X;%Agp-ud6!A@~%t z#{au^Nc^vu_5XeCU{H$uA0HR_<%W0cwvA7&FaI^kM2UIAxu+=f|6^l>j4srT!5~pD z{#`qUpT75#{GYW$^51Bc`$>)dBJkg671=>~dcDFW@KG*9<-~gKDmEa)iqV+Gr>cZ= zJNxiHTntBIz`GAfNAOHR##$6#9)ktNUUm^>{kd)0jpr-Je)y{6*t#jF=s-%ta%KJF z+MXijx8p_@G8^wFJ=IBgHuLEVW+sS;XfH1wbjo@zy1$TMW?o6OsPAl%@kgua9PVPt#AUF4P#|pz&LP_e&~f;HDT0p*@>_^{I#0h4 zO$)hIPn9D%(x%Zkw+;Q+oq1y#h*pJCHtCp*{fjSRxZTG;X3xIo0|8?x#D%)vGy-q- zP1{WXpvgauL|7DUkcwCU3&Rnvw2uKu@?i$w zDiV+Z25?aUkJ!B^!i%BYqFCes^h;}YJ$yP(RWh#iljqhjE&dOQvshO&9!b7@uv1+L z)mk=*Uu;ryOb%wAVpGrLq|T_c!*Sd)nM6w&wU?~lH{a|I#3sAe+0!AvDik>#46PD zy%+7p-lD@rhj=>UG(roba;654mhje?uPS?lt+3>wa(tAKk*?pHhKip`PzCm6E!u0+ z8@+Og$&Y)J38G8UWab!6pquYd8K6_;#qzu=!on{;d>0EUg_-5haa*}(cpjhA%lc8{Xem%{1Y7f zA3u^03`)a-`he{t`Q66LNl>j4<)QKAGo-*%mR(da%w@jI)z7W+9h4UvI$LcT23wM% zUXq z6Ryo9$P&`KaCtxYi4on{V$5F&(XD;3%?+Y&gL;^ zX>U`&RNWe8;lv_^{)Z8uf!Ro55do*d30bz!^oW$1on@M!s*-0e%Fv?+LQ}j!i?qab zSjlRz&_XO1OB@|~sIap(>u1QLW`Gal=$pd(x&+b(i0-qK)c*kG_W@#pQ{FT6_a}C% z%k}<@z^Y{Nre`GsG@GNd&r+{UD}A&q6AOiWGV~fy%p!^J-}#c5BS!m076|L5+UL#j zTD-d6Jg@VqJhxlGkd?q1($plBhWmY=f23D_<<5tQSH*y$p=e!wNnF$xA=sVymAbD( z6I5f^54z0{XNiRgR67AYQnbc-LQPeAKR&PzKi`v9f4E+UyABJx3-Y~b@>wODz+`Ve zr>6=!3%sYMCdsA(t95v0lH%E^UODtN;<56R6(ZiY{>C4UeWr>UL$`}Y|2gB$E8Jb! zFqw*;)dna0qlZkENzk)OqmQ&C#>2~VgfpVORry&}?NiIs-!TgfDg4qVf$fSp%;?)U zed1>u0L`iOxI))4O!j`Hczdgn+mE*zgw<_tJ}!Qptn z*t#SExPn<{+IDocTD%`gOnl?Ipu3K6sF7}YgR~iF^hY~OFlb$KJyn-$&?7|@*JNj; zv>`2kz2-}+_g^1kxwOAqWc72xQ^fAVpy#}E+tH+|BKHSY$UZkzEiAy@+mv^FoXGtP zXzqQTJU8)LI(l0SfU?E#EqIs*HH2w>q6pQ3cNxxK<~}e%Z5X~vbwl(j*5}$;3dlXn z{=kjalw{?XD|&wq%fx=^I^3ug%Pz2@V>>fhK7<%q6OgdD5^x^b@p;EHaV+LNQo2-Y znSS}Aabpj=r0M5;>DggSttTlL#b;-hbyV1ovJwZ8-|ei_J-?EKhFd_(yVXTj z8z$zTQ&MPnbdkm?X376vx9L7Nn;FnDBSN|TnN2rU6+@Jces}N zPErgPJbv5hXd`?db%28dxH-qZ)b;$N9JnhFDrvEEAf8UR32wr{UM?W!{XlWRokt51 zUO3vMym}vQ??ye9t^!5d%oq_r?sgxt5tMAs)pFwgAeQgjWq9(Y_%n_oE`UIPzPy#X zj87a)CZdXejC*LW8Dm=q{M^f0H?roPQoQyKcorsKqXX+$r?j+mH(65wMLfP&Cy8h6 zx+WE}6sB8<-$6+}9r^pS9{BThDWpG>+s|e#pAnlmEcA0ouJRs4Q?K|}|4@92$w7Hor|^AB*xhHxTU8C?JwN4urR*(d5dYIeczbu14@u^h~?WzVwV zq`;FiI4lJ?Xap{-yiR=8tq}l1;@ICn>c$loISwcw|9__dC7^Yxkn9$RO0C2;%oFOcD;eCJ7upD-#dPI+T^45a$7fu|vI zf%tjgQ!ymyk3CKVWG3zmS`XZSa<3h(@8Ih?C9{EGo0QnejF1d zKdG=S8CxDaX7Xt4V~)8?ZZAEBxuq5`qa`V0^pVvYfn$FVsn_^v#s8Fw+Blvr=%XLX z-^-m(^6V}o)6q5_v!jmc0n1$ue3tAn6fN7}%xIipv0{0^Opl?LzdykxaO|HCDU($58oZ zxl=vI?a&JRxVT`CF4eK?qQ;0W`ov$JQ~YHDc%gj(LPLjbD@i<~UYg@#m$UVNFs`n$2&xe_gA>htp;X`oN-Y+Vto66sdVv8gLWG(zw z3|lJXkFFs{SI~HeL+gZ4h`gD``$uEkG=a5`H&6>Loet&F(J!HWskVb!u8p&q*$5o& z&g7eJ+*tg#^tnnyBZMOip_UcB*w}IXW5K9fQA?Hmx_`6@UwTH$=7`;V@dmjzOPght z?2<>m@smoh`Ot-&E5oLcv0;dmBR2XvemBxOoCFa^WJsBeP!Hmi{XmFs`hWH{?a7YHIbZGRno(3hge`#}z;*|Z$- zKMojc&tT^-apla#WyxY4^7L7`KkTs`JD`gu&#w4y@=dmh>lR^j%$c`5e}x>q%xQQ} zh+RLr-M{PA=D+D04&zPlcKDg}oJ;40S$3sPSmoR``eVL`eA?fm@Uv*c(?tAZwdxG#!Dz!D27bOrh*s1TDONqU3UWw!uxZ`iEEWEF< zPda|{68A+uZIN&}*$CO1e|0r!Lhr3b<4NL0(i(=-Td>oXxglM+bvbD4=9C8QdiSqSx26> zG+B1hnm7r&jpBIU34Np8k%mI&*>P;*j3+ZbB`;?)Y5Tr$-7lq6TOqFSVElu-Zu3pV zNwa<-@h^{m9@=S_^1PV}*XV8di$-(~qlni%0!s`XJx@v2qG%>YM4KN1ZG6F{?Z((- z%r%jD8;HCJAPPIsszss;tx!iWe8q2~tzIaV#2a#N5 zM~^FAN(o2;@dTMY>Vw}9qqwn^w*_)>jI>VS8iK84l7G~2X}F$B^cGXGWb#HGcKywx zQNi<6?&*igOR|OsN6hO~{4g<-?khhdGfoa0AIFSdRaUfr28#gQr>|iAczlc{SJkd{ z)&GSTL%BkJ9+8W5;8IeMeB7L&Oe3Gt%SY7D-FB^=hdi8!hrzjeIo;J!X~E~cB-(l= zEwc|K+Z8p|dxE*J3N^Y6P`*$sq>$COtaz^uZcO5xE2vr#&)1I2o7LwU~ZZ%Uuny`sgde`E*$DJfC;2+XMw>u`)aRY_a!&p_GZ! z@$}8ci)GoPcFRe_db$rW4e&u~SB2H0x4B9TM2v(BFzQQwk~B29OkA zf?afqm);F2+W^Kq_{4J>qxw{FdhgjPWfSdRuk!s*+M(u6f|8%a(rl^^+KC3|;N?zE zs^4Lv&FEEX$`bk6_*VHmehayDVJIgXpVng)UdY?*lrb%Pau>tWDSst2Z2vx36E|fY z)f3BFft6l5L>!GqObn#c(+93FN}B9t3FXZgH!I}#=LMeh@1RHKe8w)6L^s?kDJGwA zCKHtRE4f&F?=*ON{nP4^ZI68_P@aj*;zuKn*sFI#$+$DW)9X(?nA84vJycuO8ojyh zfsv~COJ6R1twW1#S4FoT&I1dFc4|13&tsDxvic%I*uRQQy<^x`ft!yiVt(T! zO!(xztkjMY)CF74Z^9NiJ^1_zc6k3m-1JcD!|T)YaKDbK`;>2?Qy)DfEqNT@ii}nq{e)F?ptFz8(AXIdt!{26|SNU{@T*#KB#8cvALc8j?MRljp z^PNrC{befKX8Xwk>yV?5y>24Yk3b3HbnnJhKVZg1-N15t?w(^uOQo%H4dU;Wovg3w zchN@u*ugw#k*>rmC;6!anj8y_i*u;yRTMS*J9xgn_oB`n&RH45EI&aevWInKw28j-;uGp38t8{#uZs<|rK>BnPI9>_eQWmnUW z*T=quQshk&S#G_ZA(D<)p1PT9m5b*F0|rZjSVPOT&Jm{>2%NRuKJ;L~ES;I3GANat z%y$0-DW}Yefku~Ixq~#oSd*+@lObPe7Z<&uGK_smo0F#g3#(Ib-EDUX8@kc_OEo%v zG$A~|1nEjT^g-QYT~>yu%_OoEZqx(pHNJfPC2uzOC1IOXs?Um4u-$Ll@iuiz<#N|| zdlVFwieHpyho?Ru3;luHW%Gp^6VVfc%3Ffil|jrYLm7X^Z}b}$V~{?o^ySEh?>P>e zz1i?N;K5Dir$8jt2S7ywaxL|OOn#ZSTho;l>Bvg?glI6k0Ko(m~ik6!Ti^$Co#m{5(?i&~^l&1By zgw61KtbdPr&csiambNuE)SkB-?aQ3rBbL|Rg1XKU(t%7e{Z!A3mrS|#_Ar%)j61?r zox~JMb^rD<3NR-|MN#Z9R+_Ao zn5Rt)Y?%HDgjJh-@KBWLYk_cx@X)XX+kXhO<|cT@|K=ueqV`a6>ji^J#u{0%Jw}iP{tZr)t<>(Eu1D@HV!KaG5iifSd|a*m z!PR_(n#*wMtcpbSz2mI8BRw25HmqBj^y{}X1O6ZZU=(W`AGD7KIKpu zM&TOF>`g08oE;PpXw3?GUKHg;0_YeSdrY24f~^~IbJ`N5j=zI+v2MU*4k`WAK4&K$T>0#WVOW+*1 z`vo$d6tqfjEy?|*!-F^shMT`$z00pNHjY*Cv}F_DnPEbKwLPw_dNCOAQ+Fyax~2E? zXRdXkbNkwIIe0wnLE{r{2ffL&00-d}m0#yDjy&4So_Lp2^R*E%jrho_HVFu+U_M`# zh$%ARpxJZh${BQK@jUAbFLoc}_|v*yFV0~1$fcx2$=b1=k$cj!M0)1Af*!lCW}7MX z%2XC31_!;AW2g7d6Oq?&&n>X5WJ8tWf^uLF3$_KlW~odKdLx$gD*TbbMWv;KQFt_- zadjapjonyNLT)E}Rv~Cl@T7>vq7ITs3mZP@QL+Zdylg?5qZLo~8JyjAV5QL?Rv%y0 zy>Owg;t;hP`rf#UeD}EiBkC0Efw|~`1b9e#tG4D$=O&QRzBr*A?RA^ceQHw z)k^@(=){x1G5rS>9*Mcb!b{Y)0GDU{%K)2a0(zXDu~aSzBKw9iS5vbJCA1_k#5err zXpJO=b-GaBP| z8yLM>Iq;h5}KiBLYa2keZTn#Ws(h7{GDnU=0hKl`jg~M9;*cSaLV-U3Z?|jNg7Od zUwbw86)~6TaByM;q4U)Ez8hv)wEr`<_O_yf#V^tb(<9me!b$d$=dvKP=J3_n98ZI@ayZjN=iV{SIN5T(>a1UsHUo#;O3^UMj~W- zp%*Xb#bMxvq>J_10)f2W>6U+I#saJS&|cLr9>g!}vE2Ef>EdLwq@bEKK_21E={T67kfWE@gPo)1&%Z;HL-Z<_hlm>IJOHmszPU4@+H#l=5uldRsh zvzX&Es~-?el;Zp;lMN14v(pK~uxyk2yp>A(8fpbwqKpE{KX2nlc(B`x3<`^W8Ffn; zZ#k+VEIMIK!!LJ6@dIMq^qAsd-{0AG=nUg_0o#h92@VU==0r$AB}4`|`3Y$i?HKP{ z-RDrAB?M!x>8K}+xA-cw(q`bvIF0F37HsCN7C#eI4axMuR=^=2t3K1rI|%p@&AQYgM%dSG{Nio3DkS9AOx`esMoYxvDUgPY_0h^M9Rw4jWTT{r4_q=>2uh0 z+jq%odre6FQmWe@QKeTU2AvW%Rta6f5#BBX_uhS{z!5_a!x$qEBA>)7|~ehoc**7t#EGc*OSskyE@4s*L{T^ z@0GZ^xYR!nY|~q33LmY&zQ8y-6jwq$DH=4qpHG;i(OM1HuN~ZQ>UKP7XX!i(rV)QR zl79N}1Z#Pkip%R`wn*`;)-)JyYcc%Yb6x_zizwfdW`hO2mmq!;uEGZwWi^BXYu9b! zH`cl`v`T#2r6)3oj#)z|iYc8QpVpAazUtX6`sXvDvWVaX>MReC#y+s13A#K(j6c_| z-`JdnpZU^F#@^(65pz;s&tJGR^A_-^l6TK7{wxa^mC9RLcc48MrU0Wqn>RVE9@ULJ zcSiICEffJVhEx;y$R*|{MDW`>x#uT9+=7-?lWEahH#m=)rnulr;c#^0?ClUTmCEnm zRP)KMoCl5^YfuUzBVz{*)}J^e@QF!aoL!pXXB}zs?Q4|$Mw&3myMieKg1o|Pl$4(( z6Q$)jKMxzQCp}yT*=h<2p+|httsE3hdmKStQlpG|stGi+2c$33o>?^w)VLKBTxwZh zD?Yb-F{`n(%@|gY=Jh#2v3oJUJZ_Dy=^jSVMa0P7)p{`!`WFXpy`0QO+Q^GynIoSVXX(t! z$o^~H>mp*#PX~x~XIQ?ePYPf5?e&iO0dh3rx!^uuxS=p?KvHwQ&X?+{%bft%$(lCy zpA0?UwMZxoaCR`~rO7;do%1!Q?7O~9C4ZJH01(Cf6fjn-?L&vY^8-V?^SQ-*neJj= zPo%LHW0WhGmaiT0^2D9aBt$KVE`~RM* zn5IYqs3-`R=sHz5Xx};3t>XQ>zZF@or;9+OX=@G|dI(%NeOY<`B%b+OIC8JZbDl1g z!>ZOvYkmL>bkYsTuPe$(s=J+w6JEjN%gJ!(2A?F(=q#6ganzPZI=4AFMXyyEV&9e zfyvzD>PF*bL;0U!e9Z#Mcs}^_q7w9Q9_QJccf*}4xCu+C_1R@6jhYwl%3vXm8B%x- zQH>}{awFouJzDYb!Xz`DzX@~~!i#|Zn$yRmMqD3kH0=abq?MN-r20M>0%pXV+3&J% zIy_EWA_T2?7e1KPw<*kbL~rMIO@6lUevuYPA{4vXI`8nly7YaqfC@3(kh1Ic5V+d8 zQgF=Wdd0fj3z>K2P&nlOXtd2e{rqvf8^OOva!2`5_Z#FdYG*w~Fkh29Mp! zJ%H^6UGU{}(KUJYPhCFWbmukKx8q2C0ICN!lM$8l5vMt;WXP6K<7TDK7!EE50QIMG7RX>& zKj?1E5g6`J0}zI-z?B!tw{1fS#Dd7R!O-~ii9LP~01jAzjcp@Rm^mk6!o0I@zd;r& z!XB?1E=Bswfa~43$0G@dHYh;$!t{Yg|L$_b%O7Sg3;+`ZFg}>XmM0I*nV^`)*Ew-L z9TC1G0*KXXZ>zm=;Ks`zek5^;i@1*1a%gc})=Lp<#wls~M_{wp!Qu-S!(p}%6%H;v zm;xS;X%GI?m>h1+6uRgEk`n6onM2pGpW?61$eOY=jwN5obLf8ju_vYm5&Zarb4#Gx zyhHM~0Tt>+NHbU7dHyc?XFbJfTY)j68JRXnEH4uCZKUvvbu91W2lYDy<}G*8!FsK; z_}Q*`hBjvTRts0=*|z#Eu>`bRysyR+c#Jnd1+t&cJTU+WtM8q}UVt1376wFpc?9x- zJm7H_y9+d41SErt$+7_&jKweSZhaobj0XU2U!bHBW~janr-hRNYsG3+FevB`c)b`7 zNd^YKJ>C$bz-@pIt(y8OSoL#*u;y}tV#oWq$c!lDZ5iU7k68Nh?4 zPo6Q=B40v_`mT;nyrvYRdds_x2#57Wgwjf+g#zE)T2++863@(_i$J^F4p5bOfGL z^0luc-s$Os>t>aO9DIK1;1$7NH?JcRJ7UDC0~POU{Z@wU<&#`yP>}YE4fs*daDM$C{~CK!t8`$}vzg&?V2`6}!3hw_`^Ubf_kHB5 zEn=HP9)RZ86&o0HIzC`L`PJ+Jh0ztYYo2JXkjqD*_By?ZINUfeaO$uA!#TjOoQ=fx5qW-@M~ztyhJR zm-*baMN8K#D;mDCU$aV+?1p$zo6^B9QmlwNY&YO0AHWhRkpX}6Od*Pxquz9E@23&^ zuW5Whh7%v_C=BhRq3aCaafvn)_s^INrHWr}p{}x~m0?G>b&B3lE4f6b+kbNDetrFO zIKNwoRln$_s^nWIk}TVY;xjww-&YYE@C!+*@E?c8zFFX|hot6X80{~^|6eXIW`eJ= zZ8C)MLa_fk&u)9-1_l1hEp+)`ymY0fL+<~Fm;S^p>~DoJR9biVy3j+b8XPD?OYB^p zA!*n?iD0;O0}Re|O>QW!lF}SV7Ci#kXG}~IW(NWW_Y5+3OL36&b&&q$6>2`+sqWv3 z!LI*u(%;4@1U=n(A(;!e^Qda8eEhy($#<3mW>pgfoo<)VG|Aq7xyAK~sN-l#pV6Y4+ z)+VP$!K<-hb@>egO4@PAM+>2ef>)hC8B;gLS2#HKPWjPuM(ksFn#*N_tA_wXiFLbU z;+h0;i2x^}&0MD;Kp(?V|MgsHz>TD69JkaeiaH%M%f;*sN6EeVvC~9X#?Pw+?!VIM zvJAPKxxuW7q8JwkYo+f?zG@D6^9;1!goTm@f4#b+z!An*u>1;JL7!~G+`M}Gfoa^K zpE78JacTq^Wt;^wlk9T?`~GCJNmiZLkw@o0gvHxIPeTF>Gub~`(65$Si8ZzOpo+pF ze1XoERU`@@V&8t2c!IT7HvE?<5xzfYQ?hp;7e1QQeyYVVZ4R03CdDZ^9pjGQ1aZJK z=tWYfCC=s1lY-fCF&kh5#28J6h)p)WL)80zTRQ|Jqcv!}7yrJM;z0iqrJ2-Een%;# z0r{JFj#s0yKj~}F8gchkUm_#OVB_aj(=v(m%OE-G-yUyq!*0ci;q4`dC3d+{m&{Ou~bXZ>LH zwVkEDuGur!dF>e`>Kr>`&w|(rl!xHEjlY;cVE?#F0pG9~%Z%vnmtrAW$>?92taBBp zpfTOwu-xcAThhXMTu8~JFxRGsxf`$~?{+0xLcJZ%?`H&_%RIRz9K48wADe6Zl(vGz zX{LJmZ~c+Ncz^Jj_GhC+c-kXM`;L(rU?EzqG|9aZ!YrXa@J-KRa}vB2nZ#(d7KxYA zsTl45xQ+cjVg9cTC@B3^+~vW37?8|GC_xKs495`{Pzd4cpMJV5>r?78TSvT?S!8N| z9&^A51zp6-Y3&Pm#10QjymV{3+f^C&6+(xYa4Ee71si(J)2EZY_Gsr=d`3T)>)d+2 zanfG9GiD554$O-#sQyG{S?XL6+CoO~$RSNOT zH@1lHx>SN=Kn7?iy|_Vg`TZ=~mwM(EUtnDD;>{ILNT)gK_1ZkE<00}e8QyeTckbZy zVbz4EUmF)pQ^k&+PYR9cLvh`YXz>GDRf4d0JSKOcbG!3y{ZU?@#g*jY<*O3vVpXTq z=C`{vFPV##2KV-%)oYox;x-j{^P}yLY!h!xzvGWU*I>$}7E-olmm8kI&ENoY>To^x z%TgER4^cgPKllmO?q1rJ)kop`*Y{>nYvnzvs0dIZJ(q7%ic&U5T{G%>IFodO8_qXE z;co!pbeiE+Jc|hxL;|HBU4Nxrn`544Dpt|@O@(i04e(K#_uD=dNDAG{aKTooUngsP zIS*MxEX zyoEdqLqB^l>8%S^@;5V2zTp>je&h~&%gr_TmeK-H2?d|M10J`1+`Zm7I{rKU$`eNa zqiOudKdks)3}^XY{P1`G7sI8$EX!-$nJnE^n-9=KB`}{lo+zPrDF43y-YNrbw zS-2W>V90Z|y?P(A#yTD+e@tNC{-TMJ3L*M+$jB}g71ecl!Y;x-=gH8c=Ud)SKE3n4 za5zCy`RoDHDNd=x{SDY;N&1>>S`HYffFf)PT=6HkU(CTNFSrL8)}lN*HQbToCxbO6 ztyKuxEkT~~E(b};-}ji>f7qjh zcZfK`z#O-YEMbVJA^?bLgM)#|REdesS%_Fsj|pN|ki5Fz^wGWDMsIk3ID&?~wFEA!x&J2D<1TfML3X&3PZk^&)dMPzLas=UC`e?9sl!k@ytTk=4 zgdv7j713B!moL@dEzSFa({MuXkJu?2hmJ^8;*WQ2-md0*9vQ$=>$Mxo0QmF%cuub> z^>Rm!CLe)^(;y7@ll`dTmydBR6MWjpo7&uI*}5GTyM!ZzJDWf1246B@)1Q3lVdY!i zs=Vm&cC}0AmpFOsJ_zf{S1mjV@a9kHU#jV*W0rPe6!iG$$B`@Z;AG9S|3x^Onhd~m zHTi%$H*#wNfQJ)($02_ZA2PY?*Y>PB(gmoQrnB`i?hfrvqI2FQDWj#Ds{e`9pX}M1 zK|V0;{~kAWrdWjc+ZA}NrZ=@a;yh4%xiDn+GR8dRWSB(G2*cPMIA8mJ(e;*5aWo6p zC>Gojfy1czY3LkJGR83@5GxVu|$m%-iLo#5^^eR<9~-)n2#`ZGN>y{da! zc2%v~awafjT2zX>8PsPw9})f2W#_9QIN9zBUw_u*mXbYE96mdFHW^o#Bz*AAPF0D( z7_ZEOH0F*ntQKD(0jN@tr89Lh)K$~$xk1?wMMTje))Pb%erBHQdqOwK`~2^>_w(!9 zC;|h60RscTz|8(PJKN>;z5MqGtm~DX9T|t70va~PZrMBAJ$pY1MzkfSzvMXUWc;tU z_y24Zye5pj7Q7lm2+WP>+6B>s}1uy0<#&VS9oUMV%wk<*8tAYr6IT$pxxH>PeTxv8H!R~VvLC$J&jmU^uOU+f#&m?To%u4|s}z4Ho0EzpN62P?a~l=2m}I1`=1 zUnD8XYK_KpJyHGtY|>&Nypj5o!#$g)G8RsR+DvQlmpX5Io3aTa^&uAgOL1uxc4&~p z#C6XyyygjpKHxKR~R^nPIQu&->ZQ$OVAh8)BY`)@;1wiVy~K&3NIjnlj2Ul=B+ z(VHH`$i24>^nSzdth$zF-3mhgUyT>oegZz09ZllWkfqgB6Mt!?07=Te4Fg=APAH8~ zj)oR9A8WlVPZTAR>a$uq6_nM5AZCgJ=JA>+k8AjAa*Rd{D{;vwyo0gLYg`4mm1W)ioQf4%J(U zB78!)!BbLS@_OU1)&vMvo9jElRcPwBjM8n3kE9Xs=R=mTt03ww&z-zH5%xS;8JO|J z{0P}3*JVTk(;PGCGF;Gx4|kgb9=AHbIPZ9LhbR#{V{EbXQ{M5_qmAlglATRc_thJ& zFl6V|it>)|J@Vm_Z=%i2N9V<}=vt$2+!glI&eriy$(t|7Kc7314*D~z=NH&*VDC|` z8ZLvHZN>OCGdBRfC5P(^w~oXLATX)o;h>XF#|Rm}8U1u6AN!0(n2;Q)t*F$0QHeYh zyVETue|z_3(*2T!-l4@Da<0u};8Arb?4%PTzD_SVZJ{_nwe#hrIptHyx8I+zn|k8W zNNO#o$>x39jMSZC>8+n)A1yAfVP%T4U}2e0#oWwnRypCNes%ts%eap%Hwix?h0m=h zBKPw4lU@AY$y_(hKW>R$(XN0hNl6Y7`p(**7*s=b5;A~fg5C~Zca3D&COYt#6<6P6cU9Cw+{7BAw?aGRGIExn=ffnU%I%`Z_%Li%#x0DYJGF zUO>}0NJ`+(s==q>yNyv4EyQ_cEfc4^dpMbV<`D)HNZ}-ZOoz8x_eZBAVE6CG+zk<{ za#yiSkNQpK>9$`V0u1DSZzZhFESp>(`0)XM3Fc(1yhW&-53?ihP(0R2w8CQKAek!f zlAOU$3^jIyX&%$_5KN<#R#d;Tv%jz-wB@oRI+8>V{%dfgvl=M7KD%PIG;zE&-p`YK ztOqtsA>-v|OPs3Rt~8>FQm@u;1!PbzBVek3y@n!o=5SxUTw{0~%tT81=A5m{?7(K~Nwe_KK+F@vO^G)bz zCh*s(%3z+w-JJMRY?v3Dt%c2H+~H}kAw+)AG22) z=Y2|nXB5_vo)<*~9`}OAtja}+rO;1Y&~g3{Y5rld;5rEB`ZA(N^`c%J+sb#>&DF&a zWavc?g-$VH?PzH|uj(2D1j3hU1!Vb?oo3?eU-&#HTYIXIW zv7i>zuVk3Txa24uzY9#HR>&>S=SsM9oi^z=AoU*L(M|_AHkVzWC2O%2tXu(b`F-U! ziL3c{Rm}LW;5?U!7!ggmukVXhphs>1ReKf`uF0 z#v4&oE%?=}#+DGrZqa&ReDLL6Le>?+Z9McL4G`FzBCBfd531Og_GE>fSR z!q{Gi1pmwrxc+|w(uPj%g=5gFC+;Gz`lWW=k**OkTfNb5rbrp^Zr0`#eFQQa#Su35ts&af>4*{(HAw`^ z>Q2wWf2doD9O$MHI2im@3QgBpg<8tQovOzj|J81gt-;Fj=`8MV(VMU10!-iM2=KnX z-AO3LQ)iIrDb-Ezn$`#`slzgL!Two-NC5FfPw@EH(Qo>CG@N#YI;qn+JEyz7h8|_P zehj^ylg$Xyxu=V=jv6#B!$M~$QJ9fuI>YwBDq7Fz`@jB1+Alh5uP(El(^A}DY*-6H zt@OYA-*JxOmtsDzDwZ~IH%xKmvd6RdZwTB)-32d6Ks% zOMA!g4QcmDi}q6k$+IrO;-!M-;J5N8+dPtX^^6Z! z{FLf&a1+D`nx8eS-rGyQ`6|h5uh%_3{u|3Wn}DM8C_JyUw00z5=AUZ~fAD5>{Vk0c z2?!5|lt8kQYeGVKEg?wO(s|y{&x|!t--=C*xmWvr&tD|`kqvD%y6tR?Epda{; z45uj{zsBR2zPm1|E2y)6*N^5?LSZ5I^Ie9HDKCoP5Y`tfzY^W?3NFP1R&3IdA+NMJ z_4hMA?f-h(Fvf7Ndo^L7bO7X4;eT4ZY^poNVhq9qNHUJ@p|&U(BS^I;-Qs zE?Ck5cycIKIljMhbA1BcCbc_21&;eX7p9N4cM|q>bPg!bXrLGQKPtWg^iktzBUCyQCoqyxXF*$BQQTTgI`h}idxBA;kfY2=|(BRY*`q;C$n6C(Yw?gEJY2{+Z0O}y`R|#uNqC*uaCdGk%aR(Q zs>nG?%o@Ex*BJK5*)=|PYeWNSUf)0FT}JLkak{^f#=jU=BC^EK2|7&O$5I0hdk>RZeXpR z*l)Q28KRHp&X^~0yl%Q7C5UA;Z1fZFKQ@WJAJ_O9C7;;Bwv5cO7tKRt>X1Y6^K*eb z{}tQ2YH-NQW1fUI+^IW`aRD#UKM^8kdGOW}?i;V8WU+R_eCPRRl&%)dHRgPPwh#59 z{T+&!LX`NL&elX-h=wutOOYlFR#Az_Eaw#Yhzqu~y8}#LgNWl?>ctljh7!orIDu}Wp-0fPkf!9tHcn#|b<2p-XvFI3`|jdG zwKoXmM>@hfSATVI+o#9?0Q=&E>@v#N5iX#u)oet!8eS)SwPL~imJE$h@9y+k;eE=> z_dMhU6}4|9nU3)neMn3)l1POZ`T!I^DeL{3)}g1))1E!^LjaltJ$_V=w{zv(SR^4B z^KgY@=j2O{0W9|S@0jp~HA!COxb`k3I{BBRjItmmH2KYkL*=}IX0mVEeWcPDvRfkl z^BWD#DSU}2tMFV$szkjWu2LvqmA+h1Qwk`g?Up4%kYqk_z9Ve6;Jj;IkWx5`R}HC; zyD@LgC6B5$p^zbwlaQ+hrIsku2=~y*J?7P5slYm%{ud>U)Pm=Y~W_2p$mef!-eVOQ6OZjrDQm6oG2bKSj3B1e>GqJ3UT z3`u_?;3PAwLuq}WF}$CA&0^!i=k_B@N<-?)uvqkQcatW-ub}}EX@*q7u~Xv+2N^#k z^E^5oBLT%I-ErXoDK{V)qy1enil#KbgaFzn@iT9LN>qGGSmcFWhx1HFOHQmm_y-3S z#uE(7U_W5#S_|7!L5|FH-@x@FI^Jg7<4oH-T(~%Qyx>QM{!X%>`-kM|#U7SmqS=7! zl-#P;XbXegh?UGI9tY0{lt})zc;IhQBT!7bZbdZhj(R_QCZS>ydNROhpE3K%?m{T{ zhHO{#%}nTlO;%6`qF6gt*oA`qM<-vq$(0ZFfUyw7IVIsvA(up)_>v~uvS^ho-D~DpEc-teosZ*Lmqsn$YkK< zdy8QE7rV}?qnq>S&cv7m3zNL zTohPDd=j=k%W_nSdoR)RL{@5k(j3SEZM+-y2lNVO}^LgRnEtw6z@ku~X;wSp5`W=PyF8$%L9Dnn2Kb zp4=^KFdhj~BdH(S7}q1VjicXH_XNhJ%<9f~Whr?4E zwr~#KkDE)v5WZw`@Zu(55%SRVCVvw$aQb;wkC*K+TCte7U^Qa*U=U_pNc91Xwf~wH zW@a>JGoxKyOAv=tXbOY0lTXvX3aZc#f}4E|eCxZ7Bz_hqykx>!lrE&T&a@+D%WhN= zpf8yPUH13ixq=qm76X?{y8LEl!;AU9@t$g6yQuu+r;Lx+tc@^LJ2xDb(fpurT7EHT zi02-e?UY8#;)+38d9J{=(Gvz%@{9h{{S|`O#=Lm)ODOj`MTiI{#}sU!{JaP6@m6-t z2F)uebc=B5jWiA!upji-)V^F^9S)(?QMxmm&m2x_ za1n=o$IxdL(Y^6@K}G&3f<25=b8y{9&W#4NF+(+AA+{#~4|C9t=P=H~C&>k-Cl`ew zESuQrS$@RK9xB9}VLl@NMLfo(2#nsAdU%9+;f zP|9vGa;k_@Z^Ai4FO0RWS+T@2oEeWxGZ8`=n#qYXyV?PHp4%1(TSSuKsc9kWh^0Bb zlI{CYGxruxdF3=!Gb-at^wX8=?)Ne$`_+TH!ckgOA?g%O$H9w_SJWMh?ms7F6Ql2a~9~=*+_I99_6iwaBhkSZ2NbIDd!#&A<(fW%X6q zYG|d^=x+5EDM;lD=jh^NsVjUOE7y-o`&mak85*woxT~_z1e|mARCHJuQoLlxfH>O} z2T>d;FKJK?U&JC^5Y-^$g5r(q1SX7|!O6hgOZYxF$xP?Fiww=HizL)FALDJf3%RLb zVHycY0GnNd(DYmZk>;<6u!XYiQjCCi$qK8Ab#LxAs$ZYSc6w}B-EEG#`Vi(4t$Spm z2{Kqh#BJY{<8#;3ekK)t#=Z6H>ZdK6U71}Xq||!eXxYaehL^gi&lYslrn>!MACZcp zXyiYnkqrfhIFGyShC9b+Q3^p2Nbw=E@?H}Q3~O2r zwd9U*Y77nap2+FlR67BFuw+w_5*uUG6ldw<_9yjb+;)xWJmuMY%h)r5g4&w%gMDmr z2N}mC;dU&ynf8#tHV57-Dw~(Q+o(pnq>W?IHT)l@(_zILrJsi#y<_9w z9U|nSom>BS+sp4`xLY6E%1A46fPqfEv_kY;=?HLy%HkNs)ijmmj|@D<$t&b zi8U$7{ctujPcEsibc<)ui-DY81$6x8fY~yTN7sDE{;YfD^1B{wx)EfAan^JwY4TP0 zM+MPQP3Dg8915(@ueoFSwZiPDsQQtuGjcfkBDqmUA7SEzIgv}Oc=xIAc0-=kIq;KO zU9@=!BDj?|EjK^^ASHZi-=)K(kRVK{!YvdSp3-HA+JsK1?n-_F`sx@P30s34$Lg2K*dLyilGxTJmF%DdqP<0S&V_fuU(+TzyS^WH-7wJXx@~D;ld9@g{ zq28&4dBSoK3x#>Gf(J&2jE@)n+iC`4pYenH_$D9C4~|dV=Dyqpj9BUC;pp2_hMz+= zL*|+uZbWsuc5`y?14%2a8jywtM`Wkqx5j;t^PS(==MvQ*GZ-~54+^KgpO4$f6xz@g1?jb7!|x6nXxLP&dT!1q^UYulNHbVauXCPaCVRDN4^G#?Vo$j@@-5Nn zoi7Q*9mRBnN`9 zF>fki03&w)n9Jq;g`skvsFr1xHz~{uMn~{tlrKzqG~`ZE1#*UAO9D175&7DDO`9f( z$#rH+R3Y%BAYJMoW$$#td=k-sjDLe4wI&%gukTohVg1}$bf}tfwUZq!a|7sf$M61d zbK+YR(Lp5!8%Q>)*Q#UmI42~eg6Xl49KN0_n~XE z_R|!a!eP=!*K}ZK+Vphms)!(I(_@j_8!a%cvfq)Fa}YP zz}b@px_QfuaIk$VF)Q%c%a&5ge8K4bXwb>k?h|C0=I|lg8ow6nDY8u-ndx>VScvCR zo*5bDd^EYz7xDHDR#vpTv`Ow7=rDyr7NGiNZ&Pz6BcvkQ_TI7(%d|49)8*ZA$nFp7 zb-qjmOyW<<(&-BPf0!}44EAvM+eDjMQegDqVLr7JEBQB@+9jyhwZJQ%lByG_iz2@h zu$AV&JV&Jwa`8Gu?y7+E^<#!5)S{zC8S@1B8c@$*_B6apUgFUbdX`p|9R`cle2+{)MmtU;%LbHK0 zzz-+^gP*Zp)SrNLx;E*kP^`@?;=>V!vEM(7E3gOXRqA=B;n=g3#t|M4O0`eo9NWb_ zJhn`zKBY}Nf|xU>7z_D)CLdn*@vWhf2cJ>&XLyRyx*hM3Vk10d9xZ~c_nj2rA?>K@ zPD#rr>8#O$rDm~irt#mJ10LBer$+=8zg$17h@1msZq=Poy1ylTphmk&lGWQ z^0KkDJv0}r-GG{ZOc{sKGs@F=RIf<#kMT=6M)ihqhgNM3u@0S3T~3aZq09Ii>)7#+ z=(c`uuT`sUtr5h(g+4_fS=v~57DHi*_dI2mJPK5?-# z?tM6uyQG0o-QoKwHUBTnX*OMc-j3jEAy`1C?eA<@TYsZDeK+s!02>)AnaL3Ewxesp zRLb9PcFNf?%r0)GqgcP|+k7l-`VN?!6;CsgGRyeQ?WBl3&4xCYw|AUT)+Yrx-_~zM zeN>AB!3ghG*M?=_-;$Bw3Oh+d=}l~iNT(+O087x|Ea)_nyTqM^gvwo@hO-73`~GRa z&c@sBB=hDP7hs_LdQqu}2`YA29u3`-6p~DTL1JH8UG*uC$h>_AJk>P6x8L~rOw4U} z>#o0WeRDqE5IPJ_=6=?QgwV7XP_4_{+xmcd;Z1eSSoEGzJ?1yEzYs#*ax%SrBexgq zIRJ@%CaDUy9HTsMd2stGeXt^?WJ6A9Nrf=WN|Sd-2A-dqD@552$>E1|7}m5>2LpdF z`A{+?FKwzO2OcmXxHufX_A)O5$ijvUmv~#8jXOKQK6Ssl&?KE9KimB({?e)1<}a9e zUd~s>-1M75ZhzL-O9IPoi=exQep9rNBX%}FM^RAW#`k%nvInCE1U6}h-YU#_mg{OloBvW02Qv`-uLx=&$F_1+Iv z=141^Vp!T#yxAvG+=iv({yE8!OX_FAK7V~Y*_JfhC?yFd-MXqL zl2}a4lVpoYb2g+|tpZr?PiJpfepAE*n`y1t*|?d>k`BNiSng=u>XI_GDKSxD*)x9K z#Z_oXLr%`JR)Z=r?eKGXnf7c7k8r=IH?zT9PC=Q8?xZ46Ha^+zHf*aTrmELqAqM-R zvwUXZWm~y1I!**=hA!7d`C!WWJe%=h9;h|68Tg)$kqdvRe620a3ccv866ZL}l)j~J zsj5q--qv0-P2gR;%XknH!Ae3{bot>U-ji_RmENxb=BVnPfD@9t6Wu27i!N8_+D^yk z)dB~EAnMi{oiwv_lv)9t$+liPX;Xk~bY*0zt(M5{zEQz(QeJ=k?rzQiizwM(&$>!6w;KquN0w-{M-);gB`m1GLThT9i zlv8}OGIU-j85*{Hn*Js#Q9<$M?PHx|Zrjz7d*|mzvVqSJB7~~%OAghtWj`BbSPZiNN z`|vo~ziKT@2!lH8#ho~2dIT|b#iAkQKq77~+B&!S-HmK%Y~}c$W?Q zBYijoza?j@{X!Nz!n>usa-zbBz{S}N4pbls>Gf`heG(1YSOd=W8b1$TwS*OB#>9>Ql?+rG1*D`x z{)Y_W3jidX0?(@r_BCb|fSc(Ho}<%?;M=ZrU>%a@W9a}W#NzKucejo|iQXO0b-z67 zxsz^d0+0DtJ~w1{B_If;TjgoeIwTRk`)r$V9)S9SH!C&)oTj8Zht5NPm-#!$s^O@I z|I=dF2i>L`;6?g9#3Sm}bgYfiwe;Yj25F_jR{YQw1neVik>RvKGXi2cK9FWrh!=vO zkNuBK$J&o>b@cl2uj-0vD6OM6h#D){GvG$d(H$FEHFP$S%azi+-@R*QSuP}_czNsr z{;n}j_%6Zk_H#*I`gyzMS1E|Uv_2+)T9rBC%^KG1y+Vdb0{>KDbjC>d+&`5V?tK5Q zUeEs=lX-f>lP^z%w*bZ}Kt15q?gHZ>X~X~kejt(og1|E>7O>%WO#>vqYPeq|kK*`e z&?smaaHhfd29N}v>s}=Kn!vAOh;KOk5(4gpP`iG>0_*t~>wjw@UT}ahvj3@tn3*eJ zZ2|p#n(+pft=|~8LPQSoF=8AlNay(3xk#MXb+txCPI2Zr)zn|T++nxkmvyan?!J`y zQiSTBfsI-`-j30_v+K!r;zxbb;UJ>;ii@6~R(Gwgd$Y`zwp&RCa9Hi1uU(B7ZDf7i z_60${$ZxC`V)4{JD~Ot>s4v;|-56%~CN*ZEKG$dQQVa!xuz%Fu5P)yQB7sB^qFZd# zRsH;ILbaA%<7O2nUeeOcTQ9M($WO`#kfSP!x%ttU1phhd(u_akRYO*wmwCWGMe&|( zg-_oE&y|4NvtfA-A@KROP9mO93-pk@Fb~|`-3o)B3MfveGN*sD8(oJ<#y`CZAp|~X zx&XI-AZ!&IB48BPoCpXI3v!e>?A`Ins3`@om5E(}LLbwJ@B*1TLdRHO7N1#S^=~NB zZ*bz`n9bZSq+4s)2`DssjSLw*+nWfH(XFTemL8*G?V#2h*Dy$rmxzon1Mz2NxJyo%rLuU$zcmbz6C0=FH} z4<{6UD#?Lg=SGjbS3asxhXP3I&njo63?}6G#`PE207sLfeA|Zj@U5kcWuZ7y@@%spTBNs;x{``k1eya6-66r1sVpmxGmP+Y=yS4OLzM)jV4={YSP#pm|%|7@^_o29O z0?n01h6b}{sD|I-=l%c8w>lmY?YIR*fQL70pNG1d+4_d<`Yuf)W?EfiNo@1CTa&h3 zSV?#KiHqINX!uqY`rX<44h;ndBYD*>))XC@6C?k}&O1f0$TDoBDnx61@8{h%{Q;uO_GJP28FhXekjbSYY!l~z@y1D+`qeA9$jttFnEkdH{Wb%w!Q-4Y+C?SwjD&Vc8%5` z`yQ@f$g2T!;SBg3Xz*kM4;_u}D?Gx^LJBBCHg{N8KVLw4yMDn8wXJg-uJ*;mU<57c z)3?MHyVc%UU1Dko@AZzjYj8d_q__%Yeu;G^IHLI*f{+;GPHEM};x8R4)NQ)O_snQ1 zvB3k9IY`f8)iD$NQa9h??J1C{+s*p(LMZ?E&t1@w_s&m?E8En^F}IH+eAs_V%sU?U z2cq6*2b}(+Zzy7wZ4=WMz|LPakE0g$fJOGd>TqQ8#9;c^5VARWJ$AFJSZBw*krS0% zPcl=z2yE$aH*iEaWSPh3>g;unr{B2GS_xw({WV(xBuP0X27> zJ$X$(!vXEX+Mnb`ifX@=>hCV-Gy2G(r|DWtrW{Z!j}XWhkuI6bK1SB1eV8##39V{?Y2jHL(rcFZ7{*zzMhZ={sd zX$-z^Z`4J6>L_g$rx;!G>VQ;rX-AjpY?VvR8wh;DwXI5WCAOk(pc^wNT<$&3xdNTO zI1Y7EioD+RrnZ&8f?L*Od%jNffat*)4uw;}&c{jLxCOjT zX32GT1N!p_?m(f~vpkSBPi=N##En2f87%H~ShDE9^Bdy|!Dl@Feq{Q0=3Yn5iaC{! z){$kOTf+OUn<~~2A4&3f4xcgWmu}>p)G>X%7^hIllA!Ok*%t!`8(_=1JB}SjV6E4$ z{oM5&>$e-`-aK-CkljT}Ky`Y8X^_>#CpMx8R^}K+=`=ABG2;fx)rbwVJpu@C6w*RzV_UwmW3t6wRq%c1qztj>bQ?4CI}0-!j`!4W^OH7Hxdz_lzPgR(rGT)Z(Bt zE{?Ki(`l$wPLXM(a+!>$Hs?~e=DID@lS^H*C&Ka_p~AUZ5lBnW-MLAKQ3}Jl%KmL4 zID!A{dK3^M*ogx>cS5(CA89LbFbJ2PZp1nv_oo$1@f7C|hu5#%m80$=D#tiqH4~nL z{K?;upKk?^se+27rrcmNaT=cp$Jd$c1}VV1pxFdYm4@WkuO3}DOtdnkr&L+&K-uRB z^^Ascb4+m0lg#hRv+hrgJ8fq(2N^e>N|uu??V0zas!1(O?sH#=JVjN^Tjc8dO9~c# zYW2(>MGfwRo0x%pHb6jrk=u4I=S+*yp`lk*q|U~<-8NxGdveJ>ed#nAp!)o-G*6c{ zeySfS5%F75ZRXhKCl@xC>PSpyt5|==@SZ<0G*kMhpNhfvv3>j`|KUF6y}DBW8^5_> zV5a_?UCsZ$@LS%#egINf>R|z;`+op#8qO}w;74Q%119`f2;V5ySBB{9Yj*CIllKw! zUkqva*Oudm{y}k`S5s{kC7x6zUl@d*T3@tpnu7Z(=Z{h%Jul*|9?;LpF1Ux<5487K zib6Qgj0-rIGS6qH8I74|?n{rBGnserzVNs3!V0RhJGs_-S~JKa@sPnY z)#*cvvZq;_4lr3AP*Urt;fBEIWQ_%BoT7Me&goM^x?P~K<^ph<*>L-4#uZd*d>!}5`-8M~ z$`9JbifiPk+g&0$RF#ONJqPUU7Co5NvI$Jo#HCm}mxx=jxf8q8oBD1)UqLqTFEA>2 zA_Cd8{&Z*#@VVwPlQl)huZmZIzj>`T8w6DEo>AnDMQV z8f1)|eTX?Vs{X^}pBH|Y##|Sw@4Q=}R*qc6YGKBs)H z^yBU8SoVNURm-VRTqn9m(+zgfc&pyx!?N-GH7wNI{rkIJy_~fH2pvJ5GqPr_{io6o zIby~XGtaM4%ZX>_ybA&l!ZYT`ES^E_DX4uj8g-sOGXu+@R|jNZ0#3HD8ZvRKGD4dja7Rq zzqJ0ed^yF`2hr)yRq%&y_b{`tQIyRy3TnTw9_2_0N_;4K6=I4=w#&5StXADEaiPvb-<^6#egI z8@5m8`FyN1WD4=EOU=)(KBr)Uf0-MOijx*m-5t%=)ISn0AQGlT;&0_6RgO?s++}Z99S>oYuHVKXn`a=DRFhguqL zw^*Nrr~%X1NyOQRpd8DE z^+OwsNJ3dOkJAqt*}RVW`^^pBctUS8sk>SLekN`>MF!J^pNne$; z+9%5~84gz=^T(eV!aMc7ch%Lndi!x{w7#NUZ5lS zOW~l&&hBebnNP=j6fnx*Zqd|t26QJ;h9N9c^AvwS@%WJ)fegh+se2fQ)YDhJ@NgBU zI-|nB?Jofwo0LHl%Bqk&=Fg?_-Rr7jdwyo?vr}d356l(Np}{8+>CkmyBJjC()*UT2 zF1?^PwX^YrUB|z9ntZPrQD6V1<9f|1n*VRC`t)B|)g?wH7pkE=1{o4+w}b`>F(Cjd@alerKW?qKgRCBD@-?yq>7hwIxg<0%1XlDXbtbN2K2{!V}fk{ zg##KXiF9eg=eHNhS44Fmc_%Ov7%5On`$JdnH2-2A%+ z0ry@0U~g~7jLkwsVC#F0k||^c&;m`3D|Sf-jE;BHdZP;?<4|Ngup#Z3 zUYX9nbL0NP3w=b$Y$8y@RTpS(zN}-_n8#Ew+kI__YP28%w2=7Im_y3-jtQ`F(w5Jd z|5&sua6wzA68<5_D$4tA(5q6(;xvf6IB%S6fP9*av0!XP1O-F` zv>#e_B!`2iw6LRN_@$nojms3d*DD=qFLggt-?}!anHv;>fvAVG__eiHpL-)jDHayW=bCgq%TmvI}OOtt3J)8akL-^m$ub*}&uaXo_o#G&_TACEWvykR2-Mw)?xmJ)(S!Yh{7oXB@Np#vVd+ ze#V`@KfVfC`mL&IYY5+QyADM@N5x3}zLYXgCe}i8> zluF&M*K-+b`TvYMgDB>oT%yBA%`5)=$sL!+8^00!@nrAv$*Jg?MPuHtH_^uW@o=(DnU4_FMFtI~Qc$A+PZts&y*qTvI9Et`0Ga z7*A->TDDqEe{AadPZ#JFU&?YSfNeW|QC=+`<;T_cQ5j~y?qY^f2zO2o6Trp-eO<_$G2BdiO{F>Z!qTp?RrletdOF+X6cOX4 zDWs3P`}0|s{j1=Uruk?+vU|HX1L@AZZTO;XAuoBFgMFLC1E$s@{fiGk$)fayV7azL z5&MQj6cdv>uEwCbp~-F!MFc%~@zQ5ZlFO(O;5ek@*w?ZRyQ)r$@p6Korar323rYdg zauP)lKvfR2==0J$pP;y1IuYb`%GT6JN-E$d(eI!-nE;j3!%B_Rs{+R71;(r z>TC^Oss&)V$wYcWi@KX>SfA9PI;IJX&JtY9gKdI}ltHNd5RB54Th<>TO$=zP)8GNS zr}Z&)>g#I5!mXt5_Q?l~fDa$wyyk4_VYFuqv^~2(TfO$inh$M|<@ujc^loAv(uRAC z*<(6f-&9&4^p=!Z)Hs2wTHPJCFF`?Td>|(uQqUbFE&oBgGOA~q|8&SxiPfKxXhv+D z+)_eLpD9-tx(aNNfPBMWZjClL0Pn*p z^@J7!`l4?Jj8Rn{S-zN)HS@lC`h1%JnHDY=$NcQ|bn+_9v0jq7ubP6E-#E46!4Ew~_!cC`bB739w^c@U6LgP)r zFVBVUK4^vb15duM;QtTLKj{_E8~@dRye2>WkN!jTzxt1#4#aYy0zh7y^T=K3KE#(Q z8q@hz>ie%krDCxU6Wa-|h=|2TDAZ+kL#5nSQdJw+< zWJO8C!>0Pa_91a_=LWF-hoHzdXCEZt=_8}qOvZW*AiD8q zT>lm5nCvwP8Jx9Q3LT#YKiTnuD2QqKDIeqrf$*ea)*$v?-fDa3XxjMCn><5JqN>I2ed>N*z%Z3+B{t zaP%Fb@mroCNVUz<)Qjw=N~+TA$EQPY<9bav0r&D_wghq2JF&mLHHdy||0h>m8!9H_Q zvEQZluO5XcJ>x5&=!J(iA;6Jj{0~&EB+KRe#b>k|pz8#5pRVjfk8E>o-}J*t=pj0H zi4}Olgd10agnft0saTrfEsMPFrhgvEx(FfScvFroSwiqWDH7rhl~yNG4b?D@p!<8x zTU|xaCHq#~$GgA2xt@M*`EdspGhG`V?V7%;&srUe@AL1*de`{imd?U ze(S+3?(hsLhqU0FbJ+n3%P3}|@T8~+?in4bsKUowo$6Wpo@$&ti@8fgbEKEq3bcY| z-_w=}BRWWv@x+z|%HJbTdk(nV7=B~6UA$OICi13#d3L`St^cBGStcyxdQH9#Oq1DE zb24>JhrOeHhAdZW1Ki6DPhkfMjG}O7)!)C8@?ILV>>HvQz}u&lJ+L&?b_x_Fu(N9h ziDa5^gB-KoG9uHoA{eYWSiYd#bvo3iqFqPDxoj!|V{BjeC`AC``%62xh zkiBu1Q;HEk)3Ms=_IvVkv>1812lcJp&#eP{V_!;l04hF8qsZ+rTIN#oEr3uAgB0F; z?NTJQ&y=4If$WL)Zy3?JJYU`nrCj!W zInEIANzplCbhg4SJmFsVLyf>tv=FcQ#&Pql$-WY46shQQAN>3_V_^)Tj)aRJ2mHtT zzd_UqLDKKSGD=Q9@{N+363{Z`B`Qt$!!aeoof^)Pc8g|UnpbP%u zj(OUmgpW~F=SOP;iF5=-qiauTJ-h7WkqNrF*~!_9Jh5Pk&J*;mD4rd^>DI@c%*PU( zX`9t%?uU~EIYP#1iAUo?%*H=${g#_9k=o%=`75AR=?SS2bmt=ca;}8?o zwr|L(#LYaLc!?&%81vT$v|nha)DYGRJx&QQyRd9%j(%A|aCFuIM^_)3%}F&d>=m!E z?7#8Z_Em57@AAL1W3*oDfB(J+rvu8vaN-XC12J zqSxzF@xdQbiCdoT29|n`IO;pcR5AK%UtNhFI)Q1J>Y!kq$IELKCEfCZ z7Km$1Nf66i-Re_oyataxJ{-cDdL7zg*=FpU;qR90q1a!?bc& zBcu$(>QQV3Qh!*s7oxd_CrT*wVM)_l+xmLAIn7VIOLL9lxm#<$%D-(75FUl8k!Y#I zSjUf=CVnHiI-opl-6FhFQp5eU#$+NCW<5}A70r9Lx>J(fda_}O%BKPUsWlL3IJfJm z_g(Zued?gv5^viLG=D7oBQ`Ba(oSurdtSBlxPRUFhS}O+y=B0^!M_824M~>&!I}un zF`W6f{7)ADpOWmho_jb6j&&`YoZ^Y{USx!Qt;nM4I8eED%bhzW`MJ}t0@m(0>79bZHcgRl1|lG`$00V!VSs$wy(LJJk;lM_o*QBgpC0 zE=loRBPX5fK_%gEN^NK-@IoC25$!R>k8L=D`oMu@rd3UC2J2&XSoGKFHin7Bqga#g zppksP+|Tn4@nv(b5eyxDX3{h1xR1XrTjh>=V9Htta9=~&635>!31Vc)5mtnt9=O@o z2~mSBUPPCjB^^(!FwU<^hHzP?eI_wtC?Yn-^9YMH&(JoES6F2Y=CU}=PI&JM1`bZ% zmHj08ofBogH23;Agz2r>GA9)bZjf?XDa}rBA)!wRB1sh`$y*s{)sWI|o)iv~GI22E zG(Hck)xoP(pH9h~UcTOS`}vFuke%VwJ1ObE9beKK;XT>`TBacMKF^cZ_{&#gZ@vAy z9~pn7k-lSQOeGflb2+^hHh7d{Uwq2fDMLF9G2X%o1%HeXATPES%Hg*y??6LusHF_$ zzt2!RP+2~6LyY*g?=xkG=6h+P8mplc>Yw*hWUb0%=b)@O@EHf(@bZJYdQR<$l4&<0 zuGF*0gA2JTbo*YHKz^oWH|bFP*D z^F3>J=q>9XeKxp+e(BdRV_1!teiqh_*feP-r?J?b=e4fs`#&E}p3|9-T43*L{FGJ6 zqO*bA6004oFKE4f*gsK)UDN4{&0ADy%|&fgHkfd`RKBhe{E1$AAH)CuG4>WfaW(I{ zFB06{CBY%M2X}XOcS~>|+#LeJ-GWPy;O_43?k)o}m+$+ZeQNJ>rEXVEt*NdW>6!I* zcdhk2&rdE;OY3pp8zOCFFw&Gu3lPN-Kw3NU1!DZZH1bg`t~;rDwVepBdvrb_WgvISgBKK?c{NZC{w)CdGUp3BgB8%E z@uf6&%8+(c9o4~A=_Px&)f=~70e-zZ9S*96n5q|z3_=c-Y%cgb1A3M%@_&t2SVjkZ z*NkvjN4@v-fXT3}?I;sHdfwd`q!zv+9~@;K+#9Wq2OO!@5^r&WxB~boCUX3RGmlur zJjnmqi{r_}`*jnJt(zp!z4zgVXwT9kS`r@GNVHnFO?(&-TCG38C;pCmjkguLYTMu)PYL&-1nOpRWrhnj~y&1%Ts$X(kfgB?icV;Oc4|_ zpgYNvULiU5rfeCI^wEJHLOs`|-1c~rfgoMVzLz6A2!S}V;%C%H%jYmc zY}%YetBf})M#Bo9NdL`EJdBRvFp_b`{)>1|qqeKl!})FZ#YscWhEe+o5G`4cUaQ*I zV9a`4_6$v-On0PoRGy3bTI2sMevm{~YCwN}a`$}>62xs0Zan}k^XY8i+AF3_zbJOx zs7DX{VZ&liuP2P3Te9}_b1vRJU7m+!JxjMBtvcJ!xDp*bTS{ra;CM>@KGK;G<;~^^H2u@;8 zN|zhj1#KvHAN=@n543}M#)d0Yjz5%9I2DX7jt;1kG3J}C`*sGw+>ecLJGp)`59~)K zVPar5iHYajg}_&#e`ahJhb?aRpRN2vS>gb-dk4VI@gs2ju|Rlsw)Sn#G+BA`mrE}H zO}N;$;5kXsMBr^7vAfHl*4?MaT9H=P;?KcQhrSyytf8Oi2HP(BLOz1Yv~m3{%t~GF z2d`L<>F#L1`=~=0Szv_j5R^>lS%qg5BW=G9Hw8c`PckwKm8LbY zA|`$ng-9q_#zN|CG{&EL3)z{HqBB)&ifNnh)kj@ez}A3e1;|gzq$I3n(9I`I2IFz+ z^r7jhJQRgLogvcZ71s|!mVU-M&TZQySM!L1(`k8h&pzZ62wG;qHs!CL-dUR{kuEK6 z78~u!dx@8AUefi=YusuL57~in4;GZ>Bsxtfd8{M}X zB4yE)1r8~Mer*a>m>wPzw!!t@zZCH}>(*3>Hvx;Rp6|n4<+qj9PQcbT2=!gFJkd%) zrtCi3t0g5I9#4Svy*h}gePCWW0 zrgIZ2&6UgM;;6RBwGpx5?X7`x?_i0hyfx`JZuf{220*;R8R{=KBy9HfrATLOhVqs@-@OKSRaH}L+PL(Inf{rm$>r{m39Ikh7zp-%53`Zt3} zgr3P?-GHzfpI0a}v?)4P0Q|PNL9|{T@IXk~xsO;4x)&VRmz5q*|G5B*jSG2fE>MAE zYyuQMP!*cn>kWLV`5xOS<2bf<%1@c>aKRRw_F>o2?#8rG*@-8-kNkE=slaG9j{lY0 zAWua=jnQud3XXGs8(0q~I&Ivr)#bo9qA(R3mg(oSp&Y zq#){;7v9pD1u0bRvA@tpBq#Q3u@E$vtyq$F4S~9|W4wh;=#WLXu$<CmE@m`QK8i`dWH*EPIw;GJE=EoeiA{QGtf3xe61a|4P|X&_>MJ~b?zlW5}E#B zJeD+x_*A4JB<$Vg6Nwpqj*;?@f9PJHA3|=XSad?|i_NndG;eJm zI(upyv>||J_>+w!!4xJ}0*Nt3PQ&5`WmR1r^3^HHC8X^^o+;o%KzDRFwtS6Y3KKDs z^;QETpp`MDh8?bk-KLhj45MPjtt{a5+=R(J06GrQ4+4avSqgzG+`7oP6<11^`l2|S zv4e1OtTirmJ7f8p5Tmx>rW__I&>~D#i4vkuf)S}TZ3QH#1bD2k-g&*(Q0G+~H1_PG zv4DL-8B)T|`T%;gXXSdlYVWTL9f~W?6?qYD5|*n&f76z`R1l};GhJI+d>4RAs=Lcp zFL_V+(N!cm<8cyS<^B$;A%|u<;dPh!&`+_k#cKr`p1%YMO|6sS1@nCxmX_GBBkwKi8~gnG({XXgB zs!0@yJ7Pl%lb0d!j2MjxzeOcpo`T#4D|Oe)*Dtw^;Rx~N(7vzOe@o-syWlzflBuD@ zcSRewkg~$}MhR%s*st;PvC=j7*g%&vR;L1nKLtJ)TtQ$wvJV+w(jpJ4#i#pTKZ>mh z9I7eVlpdiwmvebFE7hx4p2a)$=G(@T3w-+s*6mAVVK1;%}ZtcpqQmr zQmSe(`ttJql*Fy>rG`u&wLx&uSDQ9(4?%UsF=Rg%KKWclfS=7vo+iI<>)Q5Zg>~<0 zuzoUcgr!nnP0@8Pd`Q0{cYgyI0h@uBH_& ztn&pl^HQd48Z*OBeGmUKV1tKwB`gdca~!Glf%eZQnEb0xu+p1WoBG`}G}rqC!jt>r zwyu}TO4-C~+#tRGa5ySUaIn{%Q34U75+~{C<~u^TR;rRKFewvPpW-7&Ajk~(rYmgI zzE~_b8keH9;fb#ClA`>o*GBZyBqMAN2GLnHlceSX;ufJLBIE)N_AnaWToFaW+bI&r z1_5edu+lR4vEy@1D$X^gy4WCE-&6^LH1=%7YdfPMZi za<>3KOJmMKM7Uphs+dhf`36L!zuSVUlDr2UnNr7HWZT61E&#l3EZ~KU8@Vy>OGChW z)*>V2O&YW318NaHyc#JkBC?vhP2y*Yy8|Z{h9{3IK`n(MyxvJ3lWIeeGeBHZa?)xC zrTHbM=dj0KD|40!mX`CI7;udsPlwcc&d6^1CM-<|>5pBJ7*5mUn#_Ggq&?d;1dUEA z?DYG{L7=B#MVgl6@)wVAoTu`_&g!Ee11Fe>A18o3T>cuYSk<2VvU(MvC-+}2q^y%4 zD@{k@?;fhQM=1ElWDIQnpIJjzmc($Tm0A-#i3TQaoXhPLd6~HjQ{s!Xc83R#pRrC8 z3%*cfB}=ZvjiomPPWO|odffkfbl1T8aJbt`K6FR^eXaT74M=Q!Li}bQzhF)Id9Gu& z`bZ?G@#~RQIV-DSd}u8Yb~w2%i>jw%rdnV&S;YC^ATx~tUAn8~f*{Hef#>ITOs4$h z7Zzb1HW6bSa-I@3vXTsGa zzjkJ)dxe*uW52>rt})Z%(DT{vv&k?`&0=I83<9Ogw+-hnqpu3OyWd~mNZzmwVj~@r zoOYpr;Do6Dg_+w;2Nwaof`@FiDeJeGi7!5XfNx5l<2IG0FH8B+vH0ilkf9Gnv^o2~ z?0z^Gm$_bGQE^jcB{xGH`}YMjXm{OZM2M@)4*jt=8Yv~k-Q7x+;)UU=xu9O0s}Hsd z=JZ~EVwne6dliJJ#wk0%^lxqg;o(w7qShkV_EInV^+?iiV}?W~!n z3`u|c;wWrtpSIMG(sA!>SkcOVKl>(p@UD(}?a^Z160zI+OWpkI9$1r8=^Oo5X*VzO zi{?AzK$GvTtxgB3o)!8 zs&}n2&Im5qF+BukJKs40GZ@)1OAnQvX8bq;v!>KfOK7MIW zEJ<0B#L*qSP{K6MVLzv5;R`m7V>_-AHu=+c>R)Y$OcTyiv(4&G&lWDFHed@VaxK8W zw!keT48nbptgCCN+~<;!fmv4{?B9?}BdrNV5Qc&QffS>kt1@lKt0VP4 z6e5qCc#?z2Lpq#iUM^2wATJZ(8I=NmN4i}Jcw;aZb7Ho4k+DSGU#pvoqy!*=z}3# zeeSE3S#yL_3iB-X?|k{s%zcFb*eUAlf4yz_#<`=Vl^Gfp?+t|f2r=W&E7txPL~L^2D#JjE;o^XFz7+%wt`(C4LVp?oN=4q$ueWFW;VAICwGxdI|~8=8P62 zAElD-Y(~v54AOz1^A&34E4!MHOR~8z=-+FuuSIb1u_An3@b;I|FfP=I4i@kBF3#R( z7b{FVx2oToO&TL<-f1Uz{bemrF7MxY+e!ZDkbdjxEwcL9K6`(16z9o+8X6`7h~6aQ z(h5xE_=tgb*-o2^3};zt zPq|rOPgG!O)oB+{|K}(BAFPUY@`o7mE)wSrHLh|Zu^YJBRFWfX{W~uku~-6tAxYlD zch5Cf6>RwBDkJNs83qVwokv|im4=2ATdfGUpXt9@~aj!Y$K?29Vxa!r-px2J1-Ouhse2o!aW#RO>b+>a@j}m=kSgme z;3ZrGUYtW%3Z4OV0pS`e{D>0I*;g}@cJ9Z2w-2DFq? zb{9}^?Fsv{JY6AJayu*~!td-V2?6o|-)4{!MfpW=zr1tjycH=Xnc&p+$V{?P zr9mxkO=VSVea)JNnnlr9Lf!&B0^)ks#?AFOl|=*a2G_?s2akBVd9|mx$GEXxYhsKh zOSW84R(On-M8BV4yMR;&a%#K3()jfqxce;**g4lL0nT$NUuy8kBfl8N`o-15*sP3& z0sX6FpO;G4ugqo9^6&IORV;etZ?cBL9wFW%JDC;Q$nj&1Z2YHlkuPc%=zk(^2$CNr zn|oS(P&cM(1dktSY?jo>($e{0XcO&LmsSjZiz75#Qwx$oNC|=kTXua0v@~Rptyk}O zth>U6(`#vl?H#tiH8caH@0}sGP1Nn8hFV)Ie9ftTQMKKF-3wN*-4SlV#LnDK2=E~b z>UyI61`UD8S7l4q%V35fwsj%L<&$WU@I+}z(oi+5H`e_2_!WN09|kF?0R>3JPvFs& zW>RrTRySc)9Y(S(^q!3Z-iHO>5rDUNyTEiGY1?`qh#U8f-if5H_nr9vAa0}(J;45; z#Fy-$cP4qGd?M64p$q7(20gT=6Ni0|SS|4B&tl*_V6zPL7P;H^*UTTJkA~J2V`T(> zg#?Fng}*5tnr#fh{YF_b+624#{CC6+zUqG=ZZ>O5568)zJN^rC6XC1)^y-Z(KkVrq z)Pb1w|ADx{XUC6EM6#7ne`p$i)%PgCQK&C;A3-hAl8Y-OH z%gXKnMqD3xiEq*XcB+5F8eytKLpC1#lY))~x=KIPS z%=J#`Z7Uu$PWA$C)b%R#$_(yM&Nh0b0cQzysQ|Ya!ST>fr?fETRq{sefQe__N6=WR zSMAy?q%C;?447~NBTdl`8-RYcD0}UQ1ErkOACehgFt|5apQHNamuuJ-=iL!gCsI*{ zG2v`j_umJ(i%usIK7$HbGtO)`r(R=xu(Qpf5NT8RwKBpN{l`z$C_k?6^rZS5@{xy( zVHg2_1UzvM=&j+-i+vO3@-tIVP_u93hc>d2{PmyhR)`tgL@o~})$e#JBj_@fdKCc; zt1!b=dGwm1nukgdOL;uG*5+SXLHaY?{iZHkLe`ojxkB~kn7$Pbh8lnT^dXwrEUZcC zNa+cU$)3#;(Ejms0paoUFCA^Hb)HnnNmBT(dRU}v%KKL71-DSD89$l~A_Do_7 z3yqzFoO}e^Zgj4T!S6NYFI?aw(iarG+-lH=%FgCz>9a&`0-!jE6nua5l1s!)Xf#)O zl^nj#XWte2RuPPPL{N+Gvju6C?J`|bTaO6Aw{yAiL~^s`ncTCmVD9j zx88WVaQG9(j*-(7Z1&=D+^-fCC;VxGHq^hIuxs$^G+J&U;3|>IsYJPcDvZ(vM6K6& z>g)YSzcb?7X6W@bSNo6oywPXaZOdn9s-;L_a$eeycOLjtGnOmJ8M=+D0&YLq0lyU? zd_+c%-B@?q-&;T6$Sht!N!Po$j4rPmLm^%~Wur%ddSNnqMUAM!sJ1ASU!92bNe^}T zks|rT9j&~6(2e<89h;`_hU!z8r7*UnK}jMi2P*%6Jw6*ll^Z8A=&FY2LwC+V3jeE$ zV)@YN5C5x*`tm;rH~-v>|4+WUs0{=qax9n`R*KCe`WPUMS%aRt8Tj1z9~Q#(hd|1e z_#cfE#XsB{8~S94V43`g0Ep>Sa&%*|Ji^@BrMq{ZD70xxxU#LM^wG{Lqk}GE&TJu_ zj-LPx2lkcshq$mVi4oKo@PHM=Cc91zTl@nk+XKzZ`&Ib)>;ihzXx=*j-?;aaQ1dQX zqBWy}P6Zt)E4N(4kj7%tX3zfLq*7{RyV8Sp2K=e9!cWWebWRd3{$qB8%(N0Jg-JyL zpMVYPxyuSu;=|-?=rHdcqVzG>R7E`(+1`Ion`r;kz)!0>wr5Oo&uj7cB7{GvZqp7c z@(Yh2C1}Q@p&J_`S9BqqK}*G}4mtCRen^V-|?bgyeis-{!`BtfVPDTzX6bdh4o2wVfQ^nrs9~Be4b}H3$nsNLGo8ORoiWqr z&{u#BA(+qn0!3R=@;BSTClVOcBk}ciW!<{p`hD?JBDX3ufXelgOe#!uUTF$$e0fO7A`W&%5p^KjP-z zBocF>p`hF;$d0z09-)vPO>75`x$8UbYkU>(38-?dQ4qQ8V}+0=oQ&os{Ht=x&T#}b zbhmx@X97xs)*x{+v)*%n3Z03FcE!jsTA;nJS*G)vREL=#TaWaEaPj=A&&I#n$a z*2&j&P`J!w=LAT<2HWi>)jZZbGhNP1B%_N*>Kjg6{!;%w!0*x>U%B;XWw zX&nm_ejHWj^|=47NOZ}b+om?Z;LLjGh2N>KUIVd1nGUI;q&s$@cs8XWSck(ZGHqP3 zHelNDiL`R!8&eP(BLIZrvh)N7<55HX0pJ8BlIF%>vMg?v*jG%t^H)oWF(vfN3|+Xo;#${}9|_bVZz+DgEn9v5N)q~US&y%)_m8)I{QS4~!SfWh@V!v9D;j}e z8*k=Itml$!1i!7^wD(afVKa8#brHU|GCA_As8z=WxWSsKZ}Y>#&M(oRdoW1TKU${K z^2rtUi>E)Aij%c#8iq>3f7CjYA9X+a@4WFbQ~WnIrNsZ9H-bL$MR(p9t&~H6lJ`RY zD0{T8Apbf1|7eR3|Dj@8e|$OU zMdksZ#3$IZM$6&d7E5^cv9Q_B>cP@NPFoHEEtn|5rYEMWU%37?BtM5Js6G*ig&iyE zxn1scVvG6`47S_s0ezlQlTKOt?rf&y#Zi2FnqLcua23B=3=Y1QRpFJqs2n-iGZ3GG zwzEAHCsdHc4SoUjRQQ)z@^lM-%p-3`=85I&L0+f5oRyW^n0+MNsTADm?9;BZ`) zQta@nFhKyS<{0qTMq&rJ_)?KSRzh#jcG(v2ZH9_^GB_^l#`MjX;zvC8SJBb6o7ZQ_ znCr^ujILIVJg|1I&=UD>%cUX6mePOo+CHyFu z);YdogC)V*npkm4NXMb!vOZ*_QVSCwxo83VTO?01Qo$^bq(1A2D!4fvPnfeR9`Jrr zp(`y7uvtBZVo4qH_{f$hr0`zI#FW%>kS)dLF4fHfBaRJh8}6HQF$r%Hyt#%MtC`c% zfZvJg6q(UXm#ddv!k}MAP|yNOod^3Bgf`o)O3X7DA*X&zq~J^}drL%dB!1vQYu7Wy zeU{ih*eYF+?iqK>VQVf*MgFkA^Pb=@6({xF9V*M2`lLv5SV5US(;!=9*R z!kIwr_b~=mTTc#nnlG{K)|I)fLnK^VE=7-;2(1AtsmnJT;qOa9s`~@O=r~o}0+Fec zB>Q<)9DD+SHTF`oFVVc=Vi4}bq-fW49Y)n!FTd^sO|a6!_^tnbWaIP66&HS^ zJ0KCAGTC&rq#jV)mm8)JipO(N;s1I1nJRI4#f4wI)R+C?he*RZ6A%3=@eLuUi}UCG zyyeM#O>$}WUze)%Vj&tmi$fPYK0F%Hc2PaDvF0EoB_sz@JT-SeA3o~)M!<6DHXPs_ zI7;O1ayYl1l=(2*!QWFWcx-$;dpO|d33!VxM2gcg5TrI2#Dk&MWdQyeD^Q06h)lgP zNa3iE{()#?2M^?vzWXk#_}}Y;`)uhtUE3%Daqbz%d7Wk$;$X6%9ESKSk-`os+Pda| z|5UZ~2iL^(->Sa*AG-mQkDC2ow3UB7HmHMLV=f6(--va0`K|m<-r+$j1x2 zxsC|`)l3h5xP0b0|Ec)r$;AFdnWf9n0a6|6O0fRHey-v_YNA7^|oO5q;D!Vn1E zv3iN&E!ldpTJWaMIq^F5CG}!X$HxeJ%-c%Y!xr^IWTT8q)DY%v20TvpUd>3(_@&=@ zP-JVrjs%w?3A%F>lGGLeKdF#IkvZ7J2)YyeDkZ_opqqF(JGtvDW4hL$vF=zAyYS<0 zDniENa3BLGyTJX;@Ypl&G7aCSBV*=}HrsYO}?k-j8WJP7PsYvb8V1>{jPSeDfzi zc@8e!Tz)qLJj8-$-rglf79^&9vO;fLP2Va%Y(2Jms+3LTU3=a}6Cx_4G3=m$NmNzO z7X*K`ziG7I7UX;hlH7nZo32x!7^s~A{wHoEj~lf2 zn2Ke3Xgp-B(AdN9F|v))Aki?|3gIexHXj|aid*`x-$12+~Y zMb-PCSTtgZA5dKvoJAXM>nbomlNq#a-D&0X`gab$1#CSA`C@lR6`XQa?2~ROr>!n2 zK)b9=MmKAdk-*YwJ>6OfFd`2RQ~w%h%aOEyznjMEOEdk=52>kB7(C-kZEQVVxt9Wt z<@A}p9NeT{oq_3uG)m@%FEiB8YAqmwMmcQZL`g#zNtHcqLy0Vm+K3Vg?@k5N7_q`#)Jy0@9 zxy?9I+KF6r*y$ed5u}0T+rTw0b^;tU9Yb4)sqxJDtJgKD$!ebOdkpM{@;643#l$*c zMUH+nZb!MWA!Vng(+lwk9J3$M_<##SRq5Ybn0$H5c~VV|sBBPGVse%8Xk{U(-+l*< z5hY-#zCL!TdpHnS1a>KPSwlgbW_|xF0=~!x2%`Me$5@|uy=O}7FesBW9X25n9#{p@ zGgQ7IM^Fe?QX*TK^A?)mxK0W2L)YE$-?g`WZHm|a!zdL=+EqAWm`&z>^36$%8f1pTigN+;D3_G=mn-?I9@cc#CNF3EPvz7F}iA ztxQBJa4{c-6izn;1JN5RASR~g)QtuKDD z2&2x|b^C?xdhIYiNk(SuFf*Z^mQ$wLaJEQs;IQlkxHavbzmFt!gBD{r>?xyI!!fiD zni{u&w_>`TW<;wy^slCv;bnYoVM`tev>YC}3v@A7Ow`Qt-DhEEBME;fT(M@sj=(v! zy+Fvq$p0Y}Ckw$9o!UD<^q0UOy$3*IFL0Y?ad^_>X)kZf)CsJt? z;6!!hlXAe@INuNaEkzwoA$^(OT#h&+$N_K}Q(NExw@B*?trV(1-(F}BxTKay`M1+M z^31~>Wm&#zzMcyUWa8r{LeTYVJ7nrZw_TxA$5q5i!JUevNOg%EvxzEqtJDR&IBjK@ zP?klMEn;3bBMqI=t|J-1oD~3;XlexAI=7WT{_yjfV?pSBo9E!mHZbX{*E$4j1{*wx zDi{1GG;=DLxhWG={f5=tjrJhszi+tU($0&!AIBy%6&c6$L$qZDE~gQG8E0&_@de_0 z^^j3uEnXBt1E=|5{If zeDGsa&bnqkUqru<7l5PIn;d?*<~)9|3jb;N)%E`&m5mk^A3GQ?jt)6rOn9m)D zj00GFJB|gmzvvyny$d&8_v68Od^nCiT7U-fKS?T}4|bZm=|5{}A|w=~paWBq43t#d zKZ~jRl{wr!S~u!6TmVEb1gPQ#vbqD}jUjyoyW$wqqHory8+GO3r@#DWK6sgpuF?HM zW9DH$uG0t`;q_6X!BX)Gpy~2+L#vEpqxpEc&pu_mhI5|va_!#Qq)FCNE8`U3A(BaHduWSu>TJ6rfmwu9CrK1xCXi)`1opExM3B7{$VqMhb)IOl$WM4OPyu=m|I z63y%G3>69d(I%#^P}k_3OVt1EF$W-8X|b~p?{>it3e_I#VcxPF@Y7c06A?2zf_lY) zg}J9kMmko<$HU1q%R-K$x@2NDk2^vIlZ(~Z9Np6o8j=c!8_rD~E3AEPB>ORP11O0M zqO+;(D9xR&An^2p`sYi14D|YX>8H4pfdX4Y6ZCG5U~v#Xc}-&0zi&KU#=@MhY-}Xz zEA2n?b9^o!h0O11&X#Akb;!0$@0(32UO0AX?W?@yfu`Qo)Sy5}nL_xi88?0XmcKu1 zl1P-z4;3RpM{N8yTEQO@jhCPP+nBbCt_(XVOvXZvFAlmk=UYpLCja+%rgJgL8oBkT zT!lB2!vP7Omm2S0am<*}{vFuO(+H))HY`$SS)j>0BX|=c?_-D>G41?;sQ)!GNHhhG zc!AGLhEiu4fyPYGWVFyrDO~Vc|F@s!P^p{e&mz(lF0UH_-frrkR)&fB_m`+p3&!u+ z3dfgtCSnmR^*p`_&UUbrujN@`ZQ|Ze4)$AyU1VT$XJyti>tB9 zJ4kxZX@9`tN}>HK(^OqTScFVx&No8DWicH1K|~5ZsB+*V$PsqOA&YK7&^;Wi_E%&= z*{Jd(ZujK_CVuFuU$n+){`xBP#~#hn@0nfiNIhoh+e@IX{7MFfz8-+A$X|ZWwYPdp zCfHKU*?DP3&eFIGa+S&B4T`5maC}R5oX>E({D{Z`&te2@ee7P=va1ZcUWGS5g2O*{ zFMGI4?VO`&VfwwAH=`49MHdC(0*CLF`3o;NxTIjUJ>xZ_Gw{&61!~POFu2wU$N~Fm z3=(^jm*udMcvvtcilV8rJL+j$0Km5Y{slY)PVD;~LQEwqvz>!Gf7)-r$q1>f3?J#H zS(M!9z&&?qL|#V)3dMB=Slz+{`io>jn<;M;jfjR1ZrXPqwB{Ee8%6MKr?Lvn6X`r4 z=Bo)jeB`~OOZGkX|6P@m6133?k zGxgDH=6xZCr6oVBLhX!rpZR`7v4FAB_Kv`EPfer3Kehxro{50qM>!6`;>}vWcsa9p z39GFw`Mu?;so0LfSA8&IbF{C6o zG1JI4FU2cKUfAk{@0X9~s=jEjmDUFWXN zRux^p(*qT_*Pli{?|bxQ)9@}5Cb;gk)d{hPI|tJj{?6AJYVX=!x*b1!x}os(r5Qrj zZA(h^s;V(f3RSZ^-`vU@+638u)8BZij5$rjxBy4(Y`E|oEqvh@fPpm{zmk81`Q0{P zCwHGOx19YsL-PsbGy7)l7m1UlG&fS&;}SbHJh9fg(aBT(mA^YSs;SKh3?viC4oyH( z#sVs(T^*&T6oDF^9@;8Xp@4X?Z8Y}WD@swD&H;VLw>Dq#uJ?*9!m@aMZty}Z z4yRTDFi}+N19z`FJxlLa3GupAD|ur58*xVNPY|bbAWZp60^{5G3MU z2DnRLt*8P<|H8C-^y?2FP5E@4#G{p@skE3#5sYWX%y{`!=Cdaof)fg7 zXo-)1r+FE;#YVUNLIaV~Cq;fzwmUa^@48)SB!A@_2OlJ+genu#=Xs@f+CzP5y?Teo zF1r0&*g^Jw!e%#BhVwKg0>-D-o=HwH(2m(QrGX3DZg%VlLerE@!THFu!XaL$=5Ib4 zu^LA!O#ID-j#h%>$8!tLQyGNDr>>aJC{h5cgq<&q1ic+3yjt=~eDbWRwp2k>lAsg7 zF1lAuo{{}Oo-%He-ca&zCoa-FvXTXQ{N^p_K@E98Mb~I4DH&$^B&Ju;U&YTi?v5XoSE+%;j(#xEWu%N)rw;*sxouuC(i@>G>#ra8C-xx*U;l zy!`#D?k>6Ol;uZwG%09v=c-+H9^1dg^U67u4f10&So- zp=N5Spao>QhGf9|mBgj*%euMy?`a{rKb|q13V3e$%E=CWZ<()PV7I^^35PI~bl<<0 zZye4c9CSzt!j4X%R^~$S-Grh99KL-hz5Oi?lg@080&*-j{>=j97FF|Lw#8FGx}BTQ zu&5rIDJ9RwcdB~sZ)UzWpT#+A(M5Drp#Ba#K);-B%b7d64nEI{M%bx-0HFWbgH8T( zE92kxKYfKwAN>jcPkc9c<^S>(lCul9L%u>%sE)Az@k7LhD?*dX1-}d3%^I}g5-CYrW%s{Jy#Ypf2-;6QzZ)Ww)b+0%;>ki};C)n!M zNODX5l?6OFIC#-Tc{5%@r?ge!ju73On9JaRP z<9%hT{MiAbPCvCGG3x8j#&g}(bpD1L-%8Z%_L%uM3z(pwqtolitgm)ri( zBaiU`>kgz{az&pLQv_XlC?UEGM>U5tecNxhC^vG$Bn0xb{z1xn{9am z#SOa}t$v~;K0xnFml&x&(Co~UqD3L@+C)dp+y^1^QyETq+SfQW2THuQZEo-5VbaO!9o}e`I&JZ6fe3fb z&8h0;%JPip`7<3W>1zAhUy%oypj{GI{@U*c@iI7z05PP020njdIkl``pp!)&&Vg-7 zpVSRStnOd9IAhzN#UBjHcp%SRBy+NlfvoH>6x=ZD|G+<7U)N`TkjYA68~Q(ei(>~; z+_;Rx8@J`z)qE6;rYJQ(ConEeQ(Ac_?I;n)oc3&tu`pMesOTwThV;5AA)DCLB}2Pm?6g> zjbw)YgTy!en!Hb$UJ*!Nk6fOI2~lK-UHqzKNKTUJLQu&g6e%T3qc& z7PhF&oy)C0@Lv=N?xo7^zY;XuY4@N6KMilO8lpb``#{IdnK_j0()*tC`4WeJY4@rTdLzbjqXLe)}hV!v3~ZT59Hgx4;E> z`HSWBHl71L_k<4t&yFh!N;G%k_4A&D5yTre-Mv8;7C0|rId*+!tid{skn~;Uq}X9c zy0BtKU<>ipIdGp1Q|cI9b^Um~j7i`-VIUhqO5TG|dEn)*TpnA!5kS;37EbbVlU^^{ z{AfR|;x)_H?fo|;rep|Dls1U618Bw7f?kLd^L*HO5L8bL|6|;)>u`qiODiJv65BZ=Q!?C;^O+ak+vH};@iKbcAM*(v9cr$v z>f_>F)2u|;O$RilK~9zQQ6*qO(ZA{ApZf2RH~5!TA&l1gB~1m%((&WPGEH6O_5`)3 z%3##5%@5JM@{*MelucJJ3z|qjemQPS)AOn#YJua*(HDC7aufn=Vh1C_$a0Rr^wRSk zD^bg$9{iaQb40Ni$U*{BpitAbL+yWjh852l)fwPGJ5+Z;PMjmKbST_39%1gcE^|oW z>wB=}@JYJZJ&q*DDJ9P-nr=9W$eHq9nSd>^8(fuGT;4<3k9pERsNaGY^M{ljKSah= z#Nz;$Ewt~uTdaTM-Xy5)rd$(Yg&v`SSN8@;-=Dw)U6)-a{X}ct$2Lrw9xY$G_uoLY zLi;L&P2IZ0D4Wb--EHxX6ajj3;0Wi(yFexpG9hsNk*(-*rR7w64w<7>eOc`b#8<6Z zUq}E}s{w5|i)f4u`qu0f3lAZ+)}2(<;g~N?FG>qrKze;$Xe3oWT&66nAEBG|mt{*tcDYpP!4fwQy9sNn>U(0*wjL{ZkX7h+#*ZL?DRm1LPGlAf5H13cfBcG)#XBCWC<(K{8d(6x3EtT zbAl#AD}{^PfSM9R%*#p}fC^~rC8tDyX?LY0H5lH@jQF@S?ZFRDUn2d6!U^{xu=n=; z`>Z?F>h~??-oSxZ%2UbRHe$IU!HH(w+X%jlGF+8g@zn&S zLLiWV1X$)t2{^31Ao4U)6sWtQ&txZ{43O;=kbLc6Uug1_*-Rr8kQR1182!ejDEG>o zxGd&8UI3|=MseZx`Sbhy$A&AQdb64GYGrlBZTlD0`P48JI+a-W9GMOTqp=+5^V3Ou&3JoTG5P;-ZjA3WdjXb_03_h`r)0hc&|1!yLq;69sWrEf+g##$!N z_G1I+yPqm@0dom7e&y}@5aYaO!88osn>X)hr9Ag{VG%uUaJZW|$p{ygiqXaQuna)p z*d|gg6Q@_n|D>SiNquK8{v(}*m~BR@#gz7BWbi2&8b!(u74$Yq&I^~Gi4`b7p=1)0 zLu8=~0EzmlPFrr6)b9GRb}}Wp-bW+NpWw6kkn^8@{T7}GNhc_PymEiC{L^%-1@aur zE#~0qjKUA%scOr6+@Q`IK1(CSHRJ)Kzu5e{u>@fB#YXjQ8;Tt@4`p?a@F$ykcD|Bm z$J^P;AXZU%W4w3Y;7RuAywJUX19c75gI$Ut~t#f4SZ^{@HI)HHY{tq=XFZx+`f=i_Vz)yqedHj`J*aSSnf5( z%XVvF?{n($WjRwDA^f$Xd;lY&(1js(T%<{59G1R$`P!)J!`MKA+bkV!}dUi}Cn zz<LJ44>U+*j<)uCrh~vYHcE+{kS<%mh4&o zsj5XO(65Uonzp>k;>ClC>b(eN0fXO{usEpexWtID*mcSz(cP?te*qd9!s7X^meLiO(+3km;Ju1lpW7tawKbA%p$MOTFI%_Tr!>JtP>EOr=@#j!U)O*^nOh0#H zmkty0d5c3hPoI}Pd(2fjB!9TUHI#^ZCo8@xZt(mxviA0ox^G^$uw^u-v7+}LrT!!H`|M>0MvmK`p&OtSz z2gB&ga~c|%UG{~cIfKP^P3tS$6&~c9XtYt+C)5qv_M&gM(KXG~pT`qyoT;*E&tc^U zTQuQQ8;unRj=yT19|x3eXbW_%g<>r#9D9?d_N)3=ZYclx8^#Ybc5dLEp40`PLXVH z$=3Hi$*UoduKD7K=Tie4?R$wEGKyd<)}MRO32x*p*sl@C!Cb=4iCKabGLUO@X|E7Z zl`Q#omst%Z4aY8^9tropt{t*&^3B_3VWFqgUWv$J zT02ruSSzZ9>V>XG{WBTAoPy>#JF^v8Uw5YRBGQ4~;vkX~+U7vH@(Kb_(m5Gk=k`Zx ziZuLhN^gSl?MVFyTLf?fM5a>f(um~xQrwDihN9AL*_lQAk~}ediEf}(7c192Ci~9b zGIN#FaaJ!bWyz0*wC8w%YGb*J`eYp`(m`I9CQ-z|%iU;;i<)oWaiVYb{-+DWCQF!f7BH5{&kly}K(S zQ!^x6wYYYlbqZTB+6vBbi{k?i?Y&KY(B*y?O0n4ZgSm+%%gb+iwQN+}bv3*ri0iL- zakr%4N$}a%^p#0~>@q@PnCAKWqZ#+!x6%kwMeRBFIul5|Fr+Ps-x5ft)-`DuK59Wv7DJj4?Q zXb*TxqkgZTHnIt>FF;%sK3*JiO8yfVxCMKwZT5EHHN*NQZ0qzF)RI5;3`_c*&d8OA zLNuIGaBQ86DXwu%&uQ<_R$CeDuFr@cVUSM7Xy$D?U_ws!YyCI2K}d?_0WZ3e;1rP~ zmt|y;p)P(|rePEF$$k|qY3Tn&*EvQ<*1c^$W=Gwz(H+~a*zCAt+eyc%*tTt_W7~Gp zv2C*^&-0&|_nnzF^c*Rc%&m9u5;(dndAhoR!&)`#I*A^@8=LmA(e;#@Fqvzuzh6Q5*PO1b3eqa8KC(i~ zR$G~Zyih+gLDJRWGQ%NP0xloCfqjE$&|b|!wnT|u8(copuV^I6oRXYJrZjSKwU@A9 zk7T9axDY08%Cvsrs2-x?2!C3*OuxnM16+0Tt)dH^(M8os2&40Lh7Rmtn_jKf-@pzk zjq+2GS~hw1_ka4~VpkerW8-k5%YuSq>Z?;1yHslr=9gJfa_0WTcwk)vMe22**xoWn zi-6w_v{t3CUQ>owhz6OwV~S6f=4UsZQaS-t1$AN5lOLVx>d&ipS)k24OS0d)#1L&Z z-6tkW>GT0lRVtMCI@V1@+MxGbJ@$8p$SICUJml6mVaj zjh1w(q`8Cb44N3)L?ami!Q(NH^g+8-6Bp4xuIMf4ZZ`} zsrd%>4?@{D&5LXC?6Q%2rusM&uX96__mb%c*QD)ro;IB{Ec>a=YR8Q8-%f4bpbjj5 zj8uMSe?q}WacaWIKhul8E`STSnlOMUq9UlcddKBPp}1o(jqR!>X&>Iv#vbB_Lfp|8 zGDw5er?#2c7*Z3=Cn7kd11%DWB&0B{rg}W^_?LX$1&Immp+aZ_K|IW&z6DPtO1ATtdVG?niCq1h0cf~V6-_PvrNtE8_o#`Le?}Rv z?_<+W$aFMBq7n7ZRNQ$7>Vds?uU|M{MXiKcu8h{@iF`z zQd^^_QsclbHg@@b(8(S7dXel%_vh);?cf=T7zc{7eL&H;-;bCNC(=F(>(q-Vtj=#4 z;c7DLR432qe*yl=8^a6kAgFf(cG>-=t9^ij^O=BWph#=Yx4v`Av8;k9>AXjgH?|Y6 z#hF%iKSXIL_~>s8^$7ZQ7R(r8uRxgk3ynlq*vh!YqR4`KIeXg1QLZ;DE^JOP*YIc9 zmS4&2eovy@rxY%~_aVaY1APf3YpVkTE5RPSr*9>ix!fsYAv)F*SU4Br7na#RQ;Z{q z>HJTzw5=F7c(+5{XFbhNnZ_xSEAkChWqG{%Bx zedpGhyKhAW49`~p?G~0ssTS$j)_j$vg^do5w)jXW&HGV}iP;;Id{qcvkSa6SfTnPL z!}GF|7o_+2H*fF79(GFet@SqjhfDMPn9=^gmr8it7h9;(y^-(al7>~A5|ybE7u@_p z3~$rv{?#iZ8nBg%zh7AMa2g1OMP#Fq1BXbG!!?JgI_p|ra9q~|oATn$Cs*f{ma=Bi znQ1p_(*6dtuIg&Ae|fI?qdU`?!k3kwhM=Amz->-ff6}es(Fm<|npTTRMZfO=@|Q+_ zLf7TY&7aQRW*!$5>R4!dsx+f*^}KQl0vDpF5L+8(TS2)*{`O7ni@{*8v-JuPE_LZo zVvv&)2hGFCzH9vhj#oA)2?Mx~F11`=m(_nt%1gJM(Tm~ zHt*}~fcPNvTIwMxg?sUR2gQIMLEWGQK-UNCN9jB0@?|}qTsoL2Z)7!V*2ATmS#u}g zNjqt%=qf0AhO0foK(V5gT&qkD_jb@2<Oza zk*DJZwqjgq1dH>LuP?6dmL*^LA`qy?YZgvEv{AzRB^I6=LBSKcTTqT!Hv}IqCvXc6 zH56*NTlrRfm0@q07(gO|jdWHJwhs&O~8cG=Wc6*; zjP>~g2bQ_Q&E;oUWuiKTrYT;`;osc$eduNNwzttLjaEN+Wp;1Qf}q&BD^DIhjwwN# zIX|3eTx_fLZ47CHHHYHnFS5WBe_9|hih<%O6lV}5+t|eQva3|56nT+GcdeSZyIxy_ zi58g}TS0RS^~?P>SH!H+f{e>76RpyoulWE^-&>Mj;OzuVvoUuOEK0IZ;4ofUJ7hc~ z*N%QFvE4P1rmr#RdVsFM450+l-aUlZv8- zwME}&KeElehE<>h{E_KPhzj}%dnO7!8`iZOsQ2l%KH{~Ep%b(V9)dQ88-6#gd6LJQc!s3uCA3NKvzqXLuu z``{0+p9u&$fX0QfCzoS)pcAhmp%6N2ju8*EZ$!e`6jyBWx}v_x2Ohx}L5bjdcb|H@ zC*ss+7HCy{cY<9>OV@b*T5i^;`K(2-+aS?GDl8~-0`3S!tToS z6^x(e$DxWcQ4IgMU+b|(itFTgE9+V}hYQyJ3jCA4da4=V7OCmf-eh#sW#+imo)|aa z&8{c7u+W%s(4Kld7rD7@`9wGWLHvCm$owWcAx(3c#b{m3`pYdF{}wVzLEh0r_m&w; zuFmm^ZUfxLm8))6+#zPY1x$0}(oLq~L6(qD1@HT>&>YBbkKKrd5L*h>EKVCGxj{d( zT>m4)P8Zx|S_JBD_Pm zEp!CB3H}9+Ko2!gA(s!D|Ma^lSINP7(g^O+3Vbk+mYCcUP?0p?)48=m`=T}-AN>Aa^7%84CQNV zB@`w;o>hBYfOe@!XkjilZ@xQK#l_#%;-qfi+lbHKx(&D2NNO&ge-r6EB?h7ht*xJu zb-uN7M~n$S6f+EE<98u5^kCz*hlzU zVo5-O>iWXzlee=|_&=*B>$Vzf%|>4+5*o;JyKC%`=So&(v!t*5#GfI3&9~oitFZ{a z)Pz2XG<-N+mI0rP{(~=0-4ku3gW9HnauR%9v-}-^kEhccq}#AQ;H#P~WCqaZ`APcc zC==+d5wsTjY;?!oGNB3zA#d;d45cI6Q$upO)gb8tEs*Uy&hDoSxpyV6fVjpr>p)0N zbeqrZuWy}G&mDBg{WY9a;hiU5^ z+SAJo^)%@^QMvq+?t-p+c*$I3;u3z*+0O!=&=Xw#8}ex!G8MmU?x$tRsU#s$M&zpK z7mnFy@srFeK)Tq+Dgc>P(1#}2w7H__8{q$xQc4b8l^KP ztNlB^^t|H6hBzs&!W!IK$ex9JtlMXU@~0m*wX}AuYje$t=(78A&@lHi8fqA~_r_X^ zp6@SWwRxjL1GV%&d>#WNx)rmSkPg9W=5n7AaC5FHF;U#GvR}|OrPR#gR+`Ju3Ir_K z%76FYa%2a{PmD&B$d3V|c4$cWZ(l}X=WKsgr5K4a4u|Q-qW6(f1&Jtq2`R!slVK)J zv8nIwj0uaJSMun2DC+xP$wfY&+~O1%7!nxRC%3rpUuhseK2Q4pC%0%#CW++zTJ37c zYlQZWqVnSKti8gPtT}Aaa`p);{!1Hhf3EUr0Q)b!xCq8?&6ptO*GJs;*&>+$o^I)T ze{I3z0S39S`vCa_3wBv{c!i>XGL0g-X@WwPsX73)FUg;MsWZ6w1F*wKxs7x{+B zl2rJbuP3jI_L#Z;6Hw;xX4>(BbJY9Bt*)9I)q;y_aQe`Lj z16xbXw*hb&t<@J$ZP-0Js&k87b0tEaI};zNvS_VjnW0xWKv3s<;oQRD&-o9xs7kI| zh8LqTT2=dwdR|iYcqaR*-yZd)eYTM`-I@_7 z2OX_rk{7~G^TuC#F#Y_To|Qn;R+p;32m>5;U3|0cxwT5;)+G5TomdMY^z_A90o9+Q zZ#}7ZR&?`_AIWO?k}(ZjN*ZQckGAK`_{iiAb>wfMVVFP#U{ocN+K78LTNPtxr))Vc zzH(3P2yNL2xaaxuY@598W>NIA%;N%{cN5T9rvz7W%o4LZOH-sZ<8%3+b=Vkhq3Ph3 z_a?p@&C=*_d1PplIW*p-l#T!^u7a2S8@z+NAgPr_qR@I~BSzq_o3Ua&`-eCPhTl!* zbrW!t0^HH3WH3`s>o~7Br$y%^pB5bE8cKi7BN_tcgT$mHr(Sm{nG%IFb>*PoC{Z3B z_pcNEMX6vyXTVP8EDlFQ4#E}JPtaqrPV{NLzIOkeJ3?s3c~;Ev8MBU`eHmCUMk4uA znm3eUO4QosW#m)o1X~3f1t44VeaQlTfULnXdN;UvDctdYM+PVtYi1;I;9y^a3|Q$WK*klx8a}ZGFD4-UV+q znoex4c=#c{S*05z77t7UHt7=Wx9ZO*q-n)Vi@P;@AQABF1Qonu0|T~zVje{LVx^T5 zj0&`UTS2MmaP5FwtSTlDChR*7V4{7G>XE$5IiJ+5v)9pm2j9KhE)Prg9;-fwb*?d~ z#Mxhos0jn^h+ck6)$Lq>cg8fg@xZNotGUqYSK)E+h<`R~_+N@9iUz6A<>BP2;PZwtEPej~Tk>-20K%OASA-5d0}sPX96UXFKwMZDCm7DM}5_N?njAETH^ z_uYAQDOm~aH1$(w{^>5t``DX5OTy6ee-th<6KYGNPDDIan7vS@c9;H1uYUl920}Iw0 z@lUhYx{SRhocP=a-N7`t>xr&1s$l!P4m#3i)+crRiMIGBKFF^UQ@ZYTMWWOA=)9Od zq63<8V2@w{=V*Jj60B+}h+E?nA1?tY>jlvE9)x^bEac|Osrv0is2#GDJ|m~Dkbl`; z@*X9pf8Tb;8&##b+lsnSTnPq8J>1a=I=2sM==2*TQIZC1=n2G@!g8^Yg&d1F^LEDe zRaSXN!+~tn#O}c|irovf@8mzM+uQp;1?YWUC!{V1$KgR57qrXXV0dnhm4vP$Ew`CL z?ZG2#YG_b&Zm$VSq|e`klvD7`XsKI6|CziTh&G^g$hN28c-u9Xox-Nr39(a|GI%)+ z-EI2<^SCAKLh9ox#w+7@hjD*mJfX|ev!9kBnvYq2{C=jBv)G1rOY8EnY1>$X5vf4lOTdD^ zar4i%+oEo4D2y3&8}_HC!rZs^^svji@50P{zlDt?goc4e^l&p@B9$vl%Hdu+IqetS z%)+ZKeiL2l&y4n8t%Z50Q;}+CO_kWcNr)MixrEya7z^57cAq$vU*Qqj805?tV4xKs z$v)IwXh|6ne&XVzF3Z`ArGgIRGQG5Zy1T3PwMT5Z^VKNy#>TCN(fVV+dzTQ|b%|S# zi?ibAO*2PJ5%eYG3N3t<%o(KVSbhkz-^P0>0bsR_>)_w2P)mg)5hn3t;(=0cb)ze_ zbXS24-ZPl$O(SQFyligNx9rv@L7DbNQ!=I?Q4~7yx+P3gVpEzCtbdJvVQ^qSCWog{ zrQK`Vft>Dk$&4?rQel41G}YDwdw4f6-|b+8C4b%`K^!A7nV)S2w*_U+e=stu#0HAq zxJYz26I{unn5b)BryBU!Nk$wpWwEBSu;ir1RyV@}QNnpZ1FQwt{ZWV*mO+2Lw^4fB z+Iqi=cCg;xzAXsCYu+m2!Za@l$J5NjZljMda~Sp8s>x$vEFF>?C^6RS@7!X5 za<@M&1divjr>9g_ivtly(RH%P6 zr}J|@0pp(S`WJ4k&`eFSeGInte- zh^5T{1oM489P|R-^c*ofZ$3oV13ftdfK4y3pyY?13zQRH@|bl_R-JECLLbvqxnyA> zYf*6DIPTS>uxNk4q4C)WOmEY&%vsi3RcXxG+sC3b_7AX4<(Ks(aBlygH|DDF);C8e z)pPS6l%v48A#pLN@m#CCE7MebqcMy{{Zy0>*sqMI8ul(pnMS=x{KjX}O3CD~WH{)2 zkjX@C?yXb2(pA8VA8$UeT$02zueSvtuQ-B<0o7uEAZV)`$m{rr=g+69Gf5Bb3$UJ% z2$o_%s0EKcieh z%X0gz9O+?lAyZaEXYi7;+3CY>D6j@s3r^wsf`lw~?MG~X%ZEuJZ+Q*srzxko+u)BP z!%CU8cgS`HE_b*e_mm`;;qkYh)MS-nk18=sfGxs!74b;x4Mhgs(LP#%SA9dC16%U%k_u*n!dpH@Mb=Hv-{4k zy49jemP^QjZq!F)CjrUaRWU}MHj~Mqu(QKW`|6~&P#RVxS9Fq{-BsS;sbu*;Ng()i z7#A{&AE{tC!nY*O6h$X4<|#$CbHCSh48WV_C|jCeKG{rqs}$zswDCfw>U&TwDNu$? z-E67k*o9=~*RMRgNkG%cb4GZDmX8_DIG6L&(fP~6eTLf6$JQn3=(?p^-|UXB)}USU z0BDGtsQyM3qj^bGIazK~30t9tXey9<)bhbRDf}AiS(QZ1!(xVVQL)p&Tgc%&>j7g~V%S5Oj^yGzKi)Yq@QTmqy>}wVs<7ty2W>i+s7*;HWWCcC%qpkmDWPx2IC@oy42hXhYo(D*7taM-57ce z(4nHVY#Z7zSya=tDfgk79x1O5%EXiOLP;{R*It|hUagW!}ao|s5 zf6)1Iv`=?J1-s6`GQ&{(#Hx2cBN6|pLts8hvu|Kv!eC%=;9$xBwI4G4-*rguzoc24 z_{S^HPyCLQOVXF>;m>@;;sOtEh|h~Ysj&$S+W)|2{~3RM&@f@5{Hc4(aS64*(X%3S zYzqXSAdm4}#&2+9yueli&65hqq=&ukb}^a zIb;nGs^jHtg$c!Nt??C6NqZeKz1qVYLCUo^DMfKs++>_lp)!pgdtIx%r>{VnMUiDV=3f#5D)(F(^$IzN4;mE}V(}k_y8O?K6@MuepkHYhRmmhlOrNw`imEj_ zCB%D@B@J2uC}n9X)j^YSx9uw}=@`@<^&}&)Uob|4KYixgn+F6OrManqv5+a;I>a~1 zwS;3~=d*k@EM7EiXA{Db)~ws>7OzZ-VXTs)xtl8y{LfI;e)EV)&6G%{)G3pCR{_7M3q!>bzafV&LjR`ZA7%E*%CNa+g9saBhP>S(!R9} zSk0RaH6EdNSn&2)MUU7JPDbC(qn)ZdDrnUkZZ4!5sD|#lD4QJbzQ#hZ~goAe+{$9uV#&jHC#@v>iO@ouJRByU${x7o+k(J z5K4r@#MPEajRUkax0#I1DI3a@{VmlPU>2)%kSev>@w7|8z}~J)U@d;yQv4COZ+L!# zr_u1O(aOi#FQK>@&_JuoUFBPr%LQVs5LWk7IIdrsu_Vx>+)rSR{uKgJQj@q*S}2~5 z1RKRvZp3-azi;`^$WmgkjFv8NRn#%2->-0S+3I)%*r+C=yfg=%;O7G0!X|*>&j(mr zvq3ic)J&AkMknoJ(Gd|DNPOzwh3^^bMAkVr)g04RtfE~sF*1U7sY#$+)S9uxQ6FC4 zdq#d^(4W131ie;x;QQIRcoT>o{yoZ34z4LpVBokbp3vZb=e`zPe9sa%Fg3AN;mG4* z2tjt-5C1S2l2R(5yptbfHm8)-U@4m`|Em*BIo5NWXe9`I?yCj3EG49Ny_b>AUA}&( zK6ZO-Zyp0)(ZWHe3lYem&9QeTLT}JD&+hE2r!7B_<<%7@(UE3=!M!E@`g(4> zbCQD=Su!x#A9*n`$j2t_g91l0R2(|bEIX~Idn$A8kPz8&4~xPCB-wgf zFMQZt&f@0#Y*1T`*rx`v(I%^7pmh{6`M%K|l6zPLhQOJNp}w30Ru+8W?a7)CS{j1r z&dUb%R9$pjZe9hl=Dh$P*?GnNuS^N~ucadNYISCW#_T~)+OXcuJavigt;56_PMBwe zd{a%Utz5zEAJ4s06n&M#W51ZUVGLHFzr*(xQvpB5VSxbmx^fUG)CK3I`AfrTTN9|W z!PupX0i+%(@K)BVIrXw?_+qHLbykg)UH=2aDF^Vr#MiN<3wq4}wk|}C5lH9Dk=w|* zcbbB>gZDk0V;j$(G`t@k1{AV>VhKCvZm1YK3wabq$K#D>0%S`rV+sr;@N9rJ)nM@F z`kiP3_Dt<0QpIV{O-%#siB@ctCAZ$*6hH9o8isDjv9^*)tZ%ctIu6hu3Jacoo;0*! z19><7GYq;pS#&J`uLe|V?yfu^9`ejcuQliWIC?nqfi^uve6-*C9El^oyGul&;4|<2ywkE;MJNqGd|M-_~M0u8p zr_o18>Y+e!4n>$-!-v;405xNhv9k=5djT2^L=9aBzmJA zCxz&@ZuDhVc31gmxUJpGlI?Dm&m^?cHqK5$M1p1}YjUVm?W*Q%`wMSjWJjHdxfxh$ z<8LV`(LdPI;>AZaS~%cuDf_AM<9X7tkNESzWxhWUFN8U%@LDc>H)QPb{Sq+dUG>&6 zhoQR_*$B-|KK-_-NylkUgY(r}s*FE%H_QM}RM*d9=eEdM-j{A3WW4L(K;SkfX}A3G z!kiN%sZ|uMEL0cjki~SD_np(lr7Bx(8;y<@V5{0gYDzI6J4*hb+Qf?qAeP)E%z3Fl z{j(*Gvi4!Y?M&>eqfosnJ%`qYvdm$GMNHZmKuoHxKT00 zmrT@jJYz9|-dkBkO$>GhcxidJ-a&XQFR?}~FT*)$(2eMiG=hAP=KGJn`ucXy zr?F|*N}7tV9T2|5lDvZ!v#b7YfV%yo>Dg&FJEdx?=KyIil>n#V*ePAW0dj2!%XF-D zF)=!BGtLt3rdQ~AY)q-vL?nDy+={cy`egPS=FGG$xI)X5m%!2xmG2ZQRaUTE@2B}c z-~NGl(W1xL_?&FTV{&Ryy2d2~i!6KHhK@;^wP}0%1nb`^H@7tNhO8Unf2*O3{}1!E z_&Lv*{ZEB*2TuD*#Iq+$7LbvW5MLij{NK!%N;%$d*mH_iP(IO%dN|QZm!vCnYcNoN z4*O+jW$7MeguhYl`c?P1?Uh4nBX<+!#p}WLM7PP^M<)-Y)PV`co#jk$K9n-`YAX*I za{)Vy{XE5F$OjekVcRF->ruP`Qp4mK1%gTo*xX~{Ey6J2P}eDj8cJ|jzz8i`=+u6c z?KOOYT5$^wzp4cE6zysDKYvj^FoZdLHDmH5aEH0{Q8ZQj!mT0tDg^~Op+=(eHz3Jo zF-kSZ8b(Cz>%IPDz-AM|8#iQ~=4Td6e5l&1(dT;pEkTCz^G#*h=8HyR?Kk=Zl4hf9 z8_VUwoAC%&FjQ}7DI0l*dewR&bfjza820cTv-nqanXPLrlv~PP^IC><*FspgZcErp z@8b?T&V;2sPg@#fi6tT=8T8N z%jpLEG3!uwp^mv7&rm7QSU$9dmC>XfW0hmpe8=Gg8Am;S0&m@(Z`Z`x)3+B@XQjT@ zM9c~Xv`y%$m^t{`YB9iB>eJu4DkaIH2tran3$c~FBWo!fXk;QC(Rrd zs%|Z5=3flYCE<`kA>erq-h(Vw)uoc%>(5-}glhsbs>Ou5e7QSuGNzXV|M@RZo3j8Q z+*?CHLYCYf={uRU`kjr*GTy-@G?mz;*MsX=&^KaMn;b!DWwM1HbcKhoYt0o&n>y=ow?r$a%rd}N8QDN%+7CuG9I zEAfM@$MMh5Z)wYr)6e-;jH}yG`jaU5-ZD6B{*1m`ue&s)@Yx`E#LNJkFW;*Q*XDBc z$$N{_O_0d@hHX!Z0h*sLS=Q$}GwG@^h4}eYf;WC;On%H(->|5tv9*vuX`)L?c{r&2 zSloE@PU*Pjl#DHH|0pY!vH?Ug`SvRB+#buKPnFaFJT+rX08yw`kqR`N(DO$_G+;oLzbn^3mA&mKs~jf-^4ois zVKQE;F-m)&a9k@&NRiOK6A_wB;OG%QX)0pkw5KRD7ER7mm=V32;5GWNR(?-s`Po#4 zo$1|aOZB}v(!!o<->jo#p0y|$=wSDm=S|n0>Lhj*!~mEA-GhkcRpxFqoR1<^@%u>gDbE{JePkM80tW;bcOcs{`@~Tf)R44%zs8;$;qELB*9pUGj?RHK*M9!5>15k{%0F{H z_!sBeVaHKd2(0KW=tq_vOxrCY0{9C-QONC@ z2v^J78U9}#H>^%O;rwl%q*O|-*39(s&#BWB8NRsV|k22%U} zD250aTeDWY0JPdK&hejX!t{lD*rYb6|6Ix}Ij6XjuH|L!dtOhMB_y*uwK!LG2}pt4 z)L-4xChBQvnCb5;438uDXf!S3`WnT+-BxThc;i$M2&J4h<>kQgD+u3sMQ%>3EQA%$ z8ATbyj=h=$?gprg?&#q~#z_1$^zJ|L{r2Qf2A39<8sO)GMX|ZKJjAe=<0Hy)W#0$ zECE42Tap|4WlBCfyugwyhg4TXzi3OkFYNM4r$jL5%)-wmkUe*dx9$6hU%?emnzVYl zG*psG*2GEhpK5U7>K?+wDfQiwD)L9y`5_Am zkxeeqXpr#Q*hA7xtPu)U7dLLSJSO(3+gr(+63=hQ459SP#dRXkDRh?vlqB7zklz%} zH@eTv|N3eX;8iuuEG%iAUQoV~h~qv$+%SigIDw>QB4PST9*I)M~7KK8he-#b=pyL)}hE@}LIyO4&oH~@NVu_(0z zAitp(s&L7Sq0Pr&dnfDCOA@XvGxE zD=We9L{#h~qurVt-C36-Cf1{HbMjVB9%eU-i6Y?Zuj`zW?>I$Ms3KR)TRV+rahHaF z|IUC@iZgX5+=Gs++Ra|mNV_0JxW3|zith?6T)I!lZxN@YPlKR}-F_Vm0K8GsA3rt(>=jnVuGW;^+JQA5HX@KyEOYi+d4y4C*j+j^3Gj=YHoKAdqU#oh9 zfv6&Nn?>y<)}A{^PIDsJia;*J1P@Oy1$7l$_UpAE7Fu0)PyF`~#y3v|%l1tqt1LLs zP(2QGnRulT{!Hif*by|cB3X1E%3sc`^26R@I%D!x+#JwC~a7u zttG1Yh*E%foKtQdy1SBpb98`*PO{w<-4$ z31G7#P-d{S2#@LEgO-VoHVlDjJifTkbj(&pPq;-RI3Kp{>F}=;5==rfA70LgOUVs3 zqpmX_!(2^KhjN1Bh^B;G7m5(1+WP-b{@k*4#4L@`ijiH9ZMkA)7jmh)bvFaGBx?9B+T!WYtEi z>&ci&t65XUm%C!zXxEx)vg(+Cr- zg_1iI94t!shuyp+Q*NaD&Zm%IsJMhRK2l`AgsYa+PUw%$vtO#`Yw={I+}Nt+c&10{cz?=+q~ zfd&9A8JMlu<#L=`cX5CW4x1zpi9z_sPVvM6p_XrBZ=_hvX_l8dHGVa>Txjm{@q*Km zn5;hSW^IeLwqAZOhFI%b*B0=ZMll)35}Ur=S>CKtuNvd|JqAgW(WaWVQLw2l#)T;a zIPtx)>Q$cyRC1ZTPGp~A-wsza*#F*Q|1zx^^3Y^+k7xUwaRpN;jh4!_a}@T6g+~ry+9}9v{5cFNda`vCr3ZQj~j|tS3CnES)tI7A5q(J zfS3XWSTVeRNS`5%<~~K-Z|xHCcfB~9uT~B{4m}R^E@ACfQM(1(WNg1rj4twu9B0}Z zDw+-NN6Fx4QE zNib)o-K7 z;=$rEl_b*Hj?t_2VCgtKW^i-kW7kDHKoZA?W7og1t2m?4j^E-0lq}_^#wnljeL61! zln%sbZ^9CgJf0kQpden< zWT@s~+TA%%`u>t1+KEC-h_4Oi<9yq#kNOIQM?Q7U`N3dHqb0iyf7{;MZIddv0ySQL zClKy4Y|Kx8m#p6NO`P&M;h|Z0qx1Pswv}Rsqs+*$R*MJgKubNNI3F39)5h4*M9>m9 z4#7a*SC>Wku+|CKGSIV()x_YLaHfuvA%DWmn8P_#g=cJKwTVu2^a*irrX^N=(|vV{ z{BjfMSmP4Z_v!ZHxDb1Oq;*c{j_5h!4~M383UONNge9(>1zQy3YR(f@Y++4iqr_Sh zv%{93`BQ+Xm3MZCt@9*$>*A{Br~UACNfyE37xs1Z$03%y%$2??c$Uu1dcyYEzmtVj z{YtH7){ECxt@i`j3WJPvpP|cNvrecRuz(G>nmE;UL;;=^GA+kami~9ku?)GZ@B^v? z<2N*=Og3rFa|_%? zq^zLnbNeJ&Z=p!lR%i>x{OjJTX#|ptMn5@VgJVBryY9aB&efIJdtm7@3zHoGcbd#mcFJoBy2%0h^ z;!IxMiQ-%Bv|eRs84)WyI|9KoxN4vEIBSR^H=G2l=rI{HgR$Q`>=hg|GFPIt;RYuO z4b<+UA1%mhMvAJ#cW&#)Q(Y)XNL5^{DfLx|2#8R z_ROP5=cP?;WBGkvR59Afk-%xFg}AnS$RjyYAD#{j6>)uh&kM@@s3McR?$iyovgg8uD(ArVL9I6R zbY8NC?De~7{R_NzU1=zde4h<5kfckCQg4g+% z6%uhlA@3NT4M>>%O?I{;yfsWASUY@t*0RFdrK-@}H-YZi)$OTeOXqz*>KA`iCQHA` zKh#h5!P2zX`R{7HP}Cr1lbTOdIEW!CBuS>2uY=FF5MB#>aWx`zN;x8#w(G}GWQkLa zP##FG_#R4kO%d$Tpr7=(AVl}d49h=tAy?g$wB7R*>2}$Nh*tkdj2Owp-(;NeKv#IM zWt5_O^9C~y4 z7X6a+Jj^kp-@dS!pj2n*xQy@prlB|v>^(#=zg**(d8vEqu#GzIx<>&4-%#F_UO`*H zlkClpXY|*qeA2#lx8qF^}WX^WQmw`w9EZHyZE7u|(w ze;VrKR^+mVcQ#F;qOpn1Hzt8L{tr3+-`JGl2x@&c4oI-C9jn=m=L@aWId_Ua7H{+7 z8`oF&j)7w(M^!U_4PlFS+#Z8J90!t6uEkl(BOvTKyDOqFh^P`>(F4cT!80j7R1*%= zXP8m$doD8;(3IeA3b10|@H3=F2}-npDnNSArRYuI7yS45!>PzW7OIJFz3-^PevN23 zQYj!v55!~mA(<~2^3Q^%x57t|Q}@xBir#AG8$wqMTDuR4E{G{U{W!H; zz~o@lWwG&Qj}sTbm|ACN`&}Kd+Iy-M;hZ69=a+rXRCAGE)4R6dOaA4ecFSbXzJ0*pPcX3T zrn0llyLFV&%3e{zo!#FWX=$>q1nz*~MN*c{YKze^Li6U6A3q5y7qdwQhc0mH{q4?` zjH$SgT+gu+J+h*HPphhnQZfo@Gc<_tgj{WLH|jzeL{`9j=(!#}uiZs?(w;&PO<6(v z1Runz<7%knEUzk_HQt71c>iTm-K{Y%&Ypx^yFzOH#!$-L&j99ylakB1vT?So;ly=5 z-U3!nPFAf%CLQDm{0SVo&*~CCF^1L!$CT$ZXRSW`>&79>?(LLNaGUb=q*sk)Qn;W~ zcoD}z^kozTf+`%O1$jI#0e1;n@^4srT)?P@4X z_;nP+EuzoRx?!cBEB#SVR3qDi*mtLx{@+q&7T@s^AR{&3v`}%3aoQT*rmwSmRT1dL zh)k34^f+rJVV;^Ug%+x>F`TtKwONh04&BeLZNF0i&mVQdpXhdZcf`Au{G8N{e{+ae zZ$-A)ACiK9=bXAV$NR9;e&9pXU zKZMF>1F|MlHo>~|n^iHFd<#+zjjVqTH@#6?q5xhBJ8l}}oTlsqzWj!8`1%nKf(A?)9vvZPH7&>*k}`n{B0z39F_$MN{;_BcnfK z(lJ2B)n~t1Ic%>hs3gd%*0r}uoQHuTDLWf4>kpk-3uWHHd9buq1r$cLRMJsjL*|1wp~%SVmugOT6caWSL~omy5aKEchS zb>QO@f`^K_Ez9pYc@sPszHW`Ap7^C3zIe&-^HPYKy;?yLC6+1}7CQGXy~uJm!@G-$ zNn%a*&8{nry%TFxH68U&y--)u<9Bozdy(DycLmn5+VblDPm_vJUiik#PJA5@GRErX z2God*h}7RS8R{HS;@*!{Xer`UjAZevI3vU;9?s4x;N5qtnEt~c1tFvATUgNtP~dwx zc_G5{HEvWxoX`9$Wp6r11jC3S!?8xLq0PJw!)uM!i(X-0X6jQj)ezH>-wss22~*M{ zp-d(Msl?eCzG0s_#?sDQ1{z}ecQ7*{h;Fc^7FZL<2><8dWz9?VB!waQ}z z6tRHU)Bx|ii=aZW`u{f9f&W11UEiH<1v3BYL)&5jT473o1T zewqzjOc%kdY1_bP^2oYT&I+Ah+534rwx1@9A?C24P8WTOb9QTvZiuCS)E5MNR0l?U zFO7JfDU>(l#)Bic!66?Vca8Wt)Xn1ACYK8^Lc`pdQbv*e4?@BNax;b8ZO;17RZy68 zTL9=|s3SDvA2e7X@_0JbYR3pzCHR+;ZWUN9{!j(sh_M3yM@h$o^QZ~Um8ig=_#YfB zA+PJm=Y<@5!NGos{Tl}pBzV!+1+UXbdqCKsUP8?~hu;u?FP+P=%}}jnJ?A79J(cUq zVGU~_B@{^}H%e^pq&!C<{Avh0p-&?fw2#q>aP|xuf3vRIxoCDNx5UrlxRN|2GMB9? zWYTv%lb^~Ub%6^oaD+1QYKoTt%6lZ|#$+~OIx+nKXJwjgmEJ$2X@he-}!n}y|vJ-Ra0dXVs zT~`HJDr~@O>V`8=@kD&4K^H?vQot5VHv)}SW~I-~nstDX?LIXh#nAJtp6(+c3&*8( zC>U2>lN=(Uw0F@1x&8zA{X)_^T)R=v`f1z)!%b}E^T7KG=vHSHETfoi9fS$?$Hr)^ zq`%m-4`R_d6TEEE(p2!zBd&A;lMz(o^%CaGqpQ;(F=E5xhQmfk`~RjAH;towCm%~# zN=W`-&T(s)_`#ps9qFaT{@)tNh!=a!OAX{pVe?D3eXal0K)y@|{@raqiE81)%qG-> zC2?Ww98UV$$bN|zT}No=^*<^ZVZMgimH)32$TU%C;YCHWx4GA|NHge+XWjzL`p*-a z{ow!BP7}Vo;`KjlHF2=oFBOk0FSeQ%n!go~s#amP^#9b=n2U45aQZWK3&BIJ?gMP$ zkBY{-D7mhb#X)KpgL~ru*N7)M=fQ#T1&Yqp5T(YJ-U`9=)*~eEC+RpnJcnKj{z%=Z zHB}yWfnb^kyJ?V80XJC2@7q0P_o# zfPW1^op*Q8`EM|zuXFCPi8h9orjCGF+Mv(o-EmM*IF$!@78CW zmlR9Cr4lZ(_1X!KG3C)A@kZ8=U~&x=4+cd2IF{`hxh&;0HnVPfQ+*};?-PZfoo<2D z4#7$((Y$l976(UX&Q~{I?!COdpO$?g2A$-FS*?-ua>ixg`ogVH}=2lhbstQ{iVo^m1+Uohd2T4HihpM@ak7qZ11qym8_8NeKV zpzSsG$&D{k^*x%KT-DWG{m3~Z88`Y2DC$bOd8ypDv++v0M(om+{FS9*)|utjs5jys zxqsr?uci{xY8vj#rZ|CA1?gl$KX2|ZMJ_TA2q7n#DkqD>aeZrRiAs+1@@s*ngqeD?f^)*j;-bOabOa#oBMxZ=U+j z*p1p9F8>b5RioZMma%Yr6^PG~xX*sb8v<|m1YCVVxDQ`Ai#>hMNdm>P+a2naoJ<2X z0DUEPbjCs<9d#m&%C~3@zB{?}kI|hNM!OA;OFxC5TEYSU$CQ9-%o}e_;ISog4@t_+ zL}%DUs*K*uyDPzc?S8vWGU3DRH?wX<@^B8T)E|xl@PZ&oNqC{GqNJ7l>?CE)M+zE{ zs7C3MSBTKwQOJnsuQHathjfcMZen5C#j!mB=87B~-&)$x-HsIfF&g|rTRfHNJ!E0P z$seYBQV%Sq6oT@GzY=C47%jRv_77C#`14D^PwhMz)Cxz|7LN>csbz>Z^0R1l^^i?b zGmCG-!=HTtr<}$gxxUKkdT@)jDScLtfc~+^-KcX@whE0ELI3miIKd1+wO~GVd>-;5 zUruI$o`HX|w#jM@Tdx-2IA1^2JF2wF{@=Ssyp@hTAO72cr*~ZeLVVny(7cj9a<9i& zqBkM_zRWFM5akx8=^2x$fD)2F?_kT_P?;#6eMuZXP<*xw_AjTugLx&)u+noE5%Cc- zIP1n=$qe6lx>N^o*x@%PSzQu;U3oLP>C)>5Y$(-Em%WpUVB}_;yPP8MxOa(g9C)9vSs}$=IzI z2&r6X$$OuSisqEr|Q{(;Gx+8~mQAP^x0h8+U;xyv=#fy`<4(cNx^)Tb}h7n}gQ!x`g zlSiD$9kM9YNEnUX`xgpp4^R)kidkb%mVQf4!dBLjP*|^rj=Rn|U$}8}bm^4!`;=mpbys-$6zOLtk#!c85vqx~InqiSaR2#A83=3yUq{a$J^dsv!Xl1A z4mvwuN@9FZ0Blq}N4x@e1>0F7Y&r=>76f3Xa0eD@jVYe8W&zz$5@5X|8$z+0dg#?( z(u-6+9d1rLw~AS}u!ary_RoFEspefrVpf`i{%uOHNdBBLD)0p+`|j_~kK1+0!Dm20 zCfqdK7)Sgc8CmIt2zUbn^MW7ez`~UOCnMYZCnKBC3@Y+S%O0A~p061Ei%HNE8P)k` zp6x!5B`Nz~8QIo*JkzAXx6~t)YrE%^^r2|RE$w%0i@~RLi6*cP^4Zfx6WF!r!KCX2 zhxog7`@JZ4{$}c5e>1hh-^#yS*ov1-&Gy13$h;);7qJ|wI0knX>@RE%-W%6=@Cfii zL0!W@VAkiSMhz&20b&+EXx{zNgXs-B^zOm&lQNveFnGG;{Hv@hti% zMU=QLmCGAL0^Emmu2+2ac{w+}I{!1iVvp$`{*GUBvmPVU%DlU_wEtG0*y{;_IY>p! zIK;_j@8C%SdpTTuASBk9+mQ#);kLiH%%f36=2J(_0n-!H%1Mlx_L-e`M3ax%v{u^^ z`HwHSC#&-}|8?Ra(}bSn=YL&w_&?Vuc9tPMx+n=h*owx|nUz1ACoWhVo6`-mn~|w) zKL|4!ihK+H_mQ`}dOk#`wZroR%E{uaqH;VP8gTA_l>kn{6Zg_c(=P@p+R3idr?mag z5mMzQ3u9yP0crny8zRY%DBAbzKEG3xH92VBKcx${Jb2!FG~V2Bxj3Dj+8yrgY;CMk zpDwNGFU-QQY^kcaoZR2L8RnT{wziqCc6sqCqJR&@*{Oe=G{<)SrTqP6JxZd25E7jfZBf@X2Y^ceEEt)fenNG(s_T;H*e0N z6j0z{Gd?X%EqR7IZMI9w=CZ(U4QkUjho`fFgja%8b==^Ma03=0k8K|E>J^iZv}~Tu(Q| zpX9se(H|UuE5a0G|6*FxCmUxMd~dMLJWa5>`yU?{0NU?9x0k8O4);SySQcAjh*kRc z58||XgH*IU2SzIQ6WjTBZPc~CI~-Cn$X_-}#nIqe!n-;-Rj?wgN;^?b)vZ2&(@T;G zrf6zF9+?%d3~H~Xxr*B{bGm0LID_aii=X#ty?-)Y*ex@E3*^K*l-ok_n#KUeQT%X+ zsuj2u^1>{>nqMUzqx&Y8FVh}SNgg&blvQnrQjoNnvG5shmj*;j~SA-#n{E6KA7 zIn7mHc|{=acRI$yyjCa!_HKYP)pW?b(b@fFUnQN4|ZB+iR?y+-VEiwy(5U zB|c+EgUs`5-9U$Qis|shL3Er-b-`^CqxR;dYL$r(JAul5gnU9L7ChO0-^|t2{u|YV z#r&KUs!qP;6Ef2xdzTEX2@jPWV%G|;i3dH9A*Q%3(E+nwbSJ#PK9MH}_o-d7iWd zbzQXoYf(?5e5Bm(N+f*hgwl$A%z;N1e<0@8aDN-Io5XVYwRWms?ew?-^D1`pyynT5 zvEpc0BYcox*0$q!z=N6MhAJ<2lyX>T=jNiRA#@&1^+ff?AaCd+;6C8Fw8DW!Zc7-~ zK_obauNd*tbrv#;h4KCV{DV%t!mfH@rp6CO)5CFS9llZNt9mq4G+ISt{Mj}Dt@kUMIdc9f4YI;4~P=k39q0L`T@Q7mCx z^Zshlv%A41iFfEcW;xVL))A61M+N{GU?DLCbU?mEKf7Bs>8^eGg=&aJxT9=4&usFr zx9Zw3LN&+^g^1kLLm-=7LMCXzIQKgAJ3iBE?FS6%{1KpY|tD>5N z!_597#5@*?`6<=v?f4`oL%4*@y>%LML=OBRmgIi35l7a@PZWGGsxfp%7&6xegO zBNe7iDMo%{Wpftq%CgfwdrzGuPOta_$0#}Ri*%G^Y@Aop6tlBtKXT|E6$wXV+Q_q>KZ$H#i5?1BYT+JI_U-le+Moa`dmd?>hWMnvx%@EkH7$Q#3qj~2*kHJ`p=hW`g9~19FlFubF!H(N ziTx3x!J^d6?(!@x?PcS%7fr0K1+OG}mG0(g{^#{h^H_cZJO0_StB>-0@=f+@w+Ejbw|8oDv0Y`4qkqa+WCzM3xmBf% zqlxpGhx8kWgGpROepiq7vyy+DJ`MC0N98SvTpqtm-Vqa!Z_>T8hy1Ls*$7!KN+~#6 zst6-oNu_0@mNh6u8O$vmc{o8^cHu(Ms z{4M~Kbl+&{s^?~j9h)=9O@RZE2NGMX-`L{PT1ln%Y3DTIUaQR=?~v3=nf!aej{0L| z6IW%GBDg@UfgHFuzV5aM9>cNpns|I0Os39aYn-lS_1Ah>8b5_Bcz>R>OmjMS`K*7! zzK%yQ^9O}}fjlyvpcK+6p48l>_NVE>*F~>s=`&4nd3y#RA)8g9liy3!vRvvw_-#$M zCV{-vO*!dZR-SF4+v4!6FTRiLa~t|as3|4-#V$^@Kg_;!lTXwjXp-^9OGUD4)qmf_s-FqA!G(SZc%ger&r>JH)kd++&C{wmc-7h!ups`;h`mE3lhZ+bgGL zIiF_J$JK`{K6)vRtv`jackB+l4SRzHN`NyrZBkO~OcIF@9d@bDI13VkWwca`5+yfoT0P^0wmexa

>w|ks?m2j0X#}Gp{;4=gYo!={hGFj^ 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
0Dw(L~+i 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%0A6L_4 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�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 Zi$q8i*;Y^lzU8Ot`H~B2!!v<{`(Ho)5|RJ_ literal 0 HcmV?d00001 diff --git a/docs/source/api/graphics/ImageGraphic.rst b/docs/source/api/graphics/ImageGraphic.rst index d4fa2e7b9..dd5ff1ccc 100644 --- a/docs/source/api/graphics/ImageGraphic.rst +++ b/docs/source/api/graphics/ImageGraphic.rst @@ -30,6 +30,7 @@ Properties ImageGraphic.interpolation ImageGraphic.name ImageGraphic.offset + ImageGraphic.right_click_menu ImageGraphic.rotation ImageGraphic.supported_events ImageGraphic.visible diff --git a/docs/source/api/graphics/LineCollection.rst b/docs/source/api/graphics/LineCollection.rst index 459884fdd..ad4b7f929 100644 --- a/docs/source/api/graphics/LineCollection.rst +++ b/docs/source/api/graphics/LineCollection.rst @@ -33,6 +33,7 @@ Properties LineCollection.names LineCollection.offset LineCollection.offsets + LineCollection.right_click_menu LineCollection.rotation LineCollection.rotations LineCollection.supported_events diff --git a/docs/source/api/graphics/LineGraphic.rst b/docs/source/api/graphics/LineGraphic.rst index a3e1587f7..cb924b4dc 100644 --- a/docs/source/api/graphics/LineGraphic.rst +++ b/docs/source/api/graphics/LineGraphic.rst @@ -29,6 +29,7 @@ Properties LineGraphic.event_handlers LineGraphic.name LineGraphic.offset + LineGraphic.right_click_menu LineGraphic.rotation LineGraphic.supported_events LineGraphic.thickness diff --git a/docs/source/api/graphics/LineStack.rst b/docs/source/api/graphics/LineStack.rst index 3c14e708c..db060a4c2 100644 --- a/docs/source/api/graphics/LineStack.rst +++ b/docs/source/api/graphics/LineStack.rst @@ -33,6 +33,7 @@ Properties LineStack.names LineStack.offset LineStack.offsets + LineStack.right_click_menu LineStack.rotation LineStack.rotations LineStack.supported_events diff --git a/docs/source/api/graphics/ScatterGraphic.rst b/docs/source/api/graphics/ScatterGraphic.rst index 595346f07..8f15e827a 100644 --- a/docs/source/api/graphics/ScatterGraphic.rst +++ b/docs/source/api/graphics/ScatterGraphic.rst @@ -29,6 +29,7 @@ Properties ScatterGraphic.event_handlers ScatterGraphic.name ScatterGraphic.offset + ScatterGraphic.right_click_menu ScatterGraphic.rotation ScatterGraphic.sizes ScatterGraphic.supported_events diff --git a/docs/source/api/graphics/TextGraphic.rst b/docs/source/api/graphics/TextGraphic.rst index 107bc1c74..2a55d78ef 100644 --- a/docs/source/api/graphics/TextGraphic.rst +++ b/docs/source/api/graphics/TextGraphic.rst @@ -30,6 +30,7 @@ Properties TextGraphic.offset TextGraphic.outline_color TextGraphic.outline_thickness + TextGraphic.right_click_menu TextGraphic.rotation TextGraphic.supported_events TextGraphic.text diff --git a/docs/source/api/layouts/figure.rst b/docs/source/api/layouts/figure.rst index 817284e18..17ee965b6 100644 --- a/docs/source/api/layouts/figure.rst +++ b/docs/source/api/layouts/figure.rst @@ -6,7 +6,7 @@ Figure ====== Figure ====== -.. currentmodule:: fastplotlib +.. currentmodule:: fastplotlib.layouts Constructor ~~~~~~~~~~~ @@ -24,10 +24,8 @@ Properties Figure.canvas Figure.controllers Figure.names - Figure.output Figure.renderer Figure.shape - Figure.toolbar Methods ~~~~~~~ @@ -38,6 +36,8 @@ Methods Figure.clear Figure.close Figure.export + Figure.get_pygfx_render_area + Figure.open_popup Figure.remove_animation Figure.render Figure.show diff --git a/docs/source/api/layouts/imgui_figure.rst b/docs/source/api/layouts/imgui_figure.rst new file mode 100644 index 000000000..38a546ae9 --- /dev/null +++ b/docs/source/api/layouts/imgui_figure.rst @@ -0,0 +1,49 @@ +.. _api.ImguiFigure: + +ImguiFigure +*********** + +=========== +ImguiFigure +=========== +.. currentmodule:: fastplotlib.layouts + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: ImguiFigure_api + + ImguiFigure + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: ImguiFigure_api + + ImguiFigure.cameras + ImguiFigure.canvas + ImguiFigure.controllers + ImguiFigure.guis + ImguiFigure.imgui_renderer + ImguiFigure.names + ImguiFigure.renderer + ImguiFigure.shape + +Methods +~~~~~~~ +.. autosummary:: + :toctree: ImguiFigure_api + + ImguiFigure.add_animations + ImguiFigure.add_gui + ImguiFigure.clear + ImguiFigure.close + ImguiFigure.export + ImguiFigure.get_pygfx_render_area + ImguiFigure.open_popup + ImguiFigure.register_popup + ImguiFigure.remove_animation + ImguiFigure.render + ImguiFigure.show + ImguiFigure.start_render + diff --git a/docs/source/api/layouts/subplot.rst b/docs/source/api/layouts/subplot.rst index efe2fa4fc..3de44222d 100644 --- a/docs/source/api/layouts/subplot.rst +++ b/docs/source/api/layouts/subplot.rst @@ -35,6 +35,7 @@ Properties Subplot.renderer Subplot.scene Subplot.selectors + Subplot.toolbar Subplot.viewport Methods @@ -56,6 +57,7 @@ Methods Subplot.center_title Subplot.clear Subplot.delete_graphic + Subplot.get_figure Subplot.get_rect Subplot.insert_graphic Subplot.map_screen_to_world diff --git a/docs/source/api/selectors/LinearRegionSelector.rst b/docs/source/api/selectors/LinearRegionSelector.rst index 34df92b2a..bb406b7e2 100644 --- a/docs/source/api/selectors/LinearRegionSelector.rst +++ b/docs/source/api/selectors/LinearRegionSelector.rst @@ -29,6 +29,7 @@ Properties LinearRegionSelector.name LinearRegionSelector.offset LinearRegionSelector.parent + LinearRegionSelector.right_click_menu LinearRegionSelector.rotation LinearRegionSelector.selection LinearRegionSelector.supported_events diff --git a/docs/source/api/selectors/LinearSelector.rst b/docs/source/api/selectors/LinearSelector.rst index 31f546e2c..d434ef82f 100644 --- a/docs/source/api/selectors/LinearSelector.rst +++ b/docs/source/api/selectors/LinearSelector.rst @@ -29,6 +29,7 @@ Properties LinearSelector.name LinearSelector.offset LinearSelector.parent + LinearSelector.right_click_menu LinearSelector.rotation LinearSelector.selection LinearSelector.supported_events diff --git a/docs/source/api/selectors/RectangleSelector.rst b/docs/source/api/selectors/RectangleSelector.rst index b2dc40d2e..930e12c67 100644 --- a/docs/source/api/selectors/RectangleSelector.rst +++ b/docs/source/api/selectors/RectangleSelector.rst @@ -29,6 +29,7 @@ Properties RectangleSelector.name RectangleSelector.offset RectangleSelector.parent + RectangleSelector.right_click_menu RectangleSelector.rotation RectangleSelector.selection RectangleSelector.supported_events diff --git a/docs/source/api/ui/BaseGUI.rst b/docs/source/api/ui/BaseGUI.rst new file mode 100644 index 000000000..788e1414a --- /dev/null +++ b/docs/source/api/ui/BaseGUI.rst @@ -0,0 +1,30 @@ +.. _api.BaseGUI: + +BaseGUI +******* + +======= +BaseGUI +======= +.. currentmodule:: fastplotlib.ui + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: BaseGUI_api + + BaseGUI + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: BaseGUI_api + + +Methods +~~~~~~~ +.. autosummary:: + :toctree: BaseGUI_api + + BaseGUI.update + diff --git a/docs/source/api/ui/EdgeWindow.rst b/docs/source/api/ui/EdgeWindow.rst new file mode 100644 index 000000000..5835ab847 --- /dev/null +++ b/docs/source/api/ui/EdgeWindow.rst @@ -0,0 +1,38 @@ +.. _api.EdgeWindow: + +EdgeWindow +********** + +========== +EdgeWindow +========== +.. currentmodule:: fastplotlib.ui + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: EdgeWindow_api + + EdgeWindow + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: EdgeWindow_api + + EdgeWindow.height + EdgeWindow.location + EdgeWindow.size + EdgeWindow.width + EdgeWindow.x + EdgeWindow.y + +Methods +~~~~~~~ +.. autosummary:: + :toctree: EdgeWindow_api + + EdgeWindow.draw_window + EdgeWindow.get_rect + EdgeWindow.update + diff --git a/docs/source/api/ui/Popup.rst b/docs/source/api/ui/Popup.rst new file mode 100644 index 000000000..a154e9ce9 --- /dev/null +++ b/docs/source/api/ui/Popup.rst @@ -0,0 +1,33 @@ +.. _api.Popup: + +Popup +***** + +===== +Popup +===== +.. currentmodule:: fastplotlib.ui + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: Popup_api + + Popup + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: Popup_api + + +Methods +~~~~~~~ +.. autosummary:: + :toctree: Popup_api + + Popup.clear_event_filters + Popup.open + Popup.set_event_filter + Popup.update + diff --git a/docs/source/api/ui/Window.rst b/docs/source/api/ui/Window.rst new file mode 100644 index 000000000..63c384261 --- /dev/null +++ b/docs/source/api/ui/Window.rst @@ -0,0 +1,30 @@ +.. _api.Window: + +Window +****** + +====== +Window +====== +.. currentmodule:: fastplotlib.ui + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: Window_api + + Window + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: Window_api + + +Methods +~~~~~~~ +.. autosummary:: + :toctree: Window_api + + Window.update + diff --git a/docs/source/api/ui/index.rst b/docs/source/api/ui/index.rst new file mode 100644 index 000000000..4f31e651a --- /dev/null +++ b/docs/source/api/ui/index.rst @@ -0,0 +1,10 @@ +UI Bases +******** + +.. toctree:: + :maxdepth: 1 + + BaseGUI + Window + EdgeWindow + Popup diff --git a/docs/source/api/widgets/ImageWidget.rst b/docs/source/api/widgets/ImageWidget.rst index 3ca384968..fbafd4723 100644 --- a/docs/source/api/widgets/ImageWidget.rst +++ b/docs/source/api/widgets/ImageWidget.rst @@ -30,8 +30,6 @@ Properties ImageWidget.n_scrollable_dims ImageWidget.ndim ImageWidget.slider_dims - ImageWidget.sliders - ImageWidget.widget ImageWidget.window_funcs Methods @@ -39,7 +37,10 @@ Methods .. autosummary:: :toctree: ImageWidget_api + ImageWidget.add_event_handler + ImageWidget.clear_event_handlers ImageWidget.close + ImageWidget.remove_event_handler ImageWidget.reset_vmin_vmax ImageWidget.reset_vmin_vmax_frame ImageWidget.set_data diff --git a/docs/source/conf.py b/docs/source/conf.py index 1b296f533..913cfd50f 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -10,12 +10,16 @@ os.environ["WGPU_FORCE_OFFSCREEN"] = "1" import fastplotlib +import pygfx from pygfx.utils.gallery_scraper import find_examples_for_gallery from pathlib import Path import sys from sphinx_gallery.sorting import ExplicitOrder import imageio.v3 as iio +MAX_TEXTURE_SIZE = 2048 +pygfx.renderers.wgpu.set_wgpu_limits(**{"max-texture-dimension2d": MAX_TEXTURE_SIZE}) + ROOT_DIR = Path(__file__).parents[1].parents[0] # repo root EXAMPLES_DIR = Path.joinpath(ROOT_DIR, "examples", "desktop") @@ -52,6 +56,7 @@ "subsection_order": ExplicitOrder( [ "../../examples/desktop/image", + "../../examples/desktop/image_widget", "../../examples/desktop/gridplot", "../../examples/desktop/line", "../../examples/desktop/line_collection", @@ -59,6 +64,7 @@ "../../examples/desktop/heatmap", "../../examples/desktop/misc", "../../examples/desktop/selectors", + "../../examples/desktop/guis" ] ), "ignore_pattern": r'__init__\.py', diff --git a/docs/source/generate_api.py b/docs/source/generate_api.py index 0150836ec..dbe1d8005 100644 --- a/docs/source/generate_api.py +++ b/docs/source/generate_api.py @@ -9,6 +9,7 @@ from fastplotlib.graphics import _features, selectors from fastplotlib import widgets from fastplotlib import utils +from fastplotlib import ui current_dir = Path(__file__).parent.resolve() @@ -19,6 +20,7 @@ GRAPHIC_FEATURES_DIR = API_DIR.joinpath("graphic_features") SELECTORS_DIR = API_DIR.joinpath("selectors") WIDGETS_DIR = API_DIR.joinpath("widgets") +UI_DIR = API_DIR.joinpath("ui") doc_sources = [ API_DIR, @@ -27,6 +29,7 @@ GRAPHIC_FEATURES_DIR, SELECTORS_DIR, WIDGETS_DIR, + UI_DIR, ] for source_dir in doc_sources: @@ -143,11 +146,18 @@ def generate_page( def main(): generate_page( page_name="Figure", - classes=[fastplotlib.Figure], - modules=["fastplotlib"], + classes=[fastplotlib.layouts._figure.Figure], + modules=["fastplotlib.layouts"], source_path=LAYOUTS_DIR.joinpath("figure.rst"), ) + generate_page( + page_name="ImguiFigure", + classes=[fastplotlib.layouts.ImguiFigure], + modules=["fastplotlib.layouts"], + source_path=LAYOUTS_DIR.joinpath("imgui_figure.rst"), + ) + generate_page( page_name="Subplot", classes=[Subplot], @@ -258,17 +268,37 @@ def main(): ) ############################################################################## + ui_classes = [ui.BaseGUI, ui.Window, ui.EdgeWindow, ui.Popup] + + ui_class_names = [cls.__name__ for cls in ui_classes] + + ui_class_names_str = "\n ".join([""] + ui_class_names) + + with open(UI_DIR.joinpath("index.rst"), "w") as f: + f.write( + f"UI Bases\n" + f"********\n" + f"\n" + f".. toctree::\n" + f" :maxdepth: 1\n" + f"{ui_class_names_str}\n" + ) + + for ui_cls in ui_classes: + generate_page( + page_name=ui_cls.__name__, + classes=[ui_cls], + modules=["fastplotlib.ui"], + source_path=UI_DIR.joinpath(f"{ui_cls.__name__}.rst"), + ) + + ############################################################################## + utils_str = generate_functions_module(utils.functions, "fastplotlib.utils") with open(API_DIR.joinpath("utils.rst"), "w") as f: f.write(utils_str) - # gpu selection - fpl_functions = generate_functions_module(fastplotlib, "fastplotlib.utils.gpu") - - with open(API_DIR.joinpath("gpu.rst"), "w") as f: - f.write(fpl_functions) - if __name__ == "__main__": main() diff --git a/docs/source/index.rst b/docs/source/index.rst index f855569e3..4caa7fc7e 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -15,10 +15,12 @@ Welcome to fastplotlib's documentation! fastplotlib Figure + ImguiFigure Subplot Graphics Graphic Features Selectors + UI Widgets Utils @@ -31,8 +33,7 @@ Welcome to fastplotlib's documentation! Summary ======= -A fast plotting library built using the `pygfx `_ render engine utilizing `Vulkan `_, `DX12 `_, or `Metal `_ via `WGPU `_, so it is very fast! We also aim to be an expressive plotting library that enables rapid prototyping for large scale explorative scientific visualization. `fastplotlib` will run on any framework that ``pygfx`` runs on, this includes ``glfw``, ``Qt`` and ``jupyter lab`` - +Next-gen plotting library built using the `pygfx `_ render engine utilizing `Vulkan `_, `DX12 `_, or `Metal `_ via `WGPU `_, so it is very fast! ``fastplotlib`` is an expressive plotting library that enables rapid prototyping for large scale exploratory scientific visualization. ``fastplotlib`` will run on any framework that ``pygfx`` runs on, this includes ``glfw``, ``Qt`` and ``jupyter lab`` Installation ============ @@ -41,17 +42,6 @@ For installation please see the instructions on GitHub: https://github.com/kushalkolar/fastplotlib#installation -FAQ -=== - -1. Axes, axis, ticks, labels, legends - -A: They are on the `roadmap `_ and expected by summer 2024 :) - -2. Why the parrot logo? - -A: The logo is a `swift parrot `_, they are the fastest species of parrot and they are colorful like fastplotlib visualizations :D - Contributing ============ diff --git a/docs/source/user_guide/faq.rst b/docs/source/user_guide/faq.rst index aa4f3dc87..ebf17fcd6 100644 --- a/docs/source/user_guide/faq.rst +++ b/docs/source/user_guide/faq.rst @@ -122,4 +122,9 @@ How can I use `fastplotlib` interactively? 2. IPython - Users can select between using a Qt backend or glfw using the same methods as above. \ No newline at end of file + Users can select between using a Qt backend or glfw using the same methods as above. + +Why the parrot logo? +-------------------- + + The logo is a `swift parrot `_, they are the fastest species of parrot and they are colorful like fastplotlib visualizations :D \ No newline at end of file diff --git a/docs/source/user_guide/guide.rst b/docs/source/user_guide/guide.rst index df0c54c78..4c482e03a 100644 --- a/docs/source/user_guide/guide.rst +++ b/docs/source/user_guide/guide.rst @@ -175,12 +175,19 @@ Using our example from above: once we add a ``Graphic`` to the figure, we can th .. image:: ../_static/guide_hello_world_vmax.png -``Graphic`` properties also support slicing and indexing. For example :: +``Graphic`` properties also support numpy-like slicing for getting and setting data. For example :: - image_graphic.data[::8, :, :] = 1 - image_graphic.data[:, ::8, :] = 1 + # basic numpy-like slicing, set the top right corner + image_graphic.data[:150, -150:] = 0 -.. image:: ../_static/guide_hello_world_data.png +.. image:: ../_static/guide_hello_world_simple_slicing.png + +Fancy indexing is also supported! :: + + bool_array = np.random.choice([True, False], size=(512, 512), p=[0.1, 0.9]) + image_graphic.data[bool_array] = 254 + +.. image:: ../_static/guide_hello_world_fancy_slicing.png Selectors @@ -207,16 +214,15 @@ data. Let's look at an example: :: # add a linear selector the sine wave selector = sine_graphic.add_linear_selector() - fig[0, 0].auto_scale() - fig.show(maintain_aspect=False) -.. image:: ../_static/guide_linear_selector.gif +.. image:: ../_static/guide_linear_selector.webp A ``LinearRegionSelector`` is very similar to a ``LinearSelector`` but as opposed to selecting a singular point of your data, you are able to select an entire region. +See the examples gallery for more in-depth examples with selector tools. Now we have the basics of creating a ``Figure``, adding ``Graphics`` to a ``Figure``, and working with ``Graphic`` properties to dynamically change or alter them. Let's take a look at how we can define events to link ``Graphics`` and their properties together. @@ -271,8 +277,10 @@ Rendering engine (``pygfx``) events: When an event occurs, the user-defined event handler will receive and event object. Depending on the type of event, the event object will have relevant information that can be used in the callback. See below for event tables. +Event Attributes +^^^^^^^^^^^^^^^^ -**All ``Graphic`` events have the following attributes:** +All ``Graphic`` events have the following attributes: +------------+-------------+-----------------------------------------------+ | attribute | type | description | @@ -447,9 +455,9 @@ For example: :: # change the closest graphic color to white nearest.colors = "w" - fig.show() + fig.show() -.. image:: ../_static/click_event.gif +.. image:: ../_static/guide_click_event.webp ImageWidget ----------- @@ -473,7 +481,7 @@ to easily navigate through different dimensions of your data. Let's look at an e iw_movie.show() -.. image:: ../_static/guide_image_widget.gif +.. image:: ../_static/guide_image_widget.webp Animations ---------- @@ -483,24 +491,41 @@ An animation function is a user-defined function that gets called on every rende import fastplotlib as fpl import numpy as np - data = np.random.rand(512, 512) + # generate some data + start, stop = 0, 2 * np.pi + increment = (2 * np.pi) / 50 - fig = fpl.Figure() + # make a simple sine wave + xs = np.linspace(start, stop, 100) + ys = np.sin(xs) - fig[0,0].add_image(data=data, name="random-img") + figure = fpl.Figure(size=(700, 560)) - def update_data(plot_instance): - new_data = np.random.rand(512, 512) - plot_instance["random-img"].data = new_data + # plot the image data + sine = figure[0, 0].add_line(ys, name="sine", colors="r") - fig[0,0].add_animations(update_data) - fig.show() + # increment along the x-axis on each render loop :D + def update_line(subplot): + global increment, start, stop + xs = np.linspace(start + increment, stop + increment, 100) + ys = np.sin(xs) + + start += increment + stop += increment -.. image:: ../_static/guide_animation.gif + # change only the y-axis values of the line + subplot["sine"].data[:, 1] = ys -Here we are defining a function that updates the data of the ``ImageGraphic`` in the plot with new random data. When adding an animation function, the -user-defined function will receive a plot instance as an argument when it is called. + + figure[0, 0].add_animations(update_line) + + figure.show(maintain_aspect=False) + +.. image:: ../_static/guide_animation.webp + +Here we are defining a function that updates the data of the ``LineGraphic`` in the plot with new data. When adding an animation function, the +user-defined function will receive a subplot instance as an argument when it is called. Spaces ------ @@ -532,6 +557,20 @@ There are several spaces to consider when using ``fastplotlib``: For more information on the various spaces used by rendering engines please see this `article `_ +Imgui +----- + +Fastplotlib uses `imgui_bundle `_ to provide within-canvas UI elemenents if you +installed ``fastplotlib`` using the ``imgui`` toggle, i.e. ``fastplotlib[imgui]``, or installed ``imgui_bundle`` afterwards. + +Fastplotlib comes built-in with imgui UIs for subplot toolbars and a standard right-click menu with a number of options. +You can also make custom GUIs and embed them within the canvas, see the examples gallery for detailed examples. + +.. note:: + Imgui is optional, you can use other GUI frameworks such at Qt or ipywidgets with fastplotlib. You can also of course + use imgui and Qt or ipywidgets. + +.. image:: ../_static/guide_imgui.png Using ``fastplotlib`` interactively ----------------------------------- diff --git a/examples/desktop/guis/README.rst b/examples/desktop/guis/README.rst new file mode 100644 index 000000000..9cbf4d424 --- /dev/null +++ b/examples/desktop/guis/README.rst @@ -0,0 +1,2 @@ +ImGUI for within-canvas GUIs +============================ diff --git a/examples/desktop/guis/image_widget_imgui.py b/examples/desktop/guis/image_widget_imgui.py new file mode 100644 index 000000000..38a5c72e1 --- /dev/null +++ b/examples/desktop/guis/image_widget_imgui.py @@ -0,0 +1,82 @@ +""" +ImGUI with ImageWidget +====================== + +Example showing how to write a custom GUI with imgui and use it with ImageWidget +""" + +# test_example = true +# sphinx_gallery_pygfx_docs = 'screenshot' + +# some simple image processing functions +from scipy.ndimage import gaussian_filter +import imageio.v3 as iio + +import fastplotlib as fpl + +# subclass from EdgeWindow to make a custom ImGUI Window to place inside the figure! +from fastplotlib.ui import EdgeWindow +from imgui_bundle import imgui + +a = iio.imread("imageio:camera.png") +iw = fpl.ImageWidget(data=a, cmap="viridis", figure_kwargs={"size": (700, 560)}) +iw.show() + + +# GUI for some basic image processing +class ImageProcessingWindow(EdgeWindow): + def __init__(self, figure, size, location, title): + super().__init__(figure=figure, size=size, location=location, title=title) + + self.sigma = 0.0 + self.order_x, self.order_y = 0, 0 + + def update(self): + # implement the GUI within the update function + # you do not need to call imgui.new_frame(), this is handled by Figure + + # window creation is handled by the base EdgeWindow.draw_window() + # if you want to customize the imgui window, you can override EdgeWindow.draw_window() + + something_changed = False + + # slider for gaussian filter sigma value + changed, value = imgui.slider_float(label="sigma", v=self.sigma, v_min=0.0, v_max=20.0) + if changed: + self.sigma = value + something_changed = True + + # int entries for gaussian filter order + for axis in ["x", "y"]: + changed, value = imgui.input_int(f"order {axis}", v=getattr(self, f"order_{axis}")) + if changed: + if value < 0: + value = 0 + setattr(self, f"order_{axis}", value) + something_changed = True + + if something_changed: + self.process_image() + + # imgui.end() is handled by EdgeWindow.draw_window() + + # do not call imgui.end_frame(), this is handled by Figure + + def process_image(self): + processed = gaussian_filter(a, sigma=self.sigma, order=(self.order_y, self.order_x)) + iw.set_data(processed) + + +gui = ImageProcessingWindow(iw.figure, size=200, location="right", title="Gaussian Filter") + + +iw.figure.add_gui(gui) + +figure = iw.figure + + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.run() diff --git a/examples/desktop/guis/imgui_basic.py b/examples/desktop/guis/imgui_basic.py new file mode 100644 index 000000000..456375950 --- /dev/null +++ b/examples/desktop/guis/imgui_basic.py @@ -0,0 +1,123 @@ +""" +ImGUI Basics +============ + +Basic examples demonstrating how to use imgui in fastplotlib. + +See the imgui docs for extensive examples on how to create all UI elements: https://pyimgui.readthedocs.io/en/latest/reference/imgui.core.html#imgui.core.begin_combo +""" + +# test_example = true +# sphinx_gallery_pygfx_docs = 'screenshot' + +import numpy as np +import fastplotlib as fpl + +# subclass from EdgeWindow to make a custom ImGUI Window to place inside the figure! +from fastplotlib.ui import EdgeWindow +from imgui_bundle import imgui + +# make some initial data +np.random.seed(0) + +xs = np.linspace(0, np.pi * 10, 100) +ys = np.sin(xs) + np.random.normal(scale=0.0, size=100) +data = np.column_stack([xs, ys]) + + +# make a figure +figure = fpl.Figure(size=(700, 560)) + +# make some scatter points at every 10th point +figure[0, 0].add_scatter(data[::10], colors="cyan", sizes=15, name="sine-scatter", uniform_color=True) + +# place a line above the scatter +figure[0, 0].add_line(data, thickness=3, colors="r", name="sine-wave", uniform_color=True) + + +class ImguiExample(EdgeWindow): + def __init__(self, figure, size, location, title): + super().__init__(figure=figure, size=size, location=location, title=title) + # this UI will modify the line + self._line = self._figure[0, 0]["sine-wave"] + + # set the default values + # wave amplitude + self._amplitude = 1 + + # sigma for gaussian noise + self._sigma = 0.0 + + def update(self): + # the UI will be used to modify the line + self._line = figure[0, 0]["sine-wave"] + + # get the current line RGB values + rgb_color = self._line.colors[:-1] + # make color picker + changed_color, rgb = imgui.color_picker3("color", col=rgb_color) + + # get current line color alpha value + alpha = self._line.colors[-1] + # make float slider + changed_alpha, new_alpha = imgui.slider_float("alpha", v=alpha, v_min=0.0, v_max=1.0) + + # if RGB or alpha changed + if changed_color | changed_alpha: + # set new color along with alpha + self._line.colors = [*rgb, new_alpha] + + # example of a slider, you can also use input_float + changed, amplitude = imgui.slider_float("amplitude", v=self._amplitude, v_max=10, v_min=0.1) + if changed: + # set y values + self._amplitude = amplitude + self._set_data() + + # slider for thickness + changed, thickness = imgui.slider_float("thickness", v=self._line.thickness, v_max=50.0, v_min=2.0) + if changed: + self._line.thickness = thickness + + # slider for gaussian noise + changed, sigma = imgui.slider_float("noise-sigma", v=self._sigma, v_max=1.0, v_min=0.0) + if changed: + self._sigma = sigma + self._set_data() + + # reset button + if imgui.button("reset"): + # reset line properties + self._line.colors = (1, 0, 0, 1) + self._line.thickness = 3 + + # reset the data params + self._amplitude = 1.0 + self._sigma = 0.0 + + # reset the data values for the line + self._set_data() + + def _set_data(self): + self._line.data[:, 1] = (np.sin(xs) * self._amplitude) + np.random.normal(scale=self._sigma, size=100) + + +# make GUI instance +gui = ImguiExample( + figure, # the figure this GUI instance should live inside + size=275, # width or height of the GUI window within the figure + location="right", # the edge to place this window at + title="Imgui Window", # window title +) + +# add it to the figure +figure.add_gui(gui) + +figure.show() + + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.run() diff --git a/examples/desktop/heatmap/heatmap.py b/examples/desktop/heatmap/heatmap.py index 008686464..11d5559c4 100644 --- a/examples/desktop/heatmap/heatmap.py +++ b/examples/desktop/heatmap/heatmap.py @@ -1,7 +1,8 @@ """ -Simple Heatmap -============== -Example showing how to plot a heatmap +Heatmap or large arrays +======================= +Example showing how ImageGraphics can be useful for viewing large arrays, these can be in the order of 10^4 x 10^4. +The performance and limitations will depend on your hardware. """ # test_example = true @@ -10,13 +11,14 @@ import fastplotlib as fpl import numpy as np + figure = fpl.Figure(size=(700, 560)) -xs = np.linspace(0, 1_000, 9_000, dtype=np.float32) +xs = np.linspace(0, 2300, 2300, dtype=np.float16) sine = np.sin(np.sqrt(xs)) -data = np.vstack([sine * i for i in range(15_000)]) +data = np.vstack([sine * i for i in range(2_300)]) # plot the image data img = figure[0, 0].add_image(data=data, name="heatmap") @@ -24,7 +26,6 @@ figure.show() - # NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": diff --git a/examples/desktop/heatmap/heatmap_cmap.py b/examples/desktop/heatmap/heatmap_cmap.py deleted file mode 100644 index 8791741a7..000000000 --- a/examples/desktop/heatmap/heatmap_cmap.py +++ /dev/null @@ -1,33 +0,0 @@ -""" -Heatmap change cmap -=================== -Change the cmap of a heatmap -""" - - -# test_example = false -# sphinx_gallery_pygfx_docs = 'hidden' - -import fastplotlib as fpl -import numpy as np - -figure = fpl.Figure(size=(700, 560)) - -xs = np.linspace(0, 1_000, 10_000, dtype=np.float32) - -sine = np.sin(np.sqrt(xs)) - -data = np.vstack([sine * i for i in range(20_000)]) - -# plot the image data -img = figure[0, 0].add_image(data=data, name="heatmap") - -figure.show() - -img.cmap = "viridis" - -# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively -# please see our docs for using fastplotlib interactively in ipython and jupyter -if __name__ == "__main__": - print(__doc__) - fpl.run() diff --git a/examples/desktop/heatmap/heatmap_data.py b/examples/desktop/heatmap/heatmap_data.py deleted file mode 100644 index f524f5476..000000000 --- a/examples/desktop/heatmap/heatmap_data.py +++ /dev/null @@ -1,35 +0,0 @@ -""" -Heatmap change data -=================== -Change the data of a heatmap -""" - -# test_example = false -# sphinx_gallery_pygfx_docs = 'hidden' - -import fastplotlib as fpl -import numpy as np - -figure = fpl.Figure(size=(700, 560)) - -xs = np.linspace(0, 1_000, 9_000, dtype=np.float32) - -sine = np.sin(np.sqrt(xs)) - -data = np.vstack([sine * i for i in range(9_000)]) - -# plot the image data -img = figure[0, 0].add_image(data=data, name="heatmap") - -figure.show() - -cosine = np.cos(np.sqrt(xs)[:3000]) - -# change first 2,000 rows and 3,000 columns -img.data[:2_000, :3_000] = np.vstack([cosine * i * 4 for i in range(2_000)]) - -# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively -# please see our docs for using fastplotlib interactively in ipython and jupyter -if __name__ == "__main__": - print(__doc__) - fpl.run() diff --git a/examples/desktop/heatmap/heatmap_square.py b/examples/desktop/heatmap/heatmap_square.py deleted file mode 100644 index aee4f7d44..000000000 --- a/examples/desktop/heatmap/heatmap_square.py +++ /dev/null @@ -1,31 +0,0 @@ -""" -Square Heatmap -============== -square heatmap test -""" - -# test_example = false -# sphinx_gallery_pygfx_docs = 'hidden' - -import fastplotlib as fpl -import numpy as np - - -figure = fpl.Figure(size=(700, 560)) - -xs = np.linspace(0, 1_000, 20_000, dtype=np.float32) - -sine = np.sin(np.sqrt(xs)) - -data = np.vstack([sine * i for i in range(20_000)]) - -# plot the image data -img = figure[0, 0].add_image(data=data, name="heatmap") - -del data # data no longer needed after given to graphic -figure.show() - - -if __name__ == "__main__": - print(__doc__) - fpl.run() diff --git a/examples/desktop/heatmap/heatmap_vmin_vmax.py b/examples/desktop/heatmap/heatmap_vmin_vmax.py deleted file mode 100644 index e7f9c758b..000000000 --- a/examples/desktop/heatmap/heatmap_vmin_vmax.py +++ /dev/null @@ -1,33 +0,0 @@ -""" -Heatmap change vmin vmax -======================== -Change the vmin vmax of a heatmap -""" - -# test_example = false -# sphinx_gallery_pygfx_docs = 'hidden' - -import fastplotlib as fpl -import numpy as np - -figure = fpl.Figure(size=(700, 560)) - -xs = np.linspace(0, 1_000, 10_000, dtype=np.float32) - -sine = np.sin(np.sqrt(xs)) - -data = np.vstack([sine * i for i in range(20_000)]) - -# plot the image data -img = figure[0, 0].add_image(data=data, name="heatmap") - -figure.show() - -img.vmin = -5_000 -img.vmax = 10_000 - -# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively -# please see our docs for using fastplotlib interactively in ipython and jupyter -if __name__ == "__main__": - print(__doc__) - fpl.run() diff --git a/examples/desktop/heatmap/heatmap_wide.py b/examples/desktop/heatmap/heatmap_wide.py deleted file mode 100644 index 6bf3ff72d..000000000 --- a/examples/desktop/heatmap/heatmap_wide.py +++ /dev/null @@ -1,30 +0,0 @@ -""" -Wide Heatmap -============ -Wide example -""" - -# test_example = false -# sphinx_gallery_pygfx_docs = 'hidden' - -import fastplotlib as fpl -import numpy as np - - -figure = fpl.Figure(size=(700, 560)) - -xs = np.linspace(0, 1_000, 20_000, dtype=np.float32) - -sine = np.sin(np.sqrt(xs)) - -data = np.vstack([sine * i for i in range(10_000)]) - -# plot the image data -img = figure[0, 0].add_image(data=data, name="heatmap") - -figure.show() - - -if __name__ == "__main__": - print(__doc__) - fpl.run() diff --git a/examples/desktop/image_widget/README.rst b/examples/desktop/image_widget/README.rst new file mode 100644 index 000000000..f445f7390 --- /dev/null +++ b/examples/desktop/image_widget/README.rst @@ -0,0 +1,2 @@ +ImageWidget Examples +==================== diff --git a/examples/desktop/image/image_widget.py b/examples/desktop/image_widget/image_widget.py similarity index 57% rename from examples/desktop/image/image_widget.py rename to examples/desktop/image_widget/image_widget.py index 131e02bd7..78b54b8ef 100644 --- a/examples/desktop/image/image_widget.py +++ b/examples/desktop/image_widget/image_widget.py @@ -3,9 +3,12 @@ ============ Example showing the image widget in action. -When run in a notebook, or with the Qt GUI backend, sliders are also shown. + +Every image in an `ImageWidget` is associated with an interactive Histogram LUT tool and colorbar. Right-click the +colorbar to pick colormaps. """ +# test_example = true # sphinx_gallery_pygfx_docs = 'screenshot' import fastplotlib as fpl @@ -15,6 +18,13 @@ iw = fpl.ImageWidget(data=a, cmap="viridis", figure_kwargs={"size": (700, 560)}) iw.show() +# Access ImageGraphics managed by the image widget +iw.figure[0, 0]["image_widget_managed"].data[:50, :50] = 0 +iw.figure[0, 0]["image_widget_managed"].cmap = "gnuplot2" + +# another way to access the image widget managed ImageGraphics +iw.managed_graphics[0].data[450:, 450:] = 255 + figure = iw.figure # NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively diff --git a/examples/desktop/image_widget/image_widget_grid.py b/examples/desktop/image_widget/image_widget_grid.py new file mode 100644 index 000000000..48b31caa7 --- /dev/null +++ b/examples/desktop/image_widget/image_widget_grid.py @@ -0,0 +1,41 @@ +""" +Image widget grid +================= + +Example showing how to view multiple images in an ImageWidget +""" + +import fastplotlib as fpl +import imageio.v3 as iio + +# test_example = true +# sphinx_gallery_pygfx_docs = 'screenshot' + +img1 = iio.imread("imageio:camera.png") +img2 = iio.imread("imageio:astronaut.png") +img3 = iio.imread("imageio:chelsea.png") +img4 = iio.imread("imageio:wikkie.png") + +iw = fpl.ImageWidget( + data=[img1, img2, img3, img4], + rgb=[False, True, True, True], # mix of grayscale and RGB images + names=["cameraman", "astronaut", "chelsea", "Almar's cat"], + # ImageWidget will sync controllers by default + # by setting `controller_ids=None` we can have independent controllers for each subplot + # this is useful when the images have different dimensions + figure_kwargs={"size": (700, 560), "controller_ids": None}, +) +iw.show() + +figure = iw.figure + +for subplot in figure: + # sometimes the toolbar adds clutter + subplot.toolbar = False + + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.run() diff --git a/examples/desktop/image_widget/image_widget_single_video.py b/examples/desktop/image_widget/image_widget_single_video.py new file mode 100644 index 000000000..30073a935 --- /dev/null +++ b/examples/desktop/image_widget/image_widget_single_video.py @@ -0,0 +1,47 @@ +""" +Image widget Video +================== + +Example showing how to scroll through one or more videos using the ImageWidget +""" + +# test_example = true +# sphinx_gallery_pygfx_docs = 'animate 6s 20fps' + +import fastplotlib as fpl +import imageio.v3 as iio +import numpy as np + + +movie = iio.imread("imageio:cockatoo.mp4") + +# Ignore and do not use the next 2 lines +# for the purposes of docs gallery generation we subsample and only use 15 frames +movie_sub = movie[:15, ::12, ::12].copy() +del movie + +iw = fpl.ImageWidget(movie_sub, rgb=[True], figure_kwargs={"size": (700, 560)}) + +# ImageWidget supports setting window functions the `time` "t" or `volume` "z" dimension +# These can also be given as kwargs to `ImageWidget` during instantiation +# to set a window function, give a dict in the form of {dim: (func, window_size)} +iw.window_funcs = {"t": (np.mean, 13)} + +# change the window size +iw.window_funcs["t"].window_size = 33 + +# change the function +iw.window_funcs["t"].func = np.max + +# or reset it +iw.window_funcs = None + +iw.show() + +figure = iw.figure + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.run() diff --git a/examples/desktop/image_widget/image_widget_videos.py b/examples/desktop/image_widget/image_widget_videos.py new file mode 100644 index 000000000..6e5c35c50 --- /dev/null +++ b/examples/desktop/image_widget/image_widget_videos.py @@ -0,0 +1,43 @@ +""" +Image widget videos side by side +================================ + +Example showing how to scroll through one or more videos using the ImageWidget +""" + +# test_example = true +# sphinx_gallery_pygfx_docs = 'animate 6s 20fps' + +import fastplotlib as fpl +import imageio.v3 as iio +import numpy as np + + +# load the standard cockatoo video +cockatoo = iio.imread("imageio:cockatoo.mp4") + +# Ignore and do not use the next 2 lines +# for the purposes of docs gallery generation we subsample and only use 15 frames +cockatoo_sub = cockatoo[:15, ::12, ::12].copy() +del cockatoo + +# make a random grayscale video, shape is [t, rows, cols] +np.random.seed(0) +random_data = np.random.rand(*cockatoo_sub.shape[:-1]) + +iw = fpl.ImageWidget( + [random_data, cockatoo_sub], + rgb=[False, True], + figure_shape=(2, 1), # 2 rows, 1 column + figure_kwargs={"size": (700, 560)} +) + +iw.show() + +figure = iw.figure + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.run() diff --git a/examples/desktop/misc/lorenz_animation.py b/examples/desktop/misc/lorenz_animation.py index cf7a77b38..af577d5a2 100644 --- a/examples/desktop/misc/lorenz_animation.py +++ b/examples/desktop/misc/lorenz_animation.py @@ -51,7 +51,8 @@ def lorenz(xyz, *, s=10, r=28, b=2.667): figure = fpl.Figure( cameras="3d", - controller_types="fly" + controller_types="fly", + size=(700, 560) ) lorenz_line = figure[0, 0].add_line_collection(data=lorenz_data, thickness=.1, cmap="tab10") @@ -59,14 +60,14 @@ def lorenz(xyz, *, s=10, r=28, b=2.667): scatter_markers = list() for graphic in lorenz_line: - marker = figure[0, 0].add_scatter(graphic.data.value[0], sizes=8, colors=graphic.colors[0]) + marker = figure[0, 0].add_scatter(graphic.data.value[0], sizes=16, colors=graphic.colors[0]) scatter_markers.append(marker) # initialize time time = 0 -def animate(supblot): +def animate(subplot): global time time += 2 @@ -83,7 +84,7 @@ def animate(supblot): figure.show() # set initial camera position to make animation in gallery render better -figure[0, 0].camera.world.z = 75 +figure[0, 0].camera.world.z = 80 # NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively # please see our docs for using fastplotlib interactively in ipython and jupyter diff --git a/examples/desktop/screenshots/gridplot.png b/examples/desktop/screenshots/gridplot.png index 99ba70155..1a222affd 100644 --- a/examples/desktop/screenshots/gridplot.png +++ b/examples/desktop/screenshots/gridplot.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a0da6067ecd930fb0add52124dfd97f7d73b27ab7696df681c75e333c749975a -size 328971 +oid sha256:8de769538bb435b71b33e038998b2bafa340c635211c0dfc388c7a5bf55fd36d +size 286794 diff --git a/examples/desktop/screenshots/gridplot_non_square.png b/examples/desktop/screenshots/gridplot_non_square.png index 6db1c3f2a..45d71abb2 100644 --- a/examples/desktop/screenshots/gridplot_non_square.png +++ b/examples/desktop/screenshots/gridplot_non_square.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2763431048efa1642a276bc3e659ed93a2f787ff6db700bcd29acc619d542f3f -size 236206 +oid sha256:92f55da7e2912a68e69e212b31df760f27e72253ec234fe1dd5b5463b60061b3 +size 212647 diff --git a/examples/desktop/screenshots/heatmap.png b/examples/desktop/screenshots/heatmap.png index a8f91765e..a63eb5ec8 100644 --- a/examples/desktop/screenshots/heatmap.png +++ b/examples/desktop/screenshots/heatmap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d40c5e47f686dc498f003684efeefc16e6962d6ce1e2edc4c2cd8537b3ff3387 -size 82267 +oid sha256:1f2f0699e01eb12c44a2dbefd1d8371b86b3b3456b28cb5f1850aed44c13f412 +size 94505 diff --git a/examples/desktop/screenshots/image_cmap.png b/examples/desktop/screenshots/image_cmap.png index 837d6765f..6f7081b03 100644 --- a/examples/desktop/screenshots/image_cmap.png +++ b/examples/desktop/screenshots/image_cmap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:95ed35b1ab7d5e56ff81e883d5c56419ddede3481f1a0c77f5af01dba83d03ea -size 236774 +oid sha256:e1482ce72511bc4f815825c29fabac5dd0f2586ac4c827a220a5cecb1162be4d +size 210019 diff --git a/examples/desktop/screenshots/image_rgb.png b/examples/desktop/screenshots/image_rgb.png index 2ca946c15..88beb7df3 100644 --- a/examples/desktop/screenshots/image_rgb.png +++ b/examples/desktop/screenshots/image_rgb.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:86e421deb8e013f25737b9a752409890ba14f794a1a01fbed728d474490292bb -size 269316 +oid sha256:8210ad8d1755f7819814bdaaf236738cdf1e9a0c4f77120aca4968fcd8aa8a7a +size 239431 diff --git a/examples/desktop/screenshots/image_rgbvminvmax.png b/examples/desktop/screenshots/image_rgbvminvmax.png index c31263344..f3ef59d84 100644 --- a/examples/desktop/screenshots/image_rgbvminvmax.png +++ b/examples/desktop/screenshots/image_rgbvminvmax.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fc5983f07d840320bf6866896d221845f59eecedbc6d89a7a0bc5dd1f6472c7b -size 49999 +oid sha256:8ebbcc4a2e83e9733eb438fe2341f77c86579421f3fa96b6a49e94073c0ffd32 +size 48270 diff --git a/examples/desktop/screenshots/image_simple.png b/examples/desktop/screenshots/image_simple.png index 194e5afe4..0c7e011f4 100644 --- a/examples/desktop/screenshots/image_simple.png +++ b/examples/desktop/screenshots/image_simple.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ec0770ff5671a9f83f43f8ece18e45b74137244ff578b8035eace3fd98291595 -size 237699 +oid sha256:44bc2d1fd97921fef0be45424f21513d5d978b807db8cf148dfc59c07f6e292f +size 211333 diff --git a/examples/desktop/screenshots/image_small.png b/examples/desktop/screenshots/image_small.png index 5ed8f615d..41a4a240e 100644 --- a/examples/desktop/screenshots/image_small.png +++ b/examples/desktop/screenshots/image_small.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3818b137bcfce829ea6a8670ca52a20122b2489f536ca5ff38e0ed6288043113 -size 12824 +oid sha256:079ee6254dc995cc5fc8c20ff1c00cb0899f21ba2d5d1a4dc0d020c3a71902c4 +size 13022 diff --git a/examples/desktop/screenshots/image_vminvmax.png b/examples/desktop/screenshots/image_vminvmax.png index c31263344..f3ef59d84 100644 --- a/examples/desktop/screenshots/image_vminvmax.png +++ b/examples/desktop/screenshots/image_vminvmax.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fc5983f07d840320bf6866896d221845f59eecedbc6d89a7a0bc5dd1f6472c7b -size 49999 +oid sha256:8ebbcc4a2e83e9733eb438fe2341f77c86579421f3fa96b6a49e94073c0ffd32 +size 48270 diff --git a/examples/desktop/screenshots/image_widget.png b/examples/desktop/screenshots/image_widget.png new file mode 100644 index 000000000..af248dd3e --- /dev/null +++ b/examples/desktop/screenshots/image_widget.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e2ae1938c5e7b742fb2dac0336877028f6ece26cd80e84f309195a55601025cb +size 197495 diff --git a/examples/desktop/screenshots/image_widget_grid.png b/examples/desktop/screenshots/image_widget_grid.png new file mode 100644 index 000000000..e0f0ff5c8 --- /dev/null +++ b/examples/desktop/screenshots/image_widget_grid.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eeb5b86e7c15dfe2e71267453426930200223026f72156f34ff1ccc2f9389b6e +size 253769 diff --git a/examples/desktop/screenshots/image_widget_imgui.png b/examples/desktop/screenshots/image_widget_imgui.png new file mode 100644 index 000000000..135a0d4c4 --- /dev/null +++ b/examples/desktop/screenshots/image_widget_imgui.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7e2cd0e3892377e6e2d552199391fc64aac6a02413168a5b4c5c4848f3390dec +size 166265 diff --git a/examples/desktop/screenshots/image_widget_single_video.png b/examples/desktop/screenshots/image_widget_single_video.png new file mode 100644 index 000000000..aa829125c --- /dev/null +++ b/examples/desktop/screenshots/image_widget_single_video.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:11ffeceb298c5b5d429da822e1764c11d862bf85630ce3390475c766366bceab +size 91299 diff --git a/examples/desktop/screenshots/image_widget_videos.png b/examples/desktop/screenshots/image_widget_videos.png new file mode 100644 index 000000000..70ad686c6 --- /dev/null +++ b/examples/desktop/screenshots/image_widget_videos.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2c17df678e54e9cddbd42869cfe7a32069b7ffa70e6227c95c333537e1efede6 +size 170218 diff --git a/examples/desktop/screenshots/imgui_basic.png b/examples/desktop/screenshots/imgui_basic.png new file mode 100644 index 000000000..27288e38f --- /dev/null +++ b/examples/desktop/screenshots/imgui_basic.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3391b7cf02fc7bd2c73dc57214b21ceaca9a1513556b3a4725639f21588824e4 +size 36261 diff --git a/examples/desktop/screenshots/line.png b/examples/desktop/screenshots/line.png index 3cf15db2d..492ea2ada 100644 --- a/examples/desktop/screenshots/line.png +++ b/examples/desktop/screenshots/line.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e0ea3004cc871f54d1f12f6e5a39afbda568748ca907468a0533268949c67916 -size 173435 +oid sha256:1458d472362f8d5bcef599fd64f931997a246f9e7649c80cc95f465cbd858850 +size 170243 diff --git a/examples/desktop/screenshots/line_cmap.png b/examples/desktop/screenshots/line_cmap.png index 6ec5a4998..10779fcd5 100644 --- a/examples/desktop/screenshots/line_cmap.png +++ b/examples/desktop/screenshots/line_cmap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cbf54efd9999593043c48a53f189c675ef6544a962c44297ce76df4fbe75ad42 -size 47804 +oid sha256:66e64835f824d80dd7606d90530517dbc320bcc11a68393ab92c08fef3d23f5a +size 48828 diff --git a/examples/desktop/screenshots/line_collection.png b/examples/desktop/screenshots/line_collection.png index ffe8cc96e..d9124daf1 100644 --- a/examples/desktop/screenshots/line_collection.png +++ b/examples/desktop/screenshots/line_collection.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b373c63989b4d3d3c9b5ea1607ef1602fa7d45753cdc0895a6e6d1d4a2c5420b -size 106504 +oid sha256:50920f4bc21bb5beffe317777a20d8d09f90f3631a14df51c219814d3507c602 +size 100758 diff --git a/examples/desktop/screenshots/line_collection_cmap_values.png b/examples/desktop/screenshots/line_collection_cmap_values.png index 66d36dec3..e04289699 100644 --- a/examples/desktop/screenshots/line_collection_cmap_values.png +++ b/examples/desktop/screenshots/line_collection_cmap_values.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dff530c128132f26aded7c2ad9e202cc98e7486fbad84146a9055b6514c99453 -size 67561 +oid sha256:850e3deb2220d44f01e6366ee7cffb83085cad933a137b9838ce8c2231e7786a +size 64152 diff --git a/examples/desktop/screenshots/line_collection_cmap_values_qualitative.png b/examples/desktop/screenshots/line_collection_cmap_values_qualitative.png index b144dbdcb..710cee119 100644 --- a/examples/desktop/screenshots/line_collection_cmap_values_qualitative.png +++ b/examples/desktop/screenshots/line_collection_cmap_values_qualitative.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ce6e25567214539b296248a4dc665552f47687cda03d412f715db7f72138c341 -size 69992 +oid sha256:ba5fefc8e1043fe0ebd926a6b8e6ab19e724205a4c13e4d7740122cfe464e38b +size 67017 diff --git a/examples/desktop/screenshots/line_collection_colors.png b/examples/desktop/screenshots/line_collection_colors.png index 90948c126..6c1d05f04 100644 --- a/examples/desktop/screenshots/line_collection_colors.png +++ b/examples/desktop/screenshots/line_collection_colors.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9aeb3ef27fd7a393b4884749e7988e8cde3906c9f19b573e51bd78bf31fc7a45 -size 60514 +oid sha256:17d48f07310090b835e5cd2e6fa9c178db9af8954f4b0a9d52d21997ec229abd +size 57778 diff --git a/examples/desktop/screenshots/line_collection_slicing.png b/examples/desktop/screenshots/line_collection_slicing.png index 26933c5cc..abb63760f 100644 --- a/examples/desktop/screenshots/line_collection_slicing.png +++ b/examples/desktop/screenshots/line_collection_slicing.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:beb5193965530c490324edeb253ed429237e44289c5239079743a71d2aece797 -size 132171 +oid sha256:ed0d4fdb729409d07ec9ec9e05d915a04ebb237087d266591e7f46b0838e05b3 +size 130192 diff --git a/examples/desktop/screenshots/line_colorslice.png b/examples/desktop/screenshots/line_colorslice.png index 34ff56c4f..1f100d89e 100644 --- a/examples/desktop/screenshots/line_colorslice.png +++ b/examples/desktop/screenshots/line_colorslice.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c8afbeb5a79192eb1805c7c8478b26f6aabc534f3ac58fc7190f108ebb8640fe -size 56462 +oid sha256:1b2c5562f4150ec69029a4a139469b0a2524a14078b78055df40d9b487946ce5 +size 57037 diff --git a/examples/desktop/screenshots/line_dataslice.png b/examples/desktop/screenshots/line_dataslice.png index c135997bb..b2f963195 100644 --- a/examples/desktop/screenshots/line_dataslice.png +++ b/examples/desktop/screenshots/line_dataslice.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e6c5c4ef3aaeca5597c11e5db3764599c8c41b191c692db5fda54f525d8079da -size 68033 +oid sha256:c31a12afa3e66c442e370e6157ad9a5aad225b21f0f95fb6a115066b1b4f2e73 +size 68811 diff --git a/examples/desktop/screenshots/line_stack.png b/examples/desktop/screenshots/line_stack.png index ea5a3a330..786f434be 100644 --- a/examples/desktop/screenshots/line_stack.png +++ b/examples/desktop/screenshots/line_stack.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cdb26c1460583f8f605ffe6751c926c0e84463b10d68343169660593b82a9078 -size 130495 +oid sha256:fcfa7c49d465ff9cfe472ee885bcc9d9a44106b82adfc151544847b95035d760 +size 121640 diff --git a/examples/desktop/screenshots/scatter_cmap_iris.png b/examples/desktop/screenshots/scatter_cmap_iris.png index 96acbec6c..a887d1f99 100644 --- a/examples/desktop/screenshots/scatter_cmap_iris.png +++ b/examples/desktop/screenshots/scatter_cmap_iris.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:79f7d22b575c3a68dfdcd4bf806f79f1896a784ecbb6a2d3ba01da5731fa78dd -size 59731 +oid sha256:6d6bfba80eb737099040eebce9b70e1b261720f26cc895ec4b81ca21af60471c +size 60550 diff --git a/examples/desktop/screenshots/scatter_colorslice_iris.png b/examples/desktop/screenshots/scatter_colorslice_iris.png index 73fcddebf..e260df642 100644 --- a/examples/desktop/screenshots/scatter_colorslice_iris.png +++ b/examples/desktop/screenshots/scatter_colorslice_iris.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3c778cf9c51c9636d4f4ff13e4a1c841795a4dba327eb7118de2a0fb60c7e3f3 -size 35810 +oid sha256:febd4aa7240eea70b2759337cf98be31cacc1b147859bf628e929ead0153ef9c +size 36791 diff --git a/examples/desktop/screenshots/scatter_dataslice_iris.png b/examples/desktop/screenshots/scatter_dataslice_iris.png index 32f797c67..e5f05bb74 100644 --- a/examples/desktop/screenshots/scatter_dataslice_iris.png +++ b/examples/desktop/screenshots/scatter_dataslice_iris.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:444f0bd81459a4977df2eb9aa5645c0f7745fce97baa0c9e39c254bd32cdb1e6 -size 38351 +oid sha256:6cfbc717281c15c6d1d8fe2989770bc9c46f42052c897c2270294ad1b4b40d66 +size 39296 diff --git a/examples/desktop/screenshots/scatter_iris.png b/examples/desktop/screenshots/scatter_iris.png index dc53d97b0..9c452d448 100644 --- a/examples/desktop/screenshots/scatter_iris.png +++ b/examples/desktop/screenshots/scatter_iris.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:153db7a803709978a1a997d7c94db37ebc0504ec9a7eebce80977d4c90d48f61 -size 37365 +oid sha256:98eab41312eb42cbffdf8add0651b55e63b5c2fb5f4523e32dc51ed28a1be369 +size 38452 diff --git a/examples/desktop/screenshots/scatter_size.png b/examples/desktop/screenshots/scatter_size.png index 74c1b6e56..f2f036ea4 100644 --- a/examples/desktop/screenshots/scatter_size.png +++ b/examples/desktop/screenshots/scatter_size.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:381877c06882f40a8b46bbe07e1e1ca41a74ff9cda84544cca4ee92a4b522cda -size 62476 +oid sha256:e3522468f99c030cb27c225f009ecb4c7aafbd97cfc743cf1d07fb8d7ff8e0d4 +size 71336 diff --git a/examples/desktop/selectors/README.rst b/examples/desktop/selectors/README.rst index 0f7e412a7..e0376d728 100644 --- a/examples/desktop/selectors/README.rst +++ b/examples/desktop/selectors/README.rst @@ -1,2 +1,2 @@ Selection Tools -=============== \ No newline at end of file +=============== diff --git a/examples/desktop/selectors/linear_selector.py b/examples/desktop/selectors/linear_selector.py index b224c197f..d724ccf5d 100644 --- a/examples/desktop/selectors/linear_selector.py +++ b/examples/desktop/selectors/linear_selector.py @@ -2,7 +2,7 @@ Linear Selectors ================ -Example showing how to use a `LinearSelector` with lines, line collections, and images +Example showing how to use a `LinearSelector` with lines and line collections. """ # test_example = false @@ -11,6 +11,7 @@ import fastplotlib as fpl import numpy as np + # create some data xs = np.linspace(0, 10 * np.pi, 100) sine = np.column_stack([xs, np.sin(xs)]) @@ -23,7 +24,7 @@ # create a figure figure = fpl.Figure( - shape=(2, 2), + shape=(1, 2), size=(700, 560) ) @@ -42,7 +43,7 @@ line_selector_text, offset=(0., 1.75, 0.), anchor="middle-left", - font_size=22, + font_size=32, face_color=line.colors[0], outline_color="w", outline_thickness=0.1, @@ -50,6 +51,8 @@ # add an event handler using a decorator, selectors are just like other graphics +# you can also use the .add_event_handler() method directly instead of a decorator +# see the line collection example below for a non-decorator example @line_selector.add_event_handler("selection") def line_selector_changed(ev): selection = ev.info["value"] @@ -79,13 +82,11 @@ def line_selector_changed(ev): line_stack_selector_text, offset=(0., 7.0, 0.), anchor="middle-left", - font_size=18, + font_size=24, face_color="w", ) -# add an event handler using a decorator -@line_stack_selector.add_event_handler("selection") def line_stack_selector_changed(ev): selection = ev.info["value"] @@ -101,57 +102,21 @@ def line_stack_selector_changed(ev): f"cosine y value: {line_stack[1].data[index, 1]:.2f}\n") -# create an image -image = figure[1, 0].add_image(image_data) - -# add a row selector -image_row_selector = image.add_linear_selector(axis="y") - -# add column selector -image_col_selector = image.add_linear_selector() - -# make a line to indicate row data -line_image_row = figure[1, 1].add_line(image.data[0]) - -# make a line to indicate column data -line_image_col_data = np.column_stack([image.data[:, 0], np.arange(100)]) -line_image_col = figure[1, 1].add_line(line_image_col_data) - - -# callbacks to change the line data in subplot [1, 1] -# to display selected row and selected column data -def image_row_selector_changed(ev): - ix = ev.get_selected_index() - new_data = image.data[ix] - # set y values of line - line_image_row.data[:, 1] = new_data - - -def image_col_selector_changed(ev): - ix = ev.get_selected_index() - new_data = image.data[:, ix] - # set x values of line - line_image_col.data[:, 0] = new_data - - -# add event handlers, you can also use a decorator -image_row_selector.add_event_handler(image_row_selector_changed, "selection") -image_col_selector.add_event_handler(image_col_selector_changed, "selection") - -figure.show(maintain_aspect=False) +# add an event handler, you can also use a decorator +line_stack_selector.add_event_handler(line_stack_selector_changed, "selection") # some axes and camera zoom settings for subplot in [figure[0, 0], figure[0, 1]]: + subplot.axes.grids.xy.visible = True subplot.axes.auto_grid = False subplot.axes.grids.xy.major_step = (np.pi, 1) subplot.axes.grids.xy.minor_step = (0, 0) - subplot.camera.zoom = 0.6 -figure[1, 1].camera.zoom = 0.5 +figure.show(maintain_aspect=False) # NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() \ No newline at end of file + fpl.run() diff --git a/examples/desktop/selectors/linear_selector_image.py b/examples/desktop/selectors/linear_selector_image.py new file mode 100644 index 000000000..540c7645a --- /dev/null +++ b/examples/desktop/selectors/linear_selector_image.py @@ -0,0 +1,73 @@ +""" +Linear Selectors Image +====================== + +Example showing how to use a `LinearSelector` to selector rows or columns of an image. The subplot on the right +displays the data for the selector row and column. +""" + +# test_example = false +# sphinx_gallery_pygfx_docs = 'screenshot' + +import fastplotlib as fpl +from imageio import v3 as iio + +image_data = iio.imread("imageio:coins.png") + +figure = fpl.Figure( + (1, 3), + size=(700, 300), + names=[["image", "selected row data", "selected column data"]] +) + +# create an image +image = figure[0, 0].add_image(image_data) + +# add a row selector +image_row_selector = image.add_linear_selector(axis="y") + +# add column selector +image_col_selector = image.add_linear_selector() + +# make a line to indicate row data +line_image_row = figure[0, 1].add_line(image.data[0]) + +# make a line to indicate column data +line_image_col = figure[0, 2].add_line(image.data[:, 0]) + + +# callbacks to change the line data in subplot [0, 1] +# to display selected row and selected column data +def image_row_selector_changed(ev): + ix = ev.get_selected_index() + new_data = image.data[ix] + # set y values of line with the row data + line_image_row.data[:, 1] = new_data + + +def image_col_selector_changed(ev): + ix = ev.get_selected_index() + new_data = image.data[:, ix] + # set y values of line with the column data + line_image_col.data[:, 1] = new_data + + +# add event handlers, you can also use a decorator +image_row_selector.add_event_handler(image_row_selector_changed, "selection") +image_col_selector.add_event_handler(image_col_selector_changed, "selection") + +# programmatically set the selection or drag it with your mouse pointer +image_row_selector.selection = 200 +image_col_selector.selection = 180 + +figure.show() + +for subplot in figure: + subplot.camera.zoom = 0.5 + + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.run() diff --git a/examples/notebooks/heatmap.ipynb b/examples/notebooks/heatmap.ipynb index 7de3af2a0..08fd72501 100644 --- a/examples/notebooks/heatmap.ipynb +++ b/examples/notebooks/heatmap.ipynb @@ -10,12 +10,65 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "id": "49b2498d-56ae-4559-9282-c8484f3e6b6d", "metadata": { "tags": [] }, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING: The name 'ylorbr' is an alias for 'colorbrewer:YlOrBr', but is also available as: 'tol:YlOrBr'.\n", + "To silence this warning, use a fully namespaced name.\n", + "WARNING: The name 'rdbu' is an alias for 'colorbrewer:RdBu', but is also available as: 'vispy:RdBu'.\n", + "To silence this warning, use a fully namespaced name.\n", + "WARNING: The name 'rainbow' is an alias for 'gnuplot:rainbow', but is also available as: 'yorick:rainbow'.\n", + "To silence this warning, use a fully namespaced name.\n", + "WARNING: The name 'ice' is an alias for 'cmocean:ice', but is also available as: 'imagej:ice, vispy:ice'.\n", + "To silence this warning, use a fully namespaced name.\n", + "WARNING: The name 'fire' is an alias for 'imagej:fire', but is also available as: 'vispy:fire'.\n", + "To silence this warning, use a fully namespaced name.\n", + "WARNING: The name 'prgn' is an alias for 'colorbrewer:PRGn', but is also available as: 'tol:PRGn'.\n", + "To silence this warning, use a fully namespaced name.\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "7e56de31fa0c41fa8ac48dc276c157b9", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Image(value=b'\\x89PNG\\r\\n\\x1a\\n\\x00\\x00\\x00\\rIHDR\\x00\\x00\\x01,\\x00\\x00\\x007\\x08\\x06\\x00\\x00\\x00\\xb6\\x1bw\\x99\\x…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WGPU: enumerate_adapters() is deprecated, use enumerate_adapters_sync() instead.\n", + "Unable to find extension: VK_EXT_swapchain_colorspace\n", + "WGPU: request_adapter() is deprecated, use request_adapter_sync() instead.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Available devices:\n", + "✅ (default) | AMD RADV POLARIS10 (ACO) | DiscreteGPU | Vulkan | Mesa 20.3.5 (ACO)\n", + "❗ | llvmpipe (LLVM 11.0.1, 256 bits) | CPU | Vulkan | Mesa 20.3.5 (LLVM 11.0.1)\n", + "✅ | NVIDIA GeForce RTX 3080 | DiscreteGPU | Vulkan | 530.30.02\n", + "❗ | Radeon RX 570 Series (POLARIS10, DRM 3.40.0, 5.10.0-21-amd64, LLVM 11.0.1) | Unknown | OpenGL | 4.6 (Core Profile) Mesa 20.3.5\n" + ] + } + ], "source": [ "import numpy as np\n", "import fastplotlib as fpl" @@ -31,7 +84,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "id": "40718465-abf6-4727-8bd7-4acdd59843d5", "metadata": { "tags": [] @@ -47,24 +100,75 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "id": "02b072eb-2909-40c8-8739-950f07efbbc2", "metadata": { "tags": [] }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "(10000, 20000)" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "data.shape" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "id": "84deb31b-5464-4cce-a938-694371011021", "metadata": { "tags": [] }, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "956570245b55414e9b89bca0dc227535", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "RFBOutputContext()" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WGPU: request_device() is deprecated, use request_device_sync() instead.\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "ce013002bc6f4d1893c14a25bd3ae55b", + "version_major": 2, + "version_minor": 0 + }, + "text/html": [ + "

" + ], + "text/plain": [ + "JupyterWgpuCanvas()" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "fig = fpl.Figure()\n", "\n", diff --git a/examples/notebooks/image_widget_test.ipynb b/examples/notebooks/image_widget_test.ipynb index aaf41f3e3..2c05db6b0 100644 --- a/examples/notebooks/image_widget_test.ipynb +++ b/examples/notebooks/image_widget_test.ipynb @@ -153,26 +153,33 @@ "outputs": [], "source": [ "# testing cell ignore\n", - "assert iw_movie.sliders[\"t\"].max == gray_movie.shape[0] - 1\n", - "assert iw_movie.sliders[\"t\"].min == 0\n", + "assert iw_movie._dims_max_bounds[\"t\"] == gray_movie.shape[0]\n", + "\n", "plot_test(\"image-widget-movie-single-0\", iw_movie.figure)\n", - "iw_movie.sliders[\"t\"].value = 50\n", + "\n", + "iw_movie.current_index = {\"t\": 50}\n", "plot_test(\"image-widget-movie-single-50\", iw_movie.figure)\n", - "iw_movie.sliders[\"t\"].value = 279\n", + "\n", + "iw_movie.current_index = {\"t\": 279}\n", "plot_test(\"image-widget-movie-single-279\", iw_movie.figure)\n", - "iw_movie.sliders[\"t\"].value = 0\n", + "\n", + "iw_movie.current_index = {\"t\": 0}\n", "plot_test(\"image-widget-movie-single-0-reset\", iw_movie.figure)\n", - "iw_movie.sliders[\"t\"].value = 50\n", + "\n", + "iw_movie.current_index = {\"t\": 50}\n", "iw_movie.window_funcs = {\"t\": (np.mean, 13)}\n", - "# testing cell ignore\n", + "\n", "plot_test(\"image-widget-movie-single-50-window-mean-13\", iw_movie.figure)\n", "iw_movie.window_funcs[\"t\"].window_size = 33\n", + "\n", "plot_test(\"image-widget-movie-single-50-window-mean-33\", iw_movie.figure)\n", "iw_movie.window_funcs[\"t\"].func = np.max\n", + "\n", "plot_test(\"image-widget-movie-single-50-window-max-33\", iw_movie.figure)\n", "iw_movie.window_funcs = None\n", + "\n", "plot_test(\"image-widget-movie-single-50-window-reset\", iw_movie.figure)\n", - "iw_movie.sliders[\"t\"].value = 0" + "iw_movie.current_index = {\"t\": 0}" ] }, { @@ -305,24 +312,31 @@ "outputs": [], "source": [ "# testing cell ignore\n", - "assert iw_zfish.sliders[\"t\"].max == zfish_data.shape[0] - 1\n", - "assert iw_zfish.sliders[\"t\"].min == 0\n", + "assert iw_zfish._dims_max_bounds[\"t\"] == zfish_data.shape[0]\n", + "\n", "plot_test(\"image-widget-zfish-grid-init-mean-window-5\", iw_zfish.figure)\n", - "iw_zfish.sliders[\"t\"].value = 50\n", + "\n", + "iw_zfish.current_index = {\"t\": 50}\n", "plot_test(\"image-widget-zfish-grid-frame-50-mean-window-5\", iw_zfish.figure)\n", + "\n", "iw_zfish.window_funcs[\"t\"].window_size = 13\n", "plot_test(\"image-widget-zfish-grid-frame-50-mean-window-13\", iw_zfish.figure)\n", + "\n", "iw_zfish.window_funcs = None\n", "plot_test(\"image-widget-zfish-grid-frame-50\", iw_zfish.figure)\n", - "iw_zfish.sliders[\"t\"].value = 99\n", + "\n", + "iw_zfish.current_index = {\"t\": 99}\n", "plot_test(\"image-widget-zfish-grid-frame-99\", iw_zfish.figure)\n", - "iw_zfish.sliders[\"t\"].value = 50\n", + "\n", + "iw_zfish.current_index = {\"t\": 50}\n", "iw_zfish.window_funcs = {\"t\": (np.max, 13)}\n", "plot_test(\"image-widget-zfish-grid-frame-50-max-window-13\", iw_zfish.figure)\n", + "\n", "iw_zfish.window_funcs = None\n", "iw_zfish.frame_apply = lambda frame: gaussian_filter(frame.astype(np.float32), sigma=3)\n", "iw_zfish.reset_vmin_vmax()\n", "plot_test(\"image-widget-zfish-grid-frame-50-frame-apply-gaussian\", iw_zfish.figure)\n", + "\n", "iw_zfish.frame_apply = None\n", "iw_zfish.reset_vmin_vmax()\n", "plot_test(\"image-widget-zfish-grid-frame-50-frame-apply-reset\", iw_zfish.figure)" @@ -405,24 +419,31 @@ "outputs": [], "source": [ "# same tests as with the figure\n", - "assert iw_z.sliders[\"t\"].max == zfish_data.shape[0] - 1\n", - "assert iw_z.sliders[\"t\"].min == 0\n", + "assert iw_z._dims_max_bounds[\"t\"] == zfish_data.shape[0]\n", + "\n", "plot_test(\"image-widget-zfish-init-mean-window-5\", iw_z.figure)\n", - "iw_z.sliders[\"t\"].value = 50\n", + "\n", + "iw_z.current_index = {\"t\": 50}\n", "plot_test(\"image-widget-zfish-frame-50-mean-window-5\", iw_z.figure)\n", + "\n", "iw_z.window_funcs[\"t\"].window_size = 13\n", "plot_test(\"image-widget-zfish-frame-50-mean-window-13\", iw_z.figure)\n", + "\n", "iw_z.window_funcs = None\n", "plot_test(\"image-widget-zfish-frame-50\", iw_z.figure)\n", - "iw_z.sliders[\"t\"].value = 99\n", + "\n", + "iw_z.current_index = {\"t\": 99}\n", "plot_test(\"image-widget-zfish-frame-99\", iw_z.figure)\n", - "iw_z.sliders[\"t\"].value = 50\n", + "\n", + "iw_z.current_index = {\"t\": 50}\n", "iw_z.window_funcs = {\"t\": (np.max, 13)}\n", "plot_test(\"image-widget-zfish-frame-50-max-window-13\", iw_z.figure)\n", + "\n", "iw_z.window_funcs = None\n", "iw_z.frame_apply = lambda frame: gaussian_filter(frame.astype(np.float32), sigma=3)\n", "iw_z.reset_vmin_vmax()\n", "plot_test(\"image-widget-zfish-frame-50-frame-apply-gaussian\", iw_z.figure)\n", + "\n", "iw_z.frame_apply = None\n", "iw_z.reset_vmin_vmax()\n", "plot_test(\"image-widget-zfish-frame-50-frame-apply-reset\", iw_z.figure)" @@ -476,7 +497,7 @@ "metadata": {}, "outputs": [], "source": [ - "iw_mixed_shapes.sliders[\"t\"].value = 50\n", + "iw_mixed_shapes.current_index = {\"t\": 50}\n", "plot_test(\"image-widget-zfish-mixed-rgb-cockatoo-frame-50\", iw_mixed_shapes.figure)\n", "\n", "# Set the data, changing the first array and also the size of the \"T\" slider\n", @@ -485,7 +506,7 @@ "\n", "# Check how a window function might work on the RGB data\n", "iw_mixed_shapes.window_funcs = {\"t\": (np.mean, 4)}\n", - "iw_mixed_shapes.sliders[\"t\"].value = 20\n", + "iw_mixed_shapes.current_index = {\"t\": 20}\n", "plot_test(\"image-widget-zfish-mixed-rgb-cockatoo-windowrgb\", iw_mixed_shapes.figure)" ] }, diff --git a/examples/notebooks/screenshots/nb-astronaut.png b/examples/notebooks/screenshots/nb-astronaut.png index 70c1a95a7..32b09caf9 100644 --- a/examples/notebooks/screenshots/nb-astronaut.png +++ b/examples/notebooks/screenshots/nb-astronaut.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c1491279a44125be3fc51678a2662b0632d8618a7425b7894677a7eba919eae9 -size 84735 +oid sha256:8d9e2b0479d3de1c12764b984679dba83a1876ea6a88c072789a0e06f957ca2a +size 70655 diff --git a/examples/notebooks/screenshots/nb-astronaut_RGB.png b/examples/notebooks/screenshots/nb-astronaut_RGB.png index 0443de1c4..be498bb6d 100644 --- a/examples/notebooks/screenshots/nb-astronaut_RGB.png +++ b/examples/notebooks/screenshots/nb-astronaut_RGB.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:716e19f1f9d16443602de327716daee8663731e1afccfa4a9b16c68ffd3b0c11 -size 76074 +oid sha256:e2d02877510191e951d38d03a6fe9d31f5c0c335913876c65b175c4bb1a9c0e1 +size 69942 diff --git a/examples/notebooks/screenshots/nb-camera.png b/examples/notebooks/screenshots/nb-camera.png index e71803ade..3e9a518f9 100644 --- a/examples/notebooks/screenshots/nb-camera.png +++ b/examples/notebooks/screenshots/nb-camera.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b84ffb87948cfd523941041a3c9c6827ccac51bb5648faddd810d15a4bd0912c -size 52034 +oid sha256:5271c2204a928185b287c73c852ffa06b708d8d6a33de09acda8d2ea734e78c5 +size 51445 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-set_data.png b/examples/notebooks/screenshots/nb-image-widget-movie-set_data.png index e8e74e817..8fdf2fd89 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-set_data.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-set_data.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d9421323aac16e9e8d3489332b7db7b2381effc4b10a132e2c58dc86544720ae -size 45797 +oid sha256:1faa3db006aa7f9d41757564783cef67d1a906dc67bca045c2c30501a86306c2 +size 43947 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-0-reset.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-0-reset.png index 4fce1c96a..f64393c89 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-0-reset.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-0-reset.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:949885c0eab52bbb5293aa74ded4d3dedfd5172d1217934fa8963b7c74f176e8 -size 118713 +oid sha256:8e3f53d21e99424f11a3a920346909dce42f2c344ae9b43af5965bc2302ae9ab +size 117732 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-0.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-0.png index ffb80c4ec..f64393c89 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-0.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f05522f502bc848086c01ba86f17036b310617e8bfb239d83505ef31c2ad23a7 -size 106685 +oid sha256:8e3f53d21e99424f11a3a920346909dce42f2c344ae9b43af5965bc2302ae9ab +size 117732 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-279.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-279.png index 0063b3fa2..812e0f60d 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-279.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-279.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9cb358df1f9dcb67f26818cad619c0740f25602cdbb634b737112d6d43c89fc8 -size 142265 +oid sha256:b59639c87a6d02aaf8a14e8d681d763a795c15b7aa8d2d0a90dba3a5732e4fe5 +size 140917 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-max-33.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-max-33.png index 9c48d5258..9907e1473 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-max-33.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-max-33.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:62c303c87a6fbc2f2874b817ca0d938b8a6f83042e81a659d9feb7d7fe7442a6 -size 127805 +oid sha256:5f9f5e1953aae367cca8add259c86d82fd5225f4cf4279c6504b1ecd9d5a0bd1 +size 125867 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-13.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-13.png index 388a280e1..695964431 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b22f9823bab849de025b256558f008efdfadcb181c66510f32293d2fea53c6f0 -size 110339 +oid sha256:3633ce4d8995ebdb224df9fcd8ebdf22ad9ffa72e3ef80692f4f691895faf903 +size 110162 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-33.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-33.png index 1d0802226..039cdd25c 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-33.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-33.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5baf57418ed6f36278715187250ac69307cd88eb4232df586a3c929ffbc40d4b -size 102774 +oid sha256:8ba9762d2d3fb7ddaa1628e40588b28780ac3e0185ec97187eb1975016aa32f1 +size 102404 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-reset.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-reset.png index 6534b9907..a6aae44ba 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-reset.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-reset.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e009147472683c8d207a23d7c64575465f936ee48250dfa9fe15654ed7d34403 -size 126018 +oid sha256:f9374ebf448c1692c63c3ca1c28b0d16125bb5ae021d9a7cc8a1beee3c25a183 +size 124817 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50.png index 6534b9907..a6aae44ba 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e009147472683c8d207a23d7c64575465f936ee48250dfa9fe15654ed7d34403 -size 126018 +oid sha256:f9374ebf448c1692c63c3ca1c28b0d16125bb5ae021d9a7cc8a1beee3c25a183 +size 124817 diff --git a/examples/notebooks/screenshots/nb-image-widget-single-gnuplot2.png b/examples/notebooks/screenshots/nb-image-widget-single-gnuplot2.png index f157e63c2..48ab5d6fe 100644 --- a/examples/notebooks/screenshots/nb-image-widget-single-gnuplot2.png +++ b/examples/notebooks/screenshots/nb-image-widget-single-gnuplot2.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4d77e42683f74dbd311aa56e5c66b4bb90449e5e52a5a9d4ae3a04cf774ca4df -size 306329 +oid sha256:c65e2dc4276393278ab769706f22172fd71e38eeb3c9f4d70fa51de31820f1d1 +size 234012 diff --git a/examples/notebooks/screenshots/nb-image-widget-single.png b/examples/notebooks/screenshots/nb-image-widget-single.png index c262e74ce..5e1cb8cc1 100644 --- a/examples/notebooks/screenshots/nb-image-widget-single.png +++ b/examples/notebooks/screenshots/nb-image-widget-single.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a0f6a4eea4dcf0100b6cdd89892fb02bc2d2c5396445ef0694a0810b9d4465e8 -size 274170 +oid sha256:7d4e4edf1429a135bafb7c1c927ea87f78a93fb5f3e0cbee2fb5c156af88d5a0 +size 220490 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-gaussian.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-gaussian.png index a78761846..0f6223ab4 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-gaussian.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-gaussian.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:de65879e7ad15cd85740c989d62bd27e4e2fdbe61de3585238caaa52b554fa95 -size 129651 +oid sha256:ecba9807b765ea12ad1183dabc35c9b6a2ba45f95aa0126d772801c3a5aba6e1 +size 92089 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-reset.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-reset.png index f5989caa9..0c6b55201 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-reset.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-reset.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:828e3e104d21f0cc743f16b72152642aa276d5727258b4454a3f5dcc6514ac7e -size 81188 +oid sha256:433190c3f56075ca3e9a5486e5986424d31fb7b6f6145225a15bfafd5e00fa83 +size 74779 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-max-window-13.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-max-window-13.png index 3e3cdc025..8321b60e9 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-max-window-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-max-window-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e615d9dbcbc09d14eb1ab2aea14c609e996e0f96bafdf3c4513acd0613260509 -size 205824 +oid sha256:de11fd007bad064ccb6574ee682d7cd25c64738768d1dc9e42b53c88cb78c46c +size 155123 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-13.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-13.png index 22fe4e54d..27c3af054 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d81c351726c3eb1cbef662b28454747e6074374bdd2203d0e71835c6611cda11 -size 151657 +oid sha256:ec9e3a90abd029a5fb6e149ba721f3340e499c26a2dd4aab6ab07a185bbc4ff0 +size 103878 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-5.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-5.png index e6a877eec..72ee543e2 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-5.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:71676028d9a29fb4dbb4e3aaa3dd9173dff585fe08300a5d8884e6d8e413952e -size 131857 +oid sha256:f1c9b64c4c67a024a5dc839ad13f68ee60f3b3675144976c7e3b6ce989e0c822 +size 97746 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50.png index 023cb947c..572b1e590 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dc841d0a37938319d177ecd74662de486c4fe2bc8be35de456ad7a3abd4ca329 -size 90997 +oid sha256:51865c6bfd62d9691c638d4bc3b62507a61e873851dc35bad66acb0d41e4fe3e +size 83536 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-99.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-99.png index c1fa94056..1ceebf476 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-99.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-99.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:42a51e116e1455fcea00dd9ea9f387633df31e185706cd0fd18e8101947718be -size 74817 +oid sha256:9f46a0e116fed8d474217d5a6ca6f9861647707e1abe049e796c67a417e197cd +size 72243 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-gaussian.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-gaussian.png index f79d956b0..8464eed64 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-gaussian.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-gaussian.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f9900ac2df273c48d163538c6cbda2d4b43245bbcc55f759a50883aaf51cf876 -size 160362 +oid sha256:ac0fcaae315baf29a46f2c4151b988535934927441c13eef711b8567951b50cb +size 106873 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-reset.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-reset.png index 572e1c2a7..c81b99e29 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-reset.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-reset.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:35d948baddc0c069e65d63d04fb3f22276dd936deee281fdf0bf31c2203c0e01 -size 156432 +oid sha256:a001606a953ea6b8a5c7f741532df4a006e7bf4dd31ff9e7b4f9f1025987153c +size 109591 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-max-window-13.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-max-window-13.png index 8f083da9b..b7314938c 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-max-window-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-max-window-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:371727d51121efa1f6250f9aebdb1a3847e2ef79cf1a9137d5c07b8738114b9b -size 307668 +oid sha256:cb99c2827dea7e49d5ffcd4fe10e4052671548500f05001067077a8841d03cc8 +size 168987 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-13.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-13.png index e59f9020f..b09d8b4ca 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d179a1279dcd12c10495e3022fdf8ae3b5ee8ed50ca0ba512f0dbd6b1fd325f8 -size 184162 +oid sha256:729b31d419eccf02eb617a10bb29b2f9295726f8167329ac0f7498e752688bb8 +size 113715 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-5.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-5.png index 3d133063f..9daeb680f 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-5.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dae80d58e60532eb151354de65574b70a73bc6ef8dcaba8c65d39da6cc388eda -size 184497 +oid sha256:7de21cfe1a21c1ef59bfb7121b2ef6f55dd9931d5885a3ee3c932dcf82389191 +size 116752 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50.png index e79a20bbd..fff0cca94 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ba9a962dfdc0bcfd033dff87e970554b3d480226c904a3fb2428c427312c7e42 -size 176697 +oid sha256:ca70505035a6b8cb8589a8219edd3d79eecdc93642e3d7f7db00516d27bac959 +size 122542 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-99.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-99.png index 9f8791bcb..57154e0c0 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-99.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-99.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b3cbdc194f63da1e33a7e367175e93e49bf5f69c988bb8ac03c137bd1505adc5 -size 166434 +oid sha256:a41f79b543ea852345ca5c5c3e401d39c43b9f5d4172805c48d7010d3e85e88a +size 118378 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-init-mean-window-5.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-init-mean-window-5.png index fcd0b1382..e5554d635 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-init-mean-window-5.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-init-mean-window-5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:051f4e6dc5a6a9824da12165bf6475baf83886ca97c6ba10c0ea2f615dc4e0ee -size 162378 +oid sha256:d364f4c18516c282cd284c731122618bdf3418b6d536afa7b0556105f89c3607 +size 119048 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-false.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-false.png index 9d45ca1aa..048078520 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-false.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-false.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6adc3973283da26ad475351af58d91892251c53fe0e09f714cf21cfdab7502c6 -size 140885 +oid sha256:33ce1260b4715b3d28ba28d0ad4c5eb94c9997bdc1676ff6208121e789e168a5 +size 99287 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-true.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-true.png index 190025d6d..ade8fb483 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-true.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-true.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bab10f413eaac26d69848ada9528fa1e36e616eab37c5244d7f1c9c3ab85e7d6 -size 143505 +oid sha256:5e08f4e4cb3330fbbbf827af56c02039af3b293036c7676f2a87c309ad07f2f6 +size 99759 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-init-mean-window-5.png b/examples/notebooks/screenshots/nb-image-widget-zfish-init-mean-window-5.png index e97c2ffd0..94b39e8f9 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-init-mean-window-5.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-init-mean-window-5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:10042f15d067049e05418f1e82eb3428f56f53a2c159ac4eaa135d42dfc3d057 -size 88268 +oid sha256:07bcc6ef243d9d3ffd7fae69facf655e7c02b1cb53ea96a38b40ed672655bf66 +size 86607 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-frame-50.png b/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-frame-50.png index de9822952..dab2098fd 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-frame-50.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-frame-50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1e031e6712bb7a9601f627e32347c05ed2669363ee1ffe428d10797081c32ef0 -size 113064 +oid sha256:7270870881ac478f48a269b060c5bf7ca59e7abe8a42254162a0295f6165230b +size 117870 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-set-data.png b/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-set-data.png index 2e47302a8..7f530e554 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-set-data.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-set-data.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0aaa7782c20f209e07a7259d676b4fc993d4f25ba1a52150d5512d8ef16b82bc -size 130999 +oid sha256:414ebe9a0b2bc4eb1caa4b4aeef070955c662bb691899c4b2046be3e2ca821e3 +size 113649 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-windowrgb.png b/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-windowrgb.png index 9104fb9ea..e2f6b8318 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-windowrgb.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-windowrgb.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5b478e4cd25c96e2c08b3f595193d019a0cfcac69f8ea3e3a8330cf6c0ffabbf -size 131188 +oid sha256:ea6d0c4756db434af6e257b7cd809f1d49089eca6b9eae9e347801e20b175686 +size 113631 diff --git a/examples/notebooks/screenshots/nb-lines-3d.png b/examples/notebooks/screenshots/nb-lines-3d.png index 65310e7f1..2e26a8cd7 100644 --- a/examples/notebooks/screenshots/nb-lines-3d.png +++ b/examples/notebooks/screenshots/nb-lines-3d.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fc7a8caabb59ff2f2fd9811678b974542c6a3dddfd0d005109777264056d458a -size 23430 +oid sha256:857eb528b02fd7dd4b9f46ce1e65942066736f1bdf5271db141d73a0abab82b0 +size 19457 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-jet-values-cosine.png b/examples/notebooks/screenshots/nb-lines-cmap-jet-values-cosine.png index 9f3a156b9..08b6e8ac1 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-jet-values-cosine.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-jet-values-cosine.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:80d318cb1daf701e682e600c0058cf3e5c336062dd459618eac60f92ec2399ad -size 17362 +oid sha256:a499ecad892c779aa857e9074a5e157b02bc914007b28aa4958b2b231b5961a4 +size 18585 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-jet-values.png b/examples/notebooks/screenshots/nb-lines-cmap-jet-values.png index 677906685..83b5c21d9 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-jet-values.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-jet-values.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:996d29cdf767f3863615ebc0d5b91f4ec07af898350c07b2fd78426c239cb452 -size 18817 +oid sha256:9506e9838bd5bb1f79d41c8dfaa92c127d12852758bfcecfa37202d02b0ba325 +size 19914 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-jet.png b/examples/notebooks/screenshots/nb-lines-cmap-jet.png index 5195c617d..34a6f8b6f 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-jet.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-jet.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:74da9cc1bac480b5d72693ea7e074d5192e382a989816912757bd307a1e04faf -size 17367 +oid sha256:8e6a74dad6621df938517558adf89e19e975b39386d1a46d48991f0cffe725dd +size 18549 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-tab-10.png b/examples/notebooks/screenshots/nb-lines-cmap-tab-10.png index d766bcda0..ca41e764d 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-tab-10.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-tab-10.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:04052da5609011c7df22e4688bfe1100ad90fa757099d7b315d0b2bcaeb8c3d0 -size 15876 +oid sha256:5725b52f18e1be1f7bd358b489374c0f733cf9c3f1f127a4fbfbd1daec30a57f +size 17061 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-viridis-values.png b/examples/notebooks/screenshots/nb-lines-cmap-viridis-values.png index 723beb580..dc7ec0c13 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-viridis-values.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-viridis-values.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1ea8423ccba04a9a137640b28ff8f84e167d46735d87fd38c48c29373a9601ac -size 16223 +oid sha256:f1f08fea3f0f74ab7632725f544c03303334cfcf1e01b188d01113a8bcc84dd7 +size 17353 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-viridis.png b/examples/notebooks/screenshots/nb-lines-cmap-viridis.png index e6493053c..912cae1e4 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-viridis.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-viridis.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9fd9697d7df47491c6b9e73424dd07088c7e22758d5672a99ddbce56e4ff3b02 -size 19316 +oid sha256:a5d14cb03071ca2ad4c0c2145d68ab3555d30565f8f6d2be6fa7fc649213d748 +size 20499 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-white.png b/examples/notebooks/screenshots/nb-lines-cmap-white.png index cbbbef0bc..2edc6903b 100644 --- a/examples/notebooks/screenshots/nb-lines-cmap-white.png +++ b/examples/notebooks/screenshots/nb-lines-cmap-white.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:54242cbcd3f196e5f39fc3a27a98b42f9515f04625d37d3b210afd37721078dc -size 8967 +oid sha256:3c7a2c8ccd70787e67800fb0d9625d7816fb6b004c0128b3b254fec7c7fd7c71 +size 16058 diff --git a/examples/notebooks/screenshots/nb-lines-colors.png b/examples/notebooks/screenshots/nb-lines-colors.png index 60792f453..1e13983f3 100644 --- a/examples/notebooks/screenshots/nb-lines-colors.png +++ b/examples/notebooks/screenshots/nb-lines-colors.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9ae3f1bae2ea0fe146c7096af3551e5e58704416bff49a6f0bdd5415cfc1533b -size 37095 +oid sha256:6681a1e5658c1f2214217dcb7321cad89c7a0a3fd7919296a1069f27f1a7ee92 +size 35381 diff --git a/examples/notebooks/screenshots/nb-lines-data.png b/examples/notebooks/screenshots/nb-lines-data.png index 86ce4362b..a7e8287ef 100644 --- a/examples/notebooks/screenshots/nb-lines-data.png +++ b/examples/notebooks/screenshots/nb-lines-data.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:17ec845345cb97088de4c18c8eebc54c9a27c5770724f8f582b4144f3e70d139 -size 46868 +oid sha256:043d8d9cd6dfc7627a6ccdb5810efd4b1a15e8880a4e30c0f558ae4d67c2f470 +size 42410 diff --git a/examples/notebooks/screenshots/nb-lines-underlay.png b/examples/notebooks/screenshots/nb-lines-underlay.png index 7d1280db4..c2908d479 100644 --- a/examples/notebooks/screenshots/nb-lines-underlay.png +++ b/examples/notebooks/screenshots/nb-lines-underlay.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:467788d8588fa1794c0cd705e03564537ff49f79762a5e8f092700516d503391 -size 52447 +oid sha256:c52ac60ffc08005d1f1fcad1b29339a89a0f31b58c9ca692f9d93400e7c8ac9e +size 48540 diff --git a/examples/notebooks/screenshots/nb-lines.png b/examples/notebooks/screenshots/nb-lines.png index 4fd64a56d..f4a4d58b1 100644 --- a/examples/notebooks/screenshots/nb-lines.png +++ b/examples/notebooks/screenshots/nb-lines.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:da1e28036caa8077885f52aa3a6ba4dbe1ee4f8cfa79a7b604614483150cd7b7 -size 24798 +oid sha256:2cef0e2fb84e985f8d9c18f77817fb3eba31bd30b8fa4c54bb71432587909458 +size 30075 diff --git a/examples/tests/test_examples.py b/examples/tests/test_examples.py index 9562a4357..0143c9bef 100644 --- a/examples/tests/test_examples.py +++ b/examples/tests/test_examples.py @@ -8,7 +8,10 @@ import os import numpy as np import imageio.v3 as iio +import pygfx +MAX_TEXTURE_SIZE = 2048 +pygfx.renderers.wgpu.set_wgpu_limits(**{"max-texture-dimension2d": MAX_TEXTURE_SIZE}) from .testutils import ( ROOT, @@ -64,14 +67,30 @@ def test_example_screenshots(module, force_offscreen): .as_posix() .replace("/", ".") ) + print(pygfx.renderers.wgpu.get_shared().device.limits["max-texture-dimension2d"]) # import the example module example = importlib.import_module(module_name) + # there doesn't seem to be a resize event for the manual offscreen canvas + example.figure.imgui_renderer._backend.io.display_size = example.figure.canvas.get_logical_size() + + # run this once so any edge widgets set their sizes and therefore the subplots get the correct rect + # hacky but it works for now + example.figure.imgui_renderer.render() + + # render each subplot for subplot in example.figure: subplot.viewport.render(subplot.scene, subplot.camera) + for dock in subplot.docks.values(): + dock.set_viewport_rect() + + # flush pygfx renderer example.figure.renderer.flush() + # render imgui + example.figure.imgui_renderer.render() + # render a frame img = np.asarray(example.figure.renderer.target.draw()) diff --git a/examples/tests/testutils.py b/examples/tests/testutils.py index 2ea4742ea..3db6901ef 100644 --- a/examples/tests/testutils.py +++ b/examples/tests/testutils.py @@ -18,13 +18,15 @@ # examples live in themed sub-folders example_globs = [ "image/*.py", + "image_widget/*.py", "heatmap/*.py", "scatter/*.py", "line/*.py", "line_collection/*.py", "gridplot/*.py", "misc/*.py", - "selectors/*.py" + "selectors/*.py", + "guis/*.py", ] diff --git a/fastplotlib/__init__.py b/fastplotlib/__init__.py index 19dfb1903..a85de93c2 100644 --- a/fastplotlib/__init__.py +++ b/fastplotlib/__init__.py @@ -5,7 +5,15 @@ from .graphics.selectors import * from .graphics.utils import pause_events from .legends import * -from .layouts import Figure +from .tools import * + +from .layouts import IMGUI + +if IMGUI: + # default to imgui figure if imgui_bundle is installed + from .layouts import ImguiFigure as Figure +else: + from .layouts import Figure from .widgets import ImageWidget from .utils import config, enumerate_adapters, select_adapter, print_wgpu_report diff --git a/fastplotlib/graphics/_base.py b/fastplotlib/graphics/_base.py index 27bfbc149..c3fc665e7 100644 --- a/fastplotlib/graphics/_base.py +++ b/fastplotlib/graphics/_base.py @@ -7,6 +7,13 @@ import pylinalg as la from wgpu.gui.base import log_exception +try: + from imgui_bundle import imgui +except ImportError: + IMGUI = False +else: + IMGUI = True + import pygfx from ._features import ( @@ -117,6 +124,8 @@ def __init__( self._axes: Axes = None + self._right_click_menu = None + @property def supported_events(self) -> tuple[str]: """events supported by this graphic""" @@ -303,17 +312,6 @@ def _handle_event(self, callback, event: pygfx.Event): # for feature events event._target = self.world_object - if isinstance(event, pygfx.PointerEvent): - # map from screen to world space and data space - world_xy = self._plot_area.map_screen_to_world(event) - - # subtract offset to map to data - data_xy = world_xy - self.offset - - # append attributes - event.x_world, event.y_world = world_xy[:2] - event.x_data, event.y_data = data_xy[:2] - with log_exception(f"Error during handling {event.type} event"): callback(event) @@ -334,8 +332,6 @@ def remove_event_handler(self, callback, *types): self._event_handlers[t].remove(callback) # remove callback wrapper from world object if pygfx event if t in PYGFX_EVENTS: - print("pygfx event") - print(wrapper) self.world_object.remove_event_handler(wrapper, t) else: feature = getattr(self, f"_{t}") @@ -437,3 +433,24 @@ def add_axes(self): self._plot_area.scene.add(self.axes.world_object) self._axes.update_using_bbox(self.world_object.get_world_bounding_box()) + + @property + def right_click_menu(self): + return self._right_click_menu + + @right_click_menu.setter + def right_click_menu(self, menu): + if not IMGUI: + raise ImportError( + "imgui is required to set right-click menus:\n" + "pip install imgui_bundle" + ) + + self._right_click_menu = menu + menu.owner = self + + def _fpl_request_right_click_menu(self): + pass + + def _fpl_close_right_click_menu(self): + pass diff --git a/fastplotlib/graphics/_features/__init__.py b/fastplotlib/graphics/_features/__init__.py index 1d2f6ca44..4f9013425 100644 --- a/fastplotlib/graphics/_features/__init__.py +++ b/fastplotlib/graphics/_features/__init__.py @@ -14,7 +14,6 @@ ImageVmax, ImageInterpolation, ImageCmapInterpolation, - WGPU_MAX_TEXTURE_SIZE, ) from ._base import ( GraphicFeature, diff --git a/fastplotlib/graphics/_features/_base.py b/fastplotlib/graphics/_features/_base.py index a57f8a453..1612414a1 100644 --- a/fastplotlib/graphics/_features/_base.py +++ b/fastplotlib/graphics/_features/_base.py @@ -9,9 +9,6 @@ import pygfx -WGPU_MAX_TEXTURE_SIZE = 8192 - - def to_gpu_supported_dtype(array): """ convert input array to float32 numpy array diff --git a/fastplotlib/graphics/_features/_image.py b/fastplotlib/graphics/_features/_image.py index 2d93745bf..513677e15 100644 --- a/fastplotlib/graphics/_features/_image.py +++ b/fastplotlib/graphics/_features/_image.py @@ -5,7 +5,7 @@ import numpy as np import pygfx -from ._base import GraphicFeature, FeatureEvent, WGPU_MAX_TEXTURE_SIZE +from ._base import GraphicFeature, FeatureEvent from ...utils import ( make_colors, @@ -20,6 +20,9 @@ def __init__(self, data, isolated_buffer: bool = True): data = self._fix_data(data) + shared = pygfx.renderers.wgpu.get_shared() + self._texture_limit_2d = shared.device.limits["max-texture-dimension2d"] + if isolated_buffer: # useful if data is read-only, example: memmaps self._value = np.zeros(data.shape, dtype=data.dtype) @@ -31,13 +34,13 @@ def __init__(self, data, isolated_buffer: bool = True): # data start indices for each Texture self._row_indices = np.arange( 0, - ceil(self.value.shape[0] / WGPU_MAX_TEXTURE_SIZE) * WGPU_MAX_TEXTURE_SIZE, - WGPU_MAX_TEXTURE_SIZE, + ceil(self.value.shape[0] / self._texture_limit_2d) * self._texture_limit_2d, + self._texture_limit_2d, ) self._col_indices = np.arange( 0, - ceil(self.value.shape[1] / WGPU_MAX_TEXTURE_SIZE) * WGPU_MAX_TEXTURE_SIZE, - WGPU_MAX_TEXTURE_SIZE, + ceil(self.value.shape[1] / self._texture_limit_2d) * self._texture_limit_2d, + self._texture_limit_2d, ) # buffer will be an array of textures @@ -118,8 +121,8 @@ def __next__(self) -> tuple[pygfx.Texture, tuple[int, int], tuple[slice, slice]] chunk_index = (chunk_row, chunk_col) # stop indices of big data array for this chunk - row_stop = min(self.value.shape[0], data_row_start + WGPU_MAX_TEXTURE_SIZE) - col_stop = min(self.value.shape[1], data_col_start + WGPU_MAX_TEXTURE_SIZE) + row_stop = min(self.value.shape[0], data_row_start + self._texture_limit_2d) + col_stop = min(self.value.shape[1], data_col_start + self._texture_limit_2d) # row and column slices that slice the data for this chunk from the big data array data_slice = (slice(data_row_start, row_stop), slice(data_col_start, col_stop)) diff --git a/fastplotlib/layouts/__init__.py b/fastplotlib/layouts/__init__.py index 60111cabc..4a4f45174 100644 --- a/fastplotlib/layouts/__init__.py +++ b/fastplotlib/layouts/__init__.py @@ -1,3 +1,15 @@ from ._figure import Figure -__all__ = ["Figure"] +try: + import imgui_bundle +except ImportError: + IMGUI = False +else: + IMGUI = True + +if IMGUI: + from ._imgui_figure import ImguiFigure + + __all__ = ["Figure", "ImguiFigure"] +else: + __all__ = ["Figure"] diff --git a/fastplotlib/layouts/_figure.py b/fastplotlib/layouts/_figure.py index 3ad5231c7..bba5d4aab 100644 --- a/fastplotlib/layouts/_figure.py +++ b/fastplotlib/layouts/_figure.py @@ -109,7 +109,9 @@ def __init__( else: subplot_names = None - canvas, renderer = make_canvas_and_renderer(canvas, renderer) + canvas, renderer = make_canvas_and_renderer( + canvas, renderer, canvas_kwargs={"size": size} + ) if isinstance(cameras, str): # create the array representing the views for each subplot in the grid @@ -322,25 +324,10 @@ def __init__( self._current_iter = None - self._starting_size = size + self._sidecar = None self._output = None - if self.canvas.__class__.__name__ == "JupyterWgpuCanvas": - self.recorder = FigureRecorder(self) - else: - self.recorder = None - - @property - def toolbar(self): - """ipywidget or QToolbar instance""" - return self._output.toolbar - - @property - def output(self): - """ipywidget or QWidget that contains this plot""" - return self._output - @property def shape(self) -> tuple[int, int]: """[n_rows, n_cols]""" @@ -390,7 +377,7 @@ def __getitem__(self, index: tuple[int, int] | str) -> Subplot: else: return self._subplots[index[0], index[1]] - def render(self): + def render(self, draw=True): # call the animation functions before render self._call_animate_functions(self._animate_funcs_pre) @@ -398,7 +385,8 @@ def render(self): subplot.render() self.renderer.flush() - self.canvas.request_draw() + if draw: + self.canvas.request_draw() # call post-render animate functions self._call_animate_functions(self._animate_funcs_post) @@ -406,19 +394,16 @@ def render(self): def start_render(self): """start render cycle""" self.canvas.request_draw(self.render) - self.canvas.set_logical_size(*self._starting_size) def show( self, autoscale: bool = True, maintain_aspect: bool = None, - toolbar: bool = True, sidecar: bool = False, sidecar_kwargs: dict = None, - add_widgets: list = None, ): """ - Begins the rendering event loop and shows the plot in the desired output context (jupyter, qt or glfw). + Begins the rendering event loop and shows the Figure, returns the canvas Parameters ---------- @@ -428,29 +413,22 @@ def show( maintain_aspect: bool, default ``True`` maintain aspect ratio - toolbar: bool, default ``True`` - show toolbar - sidecar: bool, default ``True`` - display plot in a ``jupyterlab-sidecar``, only for jupyter output context + display plot in a ``jupyterlab-sidecar``, only in jupyter sidecar_kwargs: dict, default ``None`` kwargs for sidecar instance to display plot i.e. title, layout - add_widgets: list of widgets - a list of ipywidgets or QWidget that are vertically stacked below the plot - Returns ------- - OutputContext - In jupyter, it will display the plot in the output cell or sidecar - - In Qt, it will display the Plot, toolbar, etc. as stacked widget, use `Plot.widget` to access it. + WgpuCanvasBase + In Qt or GLFW, the canvas window containing the Figure will be shown. + In jupyter, it will display the plot in the output cell or sidecar. """ - # show was already called, return existing output context - if self._output is not None: + # show was already called, return canvas + if self._output: return self._output self.start_render() @@ -458,9 +436,6 @@ def show( if sidecar_kwargs is None: sidecar_kwargs = dict() - if add_widgets is None: - add_widgets = list() - # flip y-axis if ImageGraphics are present for subplot in self: for g in subplot.graphics: @@ -476,26 +451,23 @@ def show( _maintain_aspect = maintain_aspect subplot.auto_scale(maintain_aspect=maintain_aspect) - # return the appropriate OutputContext based on the current canvas + # parse based on canvas type if self.canvas.__class__.__name__ == "JupyterWgpuCanvas": - from .output.jupyter_output import ( - JupyterOutputContext, - ) # noqa - inline import - - self._output = JupyterOutputContext( - frame=self, - make_toolbar=toolbar, - use_sidecar=sidecar, - sidecar_kwargs=sidecar_kwargs, - add_widgets=add_widgets, - ) + if sidecar: + from sidecar import Sidecar + from IPython.display import display + + self._sidecar = Sidecar(**sidecar_kwargs) + self._output = self.canvas + with self._sidecar: + return display(self.canvas) + self._output = self.canvas + return self._output elif self.canvas.__class__.__name__ == "QWgpuCanvas": - from .output.qt_output import QOutputContext # noqa - inline import - - self._output = QOutputContext( - frame=self, make_toolbar=toolbar, add_widgets=add_widgets - ) + self._output = self.canvas + self._output.show() + return self.canvas elif self.canvas.__class__.__name__ == "WgpuManualOffscreenCanvas": # for test and docs gallery screenshots @@ -509,16 +481,30 @@ def show( # but it is necessary for the gallery images too so that's why this check is here if "RTD_BUILD" in os.environ.keys(): if os.environ["RTD_BUILD"] == "1": - subplot.viewport.render(subplot.scene, subplot.camera) + self.render() - else: # assume GLFW, the output context is just the canvas + else: # assume GLFW self._output = self.canvas - # return the output context, this call is required for jupyter but not for Qt + # return the canvas return self._output def close(self): - self.output.close() + self._output.close() + if self._sidecar: + self._sidecar.close() + + def get_pygfx_render_area(self, *args) -> tuple[int, int, int, int]: + """ + Get rect for the portion of the canvas that the pygfx renderer draws to + + Returns + ------- + tuple[int, int, int, int] + x_pos, y_pos, width, height + + """ + return 0, 0, *self.canvas.get_logical_size() def _call_animate_functions(self, funcs: list[callable]): for fn in funcs: @@ -640,6 +626,25 @@ def export(self, uri: str | Path | bytes, **kwargs): return iio.imwrite(uri, snapshot, **kwargs) + def open_popup(self, *args, **kwargs): + warn("popups only supported by ImguiFigure") + + def get_pygfx_render_area(self, *args) -> tuple[int, int, int, int]: + """ + Fet rect for the portion of the canvas that the pygfx renderer draws to, + i.e. non-imgui, part of canvas + + Returns + ------- + tuple[int, int, int, int] + x_pos, y_pos, width, height + + """ + + width, height = self.canvas.get_logical_size() + + return 0, 0, width, height + def _get_iterator(self): return product(range(self.shape[0]), range(self.shape[1])) diff --git a/fastplotlib/layouts/_imgui_figure.py b/fastplotlib/layouts/_imgui_figure.py new file mode 100644 index 000000000..3396c3d27 --- /dev/null +++ b/fastplotlib/layouts/_imgui_figure.py @@ -0,0 +1,243 @@ +from pathlib import Path +from typing import Literal, Iterable + +import numpy as np + +import imgui_bundle +from imgui_bundle import imgui, icons_fontawesome_6 as fa + +from wgpu.utils.imgui import ImguiRenderer +from wgpu.gui import WgpuCanvasBase + +import pygfx + +from ._figure import Figure +from ._utils import make_canvas_and_renderer +from ..ui import EdgeWindow, SubplotToolbar, StandardRightClickMenu, Popup, GUI_EDGES +from ..ui import ColormapPicker + + +class ImguiFigure(Figure): + def __init__( + self, + shape: tuple[int, int] = (1, 1), + cameras: ( + Literal["2d", "3d"] + | Iterable[Iterable[Literal["2d", "3d"]]] + | pygfx.PerspectiveCamera + | Iterable[Iterable[pygfx.PerspectiveCamera]] + ) = "2d", + controller_types: ( + Iterable[Iterable[Literal["panzoom", "fly", "trackball", "orbit"]]] + | Iterable[Literal["panzoom", "fly", "trackball", "orbit"]] + ) = None, + controller_ids: ( + Literal["sync"] + | Iterable[int] + | Iterable[Iterable[int]] + | Iterable[Iterable[str]] + ) = None, + controllers: pygfx.Controller | Iterable[Iterable[pygfx.Controller]] = None, + canvas: str | WgpuCanvasBase | pygfx.Texture = None, + renderer: pygfx.WgpuRenderer = None, + size: tuple[int, int] = (500, 300), + names: list | np.ndarray = None, + ): + self._guis: dict[str, EdgeWindow] = {k: None for k in GUI_EDGES} + + canvas, renderer = make_canvas_and_renderer( + canvas, renderer, canvas_kwargs={"size": size} + ) + self._imgui_renderer = ImguiRenderer(renderer.device, canvas) + + super().__init__( + shape=shape, + cameras=cameras, + controller_types=controller_types, + controller_ids=controller_ids, + controllers=controllers, + canvas=canvas, + renderer=renderer, + size=size, + names=names, + ) + + fronts_path = str( + Path(imgui_bundle.__file__).parent.joinpath( + "assets", "fonts", "Font_Awesome_6_Free-Solid-900.otf" + ) + ) + + io = imgui.get_io() + + self._fa_icons = io.fonts.add_font_from_file_ttf( + fronts_path, 16, glyph_ranges_as_int_list=[fa.ICON_MIN_FA, fa.ICON_MAX_FA] + ) + + io.fonts.build() + self.imgui_renderer.backend.create_fonts_texture() + + self.imgui_renderer.set_gui(self._draw_imgui) + + self._subplot_toolbars: np.ndarray[SubplotToolbar] = np.empty( + shape=self._subplots.shape, dtype=object + ) + + for subplot in self._subplots.ravel(): + toolbar = SubplotToolbar(subplot=subplot, fa_icons=self._fa_icons) + self._subplot_toolbars[subplot.position] = toolbar + + self._right_click_menu = StandardRightClickMenu( + figure=self, fa_icons=self._fa_icons + ) + + self._popups: dict[str, Popup] = {} + + self.register_popup(ColormapPicker) + + @property + def guis(self) -> dict[str, EdgeWindow]: + """GUI windows added to the Figure""" + return self._guis + + @property + def imgui_renderer(self) -> ImguiRenderer: + """imgui renderer""" + return self._imgui_renderer + + def render(self, draw=False): + super().render(draw) + + self.imgui_renderer.render() + self.canvas.request_draw() + + def _draw_imgui(self) -> imgui.ImDrawData: + imgui.new_frame() + + for subplot, toolbar in zip( + self._subplots.ravel(), self._subplot_toolbars.ravel() + ): + if not subplot.toolbar: + # if subplot.toolbar is False + continue + toolbar.update() + + for gui in self.guis.values(): + if gui is not None: + gui.draw_window() + + for popup in self._popups.values(): + popup.update() + + self._right_click_menu.update() + + imgui.end_frame() + + imgui.render() + + return imgui.get_draw_data() + + def add_gui(self, gui: EdgeWindow): + """ + Add a GUI to the Figure. GUIs can be added to the top, bottom, left or right edge. + + Parameters + ---------- + gui: EdgeWindow + A GUI EdgeWindow instance + + """ + if not isinstance(gui, EdgeWindow): + raise TypeError( + f"GUI must be of type: {EdgeWindow} you have passed a {type(gui)}" + ) + + location = gui.location + + if location not in GUI_EDGES: + raise ValueError( + f"GUI does not have a valid location, valid locations are: {GUI_EDGES}, you have passed: {location}" + ) + + if self.guis[location] is not None: + raise ValueError(f"GUI already exists in the desired location: {location}") + + self.guis[location] = gui + + self._reset_viewports() + + def get_pygfx_render_area(self, *args) -> tuple[int, int, int, int]: + """ + Fet rect for the portion of the canvas that the pygfx renderer draws to, + i.e. non-imgui, part of canvas + + Returns + ------- + tuple[int, int, int, int] + x_pos, y_pos, width, height + + """ + + width, height = self.canvas.get_logical_size() + + for edge in ["left", "right"]: + if self.guis[edge]: + width -= self._guis[edge].size + + for edge in ["top", "bottom"]: + if self.guis[edge]: + height -= self._guis[edge].size + + if self.guis["left"]: + xpos = self.guis["left"].size + else: + xpos = 0 + + if self.guis["top"]: + ypos = self.guis["top"].size + else: + ypos = 0 + + return xpos, ypos, width, height + + def _reset_viewports(self): + # TODO: think about moving this to Figure later, + # maybe also refactor Subplot and PlotArea so that + # the resize event is handled at the Figure level instead + for subplot in self: + subplot.set_viewport_rect() + for dock in subplot.docks.values(): + dock.set_viewport_rect() + + def register_popup(self, popup: Popup.__class__): + """ + Register a popup class. Note that this takes the class, not an instance + + Parameters + ---------- + popup: Popup subclass + + """ + self._popups[popup.name] = popup(self) + + def open_popup(self, name: str, pos: tuple[int, int], **kwargs): + """ + Open a registered popup + + Parameters + ---------- + name: str + The registered name of the popup + + pos: int, int + x_pos, y_pos for the popup + + kwargs + any additional kwargs to pass to the Popup's open() method + + """ + + if self._popups[name].is_open: + return + + self._popups[name].open(pos, **kwargs) diff --git a/fastplotlib/layouts/_plot_area.py b/fastplotlib/layouts/_plot_area.py index 133526115..5dc415ad7 100644 --- a/fastplotlib/layouts/_plot_area.py +++ b/fastplotlib/layouts/_plot_area.py @@ -34,6 +34,7 @@ def __init__( scene: pygfx.Scene, canvas: WgpuCanvasBase, renderer: pygfx.WgpuRenderer, + extra_renderers: dict = None, name: str = None, ): """ @@ -122,6 +123,19 @@ def __init__( self.set_viewport_rect() + def get_figure(self, obj=None): + """Get Figure instance that contains this plot area""" + if obj is None: + obj = self + + if obj.parent.__class__.__name__.endswith("Figure"): + return obj.parent + else: + if obj.parent is None: + raise RecursionError + + return self.get_figure(obj=obj.parent) + # several read-only properties @property def parent(self): @@ -214,7 +228,7 @@ def controller(self, new_controller: str | pygfx.Controller): # TODO: monkeypatch until we figure out a better # pygfx plans on refactoring viewports anyways if self.parent is not None: - if self.parent.__class__.__name__ == "Figure": + if self.parent.__class__.__name__.endswith("Figure"): for subplot in self.parent: if subplot.camera in cameras_list: new_controller.register_events(subplot.viewport) diff --git a/fastplotlib/layouts/_subplot.py b/fastplotlib/layouts/_subplot.py index 293cea00c..9c3b174a9 100644 --- a/fastplotlib/layouts/_subplot.py +++ b/fastplotlib/layouts/_subplot.py @@ -7,25 +7,25 @@ from wgpu.gui import WgpuCanvasBase from ..graphics import TextGraphic -from ._utils import make_canvas_and_renderer, create_camera, create_controller +from ._utils import create_camera, create_controller from ._plot_area import PlotArea from ._graphic_methods_mixin import GraphicMethodsMixin from ..graphics._axes import Axes +# number of pixels taken by the imgui toolbar when present +IMGUI_TOOLBAR_HEIGHT = 39 + + class Subplot(PlotArea, GraphicMethodsMixin): def __init__( self, - parent: Union["Figure", None] = None, - position: tuple[int, int] = None, - parent_dims: tuple[int, int] = None, - camera: Literal["2d", "3d"] | pygfx.PerspectiveCamera = "2d", - controller: ( - Literal["panzoom", "fly", "trackball", "orbit"] | pygfx.Controller - ) = None, - canvas: ( - Literal["glfw", "jupyter", "qt", "wx"] | WgpuCanvasBase | pygfx.Texture - ) = None, + parent: Union["Figure"], + position: tuple[int, int], + parent_dims: tuple[int, int], + camera: Literal["2d", "3d"] | pygfx.PerspectiveCamera, + controller: pygfx.Controller, + canvas: WgpuCanvasBase | pygfx.Texture, renderer: pygfx.WgpuRenderer = None, name: str = None, ): @@ -56,12 +56,10 @@ def __init__( | if ``str``, must be one of: `"panzoom", "fly", "trackball", or "orbit"`. | also accepts a pygfx.Controller instance - canvas: one of "jupyter", "glfw", "qt", "ex, a WgpuCanvas, or a pygfx.Texture, optional - Provides surface on which a scene will be rendered. Can optionally provide a WgpuCanvas instance or a str - to force the PlotArea to use a specific canvas from one of the following options: "jupyter", "glfw", "qt". - Can also provide a pygfx Texture to render to. + canvas: WgpuCanvas, or a pygfx.Texture + Provides surface on which a scene will be rendered. - renderer: WgpuRenderer, optional + renderer: WgpuRenderer object used to render scenes using wgpu name: str, optional @@ -71,8 +69,6 @@ def __init__( super(GraphicMethodsMixin, self).__init__() - canvas, renderer = make_canvas_and_renderer(canvas, renderer) - if position is None: position = (0, 0) @@ -91,6 +87,8 @@ def __init__( self._title_graphic: TextGraphic = None + self._toolbar = True + super(Subplot, self).__init__( parent=parent, position=position, @@ -142,6 +140,16 @@ def docks(self) -> dict: """ return self._docks + @property + def toolbar(self) -> bool: + """show/hide toolbar""" + return self._toolbar + + @toolbar.setter + def toolbar(self, visible: bool): + self._toolbar = bool(visible) + self.set_viewport_rect() + def render(self): self.axes.update_using_camera() super().render() @@ -172,19 +180,44 @@ def center_title(self): self.docks["top"].center_graphic(self._title_graphic, zoom=1.5) self._title_graphic.world_object.position_y = -3.5 - def get_rect(self): - """Returns the bounding box that defines the Subplot within the canvas.""" + def get_rect(self) -> np.ndarray: + """ + Returns the bounding box that defines the Subplot within the canvas. + + Returns + ------- + np.ndarray + x_position, y_position, width, height + + """ row_ix, col_ix = self.position - width_canvas, height_canvas = self.canvas.get_logical_size() + + x_start_render, y_start_render, width_canvas_render, height_canvas_render = ( + self.parent.get_pygfx_render_area() + ) x_pos = ( - (width_canvas / self.ncols) + ((col_ix - 1) * (width_canvas / self.ncols)) - ) + self.spacing + ( + (width_canvas_render / self.ncols) + + ((col_ix - 1) * (width_canvas_render / self.ncols)) + ) + + self.spacing + + x_start_render + ) y_pos = ( - (height_canvas / self.nrows) + ((row_ix - 1) * (height_canvas / self.nrows)) - ) + self.spacing - width_subplot = (width_canvas / self.ncols) - self.spacing - height_subplot = (height_canvas / self.nrows) - self.spacing + ( + (height_canvas_render / self.nrows) + + ((row_ix - 1) * (height_canvas_render / self.nrows)) + ) + + self.spacing + + y_start_render + ) + width_subplot = (width_canvas_render / self.ncols) - self.spacing + height_subplot = (height_canvas_render / self.nrows) - self.spacing + + if self.parent.__class__.__name__ == "ImguiFigure" and self.toolbar: + # leave space for imgui toolbar + height_subplot -= IMGUI_TOOLBAR_HEIGHT rect = np.array([x_pos, y_pos, width_subplot, height_subplot]) @@ -232,73 +265,93 @@ def size(self, s: int): self.set_viewport_rect() def get_rect(self, *args): + """ + Returns the bounding box that defines this dock area within the canvas. + + Returns + ------- + np.ndarray + x_position, y_position, width, height + """ if self.size == 0: self.viewport.rect = None return row_ix_parent, col_ix_parent = self.parent.position - width_canvas, height_canvas = self.parent.renderer.logical_size + + x_start_render, y_start_render, width_render_canvas, height_render_canvas = ( + self.parent.parent.get_pygfx_render_area() + ) spacing = 2 # spacing in pixels if self.position == "right": x_pos = ( - (width_canvas / self.parent.ncols) - + ((col_ix_parent - 1) * (width_canvas / self.parent.ncols)) - + (width_canvas / self.parent.ncols) + (width_render_canvas / self.parent.ncols) + + ((col_ix_parent - 1) * (width_render_canvas / self.parent.ncols)) + + (width_render_canvas / self.parent.ncols) - self.size ) y_pos = ( - (height_canvas / self.parent.nrows) - + ((row_ix_parent - 1) * (height_canvas / self.parent.nrows)) + (height_render_canvas / self.parent.nrows) + + ((row_ix_parent - 1) * (height_render_canvas / self.parent.nrows)) ) + spacing width_viewport = self.size - height_viewport = (height_canvas / self.parent.nrows) - spacing + height_viewport = (height_render_canvas / self.parent.nrows) - spacing elif self.position == "left": - x_pos = (width_canvas / self.parent.ncols) + ( - (col_ix_parent - 1) * (width_canvas / self.parent.ncols) + x_pos = (width_render_canvas / self.parent.ncols) + ( + (col_ix_parent - 1) * (width_render_canvas / self.parent.ncols) ) y_pos = ( - (height_canvas / self.parent.nrows) - + ((row_ix_parent - 1) * (height_canvas / self.parent.nrows)) + (height_render_canvas / self.parent.nrows) + + ((row_ix_parent - 1) * (height_render_canvas / self.parent.nrows)) ) + spacing width_viewport = self.size - height_viewport = (height_canvas / self.parent.nrows) - spacing + height_viewport = (height_render_canvas / self.parent.nrows) - spacing elif self.position == "top": x_pos = ( - (width_canvas / self.parent.ncols) - + ((col_ix_parent - 1) * (width_canvas / self.parent.ncols)) + (width_render_canvas / self.parent.ncols) + + ((col_ix_parent - 1) * (width_render_canvas / self.parent.ncols)) + spacing ) y_pos = ( - (height_canvas / self.parent.nrows) - + ((row_ix_parent - 1) * (height_canvas / self.parent.nrows)) + (height_render_canvas / self.parent.nrows) + + ((row_ix_parent - 1) * (height_render_canvas / self.parent.nrows)) ) + spacing - width_viewport = (width_canvas / self.parent.ncols) - spacing + width_viewport = (width_render_canvas / self.parent.ncols) - spacing height_viewport = self.size elif self.position == "bottom": x_pos = ( - (width_canvas / self.parent.ncols) - + ((col_ix_parent - 1) * (width_canvas / self.parent.ncols)) + (width_render_canvas / self.parent.ncols) + + ((col_ix_parent - 1) * (width_render_canvas / self.parent.ncols)) + spacing ) y_pos = ( ( - (height_canvas / self.parent.nrows) - + ((row_ix_parent - 1) * (height_canvas / self.parent.nrows)) + (height_render_canvas / self.parent.nrows) + + ((row_ix_parent - 1) * (height_render_canvas / self.parent.nrows)) ) - + (height_canvas / self.parent.nrows) + + (height_render_canvas / self.parent.nrows) - self.size ) - width_viewport = (width_canvas / self.parent.ncols) - spacing + width_viewport = (width_render_canvas / self.parent.ncols) - spacing height_viewport = self.size else: raise ValueError("invalid position") - return [x_pos, y_pos, width_viewport, height_viewport] + if self.parent.__class__.__name__ == "ImguiFigure" and self.parent.toolbar: + # leave space for imgui toolbar + height_viewport -= IMGUI_TOOLBAR_HEIGHT + + return [ + x_pos + x_start_render, + y_pos + y_start_render, + width_viewport, + height_viewport, + ] def get_parent_rect_adjust(self): if self.position == "right": diff --git a/fastplotlib/layouts/_utils.py b/fastplotlib/layouts/_utils.py index 85c35532c..ea44f6950 100644 --- a/fastplotlib/layouts/_utils.py +++ b/fastplotlib/layouts/_utils.py @@ -2,13 +2,45 @@ import pygfx from pygfx import WgpuRenderer, Texture, Renderer +from pygfx.renderers.wgpu.engine.renderer import ( + EVENT_TYPE_MAP, + PointerEvent, + WheelEvent, +) + from wgpu.gui import WgpuCanvasBase from ..utils import gui +# temporary until https://github.com/pygfx/pygfx/issues/495 +class WgpuRendererWithEventFilters(WgpuRenderer): + def __init__(self, target, *args, **kwargs): + super().__init__(target, *args, **kwargs) + self._event_filters = {} + + def convert_event(self, event: dict): + event_type = event["event_type"] + + if EVENT_TYPE_MAP[event_type] in [PointerEvent, WheelEvent]: + for filt in self.event_filters.values(): + if ( + filt[0, 0] < event["x"] < filt[1, 0] + and filt[0, 1] < event["y"] < filt[1, 1] + ): + return + + super().convert_event(event) + + @property + def event_filters(self) -> dict: + return self._event_filters + + def make_canvas_and_renderer( - canvas: str | WgpuCanvasBase | Texture | None, renderer: Renderer | None + canvas: str | WgpuCanvasBase | Texture | None, + renderer: Renderer | None, + canvas_kwargs: dict, ): """ Parses arguments and returns the appropriate canvas and renderer instances @@ -16,10 +48,10 @@ def make_canvas_and_renderer( """ if canvas is None: - canvas = gui.WgpuCanvas(max_fps=60) + canvas = gui.WgpuCanvas(max_fps=60, **canvas_kwargs) elif isinstance(canvas, str): m = importlib.import_module("wgpu.gui." + canvas) - canvas = m.WgpuCanvas(max_fps=60) + canvas = m.WgpuCanvas(max_fps=60, **canvas_kwargs) elif not isinstance(canvas, (WgpuCanvasBase, Texture)): raise TypeError( f"canvas option must either be a valid WgpuCanvas implementation, a pygfx Texture" @@ -27,7 +59,7 @@ def make_canvas_and_renderer( ) if renderer is None: - renderer = WgpuRenderer(canvas) + renderer = WgpuRendererWithEventFilters(canvas) elif not isinstance(renderer, Renderer): raise TypeError( f"renderer option must be a pygfx.Renderer instance such as pygfx.WgpuRenderer" diff --git a/fastplotlib/layouts/output/__init__.py b/fastplotlib/layouts/output/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/fastplotlib/layouts/output/_ipywidget_toolbar.py b/fastplotlib/layouts/output/_ipywidget_toolbar.py deleted file mode 100644 index 787c8d442..000000000 --- a/fastplotlib/layouts/output/_ipywidget_toolbar.py +++ /dev/null @@ -1,202 +0,0 @@ -import traceback -from datetime import datetime -from itertools import product -from math import copysign -from pathlib import Path - -from ipywidgets.widgets import ( - HBox, - ToggleButton, - Dropdown, - Layout, - Button, - Image, -) - -from ...graphics.selectors import PolygonSelector -from ._toolbar import ToolBar -from ...utils import config - - -class IpywidgetToolBar(HBox, ToolBar): - """Basic toolbar using ipywidgets""" - - def __init__(self, figure): - ToolBar.__init__(self, figure) - - self._auto_scale_button = Button( - value=False, - disabled=False, - icon="expand-arrows-alt", - layout=Layout(width="auto"), - tooltip="auto-scale scene", - ) - self._center_scene_button = Button( - value=False, - disabled=False, - icon="align-center", - layout=Layout(width="auto"), - tooltip="auto-center scene", - ) - self._panzoom_controller_button = ToggleButton( - value=True, - disabled=False, - icon="hand-pointer", - layout=Layout(width="auto"), - tooltip="panzoom controller", - ) - self._maintain_aspect_button = ToggleButton( - value=True, - disabled=False, - description="1:1", - layout=Layout(width="auto"), - tooltip="maintain aspect", - ) - self._maintain_aspect_button.style.font_weight = "bold" - - self._y_direction_button = Button( - value=False, - disabled=False, - icon="arrow-up", - layout=Layout(width="auto"), - tooltip="y-axis direction", - ) - - self._record_button = ToggleButton( - value=False, - disabled=False, - icon="video", - layout=Layout(width="auto"), - tooltip="record", - ) - - self._add_polygon_button = Button( - value=False, - disabled=False, - icon="draw-polygon", - layout=Layout(width="auto"), - tooltip="add PolygonSelector", - ) - - widgets = [ - self._auto_scale_button, - self._center_scene_button, - self._panzoom_controller_button, - self._maintain_aspect_button, - self._y_direction_button, - self._add_polygon_button, - self._record_button, - ] - - if config.party_parrot: - gif_path = Path(__file__).parent.parent.parent.joinpath("assets", "egg.gif") - with open(gif_path, "rb") as f: - gif = f.read() - - image = Image( - value=gif, - format="png", - width=35, - height=25, - ) - widgets.append(image) - - positions = list( - product(range(self.figure.shape[0]), range(self.figure.shape[1])) - ) - values = list() - for pos in positions: - if self.figure[pos].name is not None: - values.append(self.figure[pos].name) - else: - values.append(str(pos)) - - self._dropdown = Dropdown( - options=values, - disabled=False, - description="Subplots:", - layout=Layout(width="200px"), - ) - - self.figure.renderer.add_event_handler(self.update_current_subplot, "click") - - widgets.append(self._dropdown) - - self._panzoom_controller_button.observe(self.panzoom_handler, "value") - self._auto_scale_button.on_click(self.auto_scale_handler) - self._center_scene_button.on_click(self.center_scene_handler) - self._maintain_aspect_button.observe(self.maintain_aspect_handler, "value") - self._y_direction_button.on_click(self.y_direction_handler) - self._add_polygon_button.on_click(self.add_polygon) - self._record_button.observe(self.record_plot, "value") - - # set initial values for some buttons - self._maintain_aspect_button.value = self.current_subplot.camera.maintain_aspect - - if copysign(1, self.current_subplot.camera.local.scale_y) == -1: - self._y_direction_button.icon = "arrow-down" - else: - self._y_direction_button.icon = "arrow-up" - - super().__init__(widgets) - - def _get_subplot_dropdown_value(self) -> str: - return self._dropdown.value - - def auto_scale_handler(self, obj): - self.current_subplot.auto_scale( - maintain_aspect=self.current_subplot.camera.maintain_aspect - ) - - def center_scene_handler(self, obj): - self.current_subplot.center_scene() - - def panzoom_handler(self, obj): - self.current_subplot.controller.enabled = self._panzoom_controller_button.value - - def maintain_aspect_handler(self, obj): - for camera in self.current_subplot.controller.cameras: - camera.maintain_aspect = self._maintain_aspect_button.value - - def y_direction_handler(self, obj): - # flip every camera under the same controller - for camera in self.current_subplot.controller.cameras: - camera.local.scale_y *= -1 - - if copysign(1, self.current_subplot.camera.local.scale_y) == -1: - self._y_direction_button.icon = "arrow-down" - else: - self._y_direction_button.icon = "arrow-up" - - def update_current_subplot(self, ev): - for subplot in self.figure: - pos = subplot.map_screen_to_world((ev.x, ev.y)) - if pos is not None: - # update self.dropdown - if subplot.name is None: - self._dropdown.value = str(subplot.position) - else: - self._dropdown.value = subplot.name - self._panzoom_controller_button.value = subplot.controller.enabled - self._maintain_aspect_button.value = subplot.camera.maintain_aspect - - if copysign(1, subplot.camera.local.scale_y) == -1: - self._y_direction_button.icon = "arrow-down" - else: - self._y_direction_button.icon = "arrow-up" - - def record_plot(self, obj): - if self._record_button.value: - try: - self.figure.recorder.start( - f"./{datetime.now().isoformat(timespec='seconds').replace(':', '_')}.mp4" - ) - except Exception: - traceback.print_exc() - self._record_button.value = False - else: - self.figure.recorder.stop() - - def add_polygon(self, obj): - ps = PolygonSelector(edge_width=3, edge_color="magenta") - self.current_subplot.add_graphic(ps, center=False) diff --git a/fastplotlib/layouts/output/_qt_toolbar.py b/fastplotlib/layouts/output/_qt_toolbar.py deleted file mode 100644 index 4334f1369..000000000 --- a/fastplotlib/layouts/output/_qt_toolbar.py +++ /dev/null @@ -1,125 +0,0 @@ -from datetime import datetime -from math import copysign -import traceback - -from ...utils.gui import QtWidgets -from ...graphics.selectors import PolygonSelector -from ._toolbar import ToolBar -from ._qtoolbar_template import Ui_QToolbar - - -class QToolbar( - ToolBar, QtWidgets.QWidget -): # inheritance order MUST be Toolbar first, QWidget second! Else breaks - """Toolbar for Qt context""" - - def __init__(self, output_context, figure): - QtWidgets.QWidget.__init__(self, parent=output_context) - ToolBar.__init__(self, figure) - - # initialize UI - self.ui = Ui_QToolbar() - self.ui.setupUi(self) - - # connect button events - self.ui.auto_scale_button.clicked.connect(self.auto_scale_handler) - self.ui.center_button.clicked.connect(self.center_scene_handler) - self.ui.panzoom_button.toggled.connect(self.panzoom_handler) - self.ui.maintain_aspect_button.toggled.connect(self.maintain_aspect_handler) - self.ui.y_direction_button.clicked.connect(self.y_direction_handler) - - # subplot labels update when a user click on subplots - subplot = self.figure[0, 0] - # set label from first subplot name - if subplot.name is not None: - name = subplot.name - else: - name = str(subplot.position) - - # here we will just use a simple label, not a dropdown like ipywidgets - # the dropdown implementation is tedious with Qt - self.ui.current_subplot = QtWidgets.QLabel(parent=self) - self.ui.current_subplot.setText(name) - self.ui.horizontalLayout.addWidget(self.ui.current_subplot) - - # update the subplot label when a subplot is clicked into - self.figure.renderer.add_event_handler(self.update_current_subplot, "click") - - self.setMaximumHeight(35) - - # set the initial values for buttons - self.ui.maintain_aspect_button.setChecked( - self.current_subplot.camera.maintain_aspect - ) - self.ui.panzoom_button.setChecked(self.current_subplot.controller.enabled) - - if copysign(1, self.current_subplot.camera.local.scale_y) == -1: - self.ui.y_direction_button.setText("v") - else: - self.ui.y_direction_button.setText("^") - - def update_current_subplot(self, ev): - """update the text label for the current subplot""" - for subplot in self.figure: - pos = subplot.map_screen_to_world((ev.x, ev.y)) - if pos is not None: - if subplot.name is not None: - name = subplot.name - else: - name = str(subplot.position) - self.ui.current_subplot.setText(name) - - # set buttons w.r.t. current subplot - self.ui.panzoom_button.setChecked(subplot.controller.enabled) - self.ui.maintain_aspect_button.setChecked( - subplot.camera.maintain_aspect - ) - - if copysign(1, subplot.camera.local.scale_y) == -1: - self.ui.y_direction_button.setText("v") - else: - self.ui.y_direction_button.setText("^") - - def _get_subplot_dropdown_value(self) -> str: - return self.ui.current_subplot.text() - - def auto_scale_handler(self, *args): - self.current_subplot.auto_scale( - maintain_aspect=self.current_subplot.camera.maintain_aspect - ) - - def center_scene_handler(self, *args): - self.current_subplot.center_scene() - - def panzoom_handler(self, value: bool): - self.current_subplot.controller.enabled = value - - def maintain_aspect_handler(self, value: bool): - for camera in self.current_subplot.controller.cameras: - camera.maintain_aspect = value - - def y_direction_handler(self, *args): - # flip every camera under the same controller - for camera in self.current_subplot.controller.cameras: - camera.local.scale_y *= -1 - - if copysign(1, self.current_subplot.camera.local.scale_y) == -1: - self.ui.y_direction_button.setText("v") - else: - self.ui.y_direction_button.setText("^") - - def record_handler(self, ev): - if self.ui.record_button.isChecked(): - try: - self.figure.record_start( - f"./{datetime.now().isoformat(timespec='seconds').replace(':', '_')}.mp4" - ) - except Exception: - traceback.print_exc() - self.ui.record_button.setChecked(False) - else: - self.figure.record_stop() - - def add_polygon(self, *args): - ps = PolygonSelector(edge_width=3, edge_color="mageneta") - self.current_subplot.add_graphic(ps, center=False) diff --git a/fastplotlib/layouts/output/_qtoolbar_template.py b/fastplotlib/layouts/output/_qtoolbar_template.py deleted file mode 100644 index d2311c595..000000000 --- a/fastplotlib/layouts/output/_qtoolbar_template.py +++ /dev/null @@ -1,61 +0,0 @@ -# Form implementation generated from reading ui file 'qtoolbar.ui' -# -# Created by: PyQt6 UI code generator 6.5.3 -# -# WARNING: Any manual changes made to this file will be lost when pyuic6 is -# run again. Do not edit this file unless you know what you are doing. - -from ...utils.gui import QtGui, QtCore, QtWidgets - - -class Ui_QToolbar(object): - def setupUi(self, QToolbar): - QToolbar.setObjectName("QToolbar") - QToolbar.resize(638, 48) - self.horizontalLayout_2 = QtWidgets.QHBoxLayout(QToolbar) - self.horizontalLayout_2.setObjectName("horizontalLayout_2") - self.horizontalLayout = QtWidgets.QHBoxLayout() - self.horizontalLayout.setObjectName("horizontalLayout") - self.auto_scale_button = QtWidgets.QPushButton(parent=QToolbar) - self.auto_scale_button.setObjectName("auto_scale_button") - self.horizontalLayout.addWidget(self.auto_scale_button) - self.center_button = QtWidgets.QPushButton(parent=QToolbar) - self.center_button.setObjectName("center_button") - self.horizontalLayout.addWidget(self.center_button) - self.panzoom_button = QtWidgets.QPushButton(parent=QToolbar) - self.panzoom_button.setCheckable(True) - self.panzoom_button.setObjectName("panzoom_button") - self.horizontalLayout.addWidget(self.panzoom_button) - self.maintain_aspect_button = QtWidgets.QPushButton(parent=QToolbar) - font = QtGui.QFont() - font.setBold(True) - font.setWeight(QtGui.QFont.Weight.Bold) - self.maintain_aspect_button.setFont(font) - self.maintain_aspect_button.setCheckable(True) - self.maintain_aspect_button.setObjectName("maintain_aspect_button") - self.horizontalLayout.addWidget(self.maintain_aspect_button) - self.y_direction_button = QtWidgets.QPushButton(parent=QToolbar) - self.y_direction_button.setObjectName("y_direction_button") - self.horizontalLayout.addWidget(self.y_direction_button) - self.add_polygon_button = QtWidgets.QPushButton(parent=QToolbar) - self.add_polygon_button.setObjectName("add_polygon_button") - self.horizontalLayout.addWidget(self.add_polygon_button) - self.record_button = QtWidgets.QPushButton(parent=QToolbar) - self.record_button.setCheckable(True) - self.record_button.setObjectName("record_button") - self.horizontalLayout.addWidget(self.record_button) - self.horizontalLayout_2.addLayout(self.horizontalLayout) - - self.retranslateUi(QToolbar) - QtCore.QMetaObject.connectSlotsByName(QToolbar) - - def retranslateUi(self, QToolbar): - _translate = QtCore.QCoreApplication.translate - QToolbar.setWindowTitle(_translate("QToolbar", "Form")) - self.auto_scale_button.setText(_translate("QToolbar", "autoscale")) - self.center_button.setText(_translate("QToolbar", "center")) - self.panzoom_button.setText(_translate("QToolbar", "panzoom")) - self.maintain_aspect_button.setText(_translate("QToolbar", "1:1")) - self.y_direction_button.setText(_translate("QToolbar", "^")) - self.add_polygon_button.setText(_translate("QToolbar", "polygon")) - self.record_button.setText(_translate("QToolbar", "record")) diff --git a/fastplotlib/layouts/output/_toolbar.py b/fastplotlib/layouts/output/_toolbar.py deleted file mode 100644 index 5edd201fa..000000000 --- a/fastplotlib/layouts/output/_toolbar.py +++ /dev/null @@ -1,45 +0,0 @@ -from .._subplot import Subplot - - -class ToolBar: - def __init__(self, figure): - self.figure = figure - - def _get_subplot_dropdown_value(self) -> str: - raise NotImplemented - - @property - def current_subplot(self) -> Subplot: - """Returns current subplot""" - if hasattr(self.figure, "_subplots"): - # parses dropdown or label value as plot name or position - current = self._get_subplot_dropdown_value() - if current[0] == "(": - # str representation of int tuple to tuple of int - current = tuple(int(i) for i in current.strip("()").split(",")) - return self.figure[current] - else: - return self.figure[current] - else: - return self.figure - - def panzoom_handler(self, ev): - raise NotImplemented - - def maintain_aspect_handler(self, ev): - raise NotImplemented - - def y_direction_handler(self, ev): - raise NotImplemented - - def auto_scale_handler(self, ev): - raise NotImplemented - - def center_scene_handler(self, ev): - raise NotImplemented - - def record_handler(self, ev): - raise NotImplemented - - def add_polygon(self, ev): - raise NotImplemented diff --git a/fastplotlib/layouts/output/jupyter_output.py b/fastplotlib/layouts/output/jupyter_output.py deleted file mode 100644 index 9ebf0941d..000000000 --- a/fastplotlib/layouts/output/jupyter_output.py +++ /dev/null @@ -1,83 +0,0 @@ -from ipywidgets import VBox, Widget -from sidecar import Sidecar -from IPython.display import display - -from ._ipywidget_toolbar import IpywidgetToolBar - - -class JupyterOutputContext(VBox): - """ - Output context to display plots in jupyter. Inherits from ipywidgets.VBox - - Basically vstacks plot canvas, toolbar, and other widgets. Uses sidecar if desired. - """ - - def __init__( - self, - frame, - make_toolbar: bool, - use_sidecar: bool, - sidecar_kwargs: dict, - add_widgets: list[Widget], - ): - """ - - Parameters - ---------- - frame: - Plot frame for which to generate the output context - - sidecar_kwargs: dict - optional kwargs passed to Sidecar - - add_widgets: List[Widget] - list of ipywidgets to stack below the plot and toolbar - """ - self.frame = frame - self.toolbar = None - self.sidecar = None - - # verify they are all valid ipywidgets - if False in [isinstance(w, Widget) for w in add_widgets]: - raise TypeError( - f"add_widgets must be list of ipywidgets, you have passed:\n{add_widgets}" - ) - - self.use_sidecar = use_sidecar - - if not make_toolbar: # just stack canvas and the additional widgets, if any - self.output = (frame.canvas, *add_widgets) - - if make_toolbar: # make toolbar and stack canvas, toolbar, add_widgets - self.toolbar = IpywidgetToolBar(frame) - self.output = (frame.canvas, self.toolbar, *add_widgets) - - if use_sidecar: # instantiate sidecar if desired - self.sidecar = Sidecar(**sidecar_kwargs) - - # stack all of these in the VBox - super().__init__(self.output) - - def _repr_mimebundle_(self, *args, **kwargs): - """ - This is what jupyter hook into when this output context instance is returned at the end of a cell. - """ - if self.use_sidecar: - with self.sidecar: - # TODO: prints all the child widgets in the cell output, will figure out later, sidecar output works - return display(VBox(self.output)) - else: - # just display VBox contents in cell output - return super()._repr_mimebundle_(*args, **kwargs) - - def close(self): - """Closes the output context, cleanup all the stuff""" - self.frame.canvas.close() - - if self.toolbar is not None: - self.toolbar.close() - - if self.sidecar is not None: - self.sidecar.close() - - super().close() # ipywidget VBox cleanup diff --git a/fastplotlib/layouts/output/qt_output.py b/fastplotlib/layouts/output/qt_output.py deleted file mode 100644 index 20aaef2d1..000000000 --- a/fastplotlib/layouts/output/qt_output.py +++ /dev/null @@ -1,57 +0,0 @@ -from ...utils.gui import QtWidgets -from ._qt_toolbar import QToolbar - - -class QOutputContext(QtWidgets.QWidget): - """ - Output context to display plots in Qt apps. Inherits from QtWidgets.QWidget - - Basically vstacks plot canvas, toolbar, and other widgets. - """ - - def __init__( - self, - frame, - make_toolbar, - add_widgets, - ): - """ - - Parameters - ---------- - frame: - Plot frame for which to generate the output context - - add_widgets: List[Widget] - list of QWidget to stack below the plot and toolbar - """ - # no parent, user can use Plot.widget.setParent(parent) if necessary to embed into other widgets - QtWidgets.QWidget.__init__(self, parent=None) - self.frame = frame - self.toolbar = None - - # vertical layout used to stack plot canvas, toolbar, and add_widgets - self.vlayout = QtWidgets.QVBoxLayout(self) - - # add canvas to layout - self.vlayout.addWidget(self.frame.canvas) - - if make_toolbar: # make toolbar and add to layout - self.toolbar = QToolbar(output_context=self, figure=frame) - self.vlayout.addWidget(self.toolbar) - - for w in add_widgets: # add any additional widgets to layout - w.setParent(self) - self.vlayout.addWidget(w) - - self.setLayout(self.vlayout) - - self.resize(*self.frame._starting_size) - - self.show() - - def close(self): - """Cleanup and close the output context""" - self.frame.canvas.close() - self.toolbar.close() - super().close() # QWidget cleanup diff --git a/fastplotlib/layouts/output/qtoolbar.ui b/fastplotlib/layouts/output/qtoolbar.ui deleted file mode 100644 index 6c9aadae8..000000000 --- a/fastplotlib/layouts/output/qtoolbar.ui +++ /dev/null @@ -1,89 +0,0 @@ - - - QToolbar - - - - 0 - 0 - 638 - 48 - - - - Form - - - - - - - - autoscale - - - - - - - center - - - - - - - panzoom - - - true - - - - - - - - 75 - true - - - - 1:1 - - - true - - - - - - - ^ - - - - - - - polygon - - - - - - - record - - - true - - - - - - - - - - diff --git a/fastplotlib/tools/__init__.py b/fastplotlib/tools/__init__.py new file mode 100644 index 000000000..80396c98d --- /dev/null +++ b/fastplotlib/tools/__init__.py @@ -0,0 +1 @@ +from ._histogram_lut import HistogramLUTTool diff --git a/fastplotlib/widgets/histogram_lut.py b/fastplotlib/tools/_histogram_lut.py similarity index 95% rename from fastplotlib/widgets/histogram_lut.py rename to fastplotlib/tools/_histogram_lut.py index 0f63eb8f4..b8c6633a8 100644 --- a/fastplotlib/widgets/histogram_lut.py +++ b/fastplotlib/tools/_histogram_lut.py @@ -24,7 +24,7 @@ def _get_image_graphic_events(image_graphic: ImageGraphic) -> list[str]: # TODO: This is a widget, we can think about a BaseWidget class later if necessary -class HistogramLUT(Graphic): +class HistogramLUTTool(Graphic): def __init__( self, data: np.ndarray, @@ -136,6 +136,7 @@ def __init__( # colorbar for grayscale images if self.image_graphic.data.value.ndim != 3: self._colorbar: ImageGraphic = self._make_colorbar(edges_flanked) + self._colorbar.add_event_handler(self._open_cmap_picker, "click") self.world_object.add(self._colorbar.world_object) else: @@ -349,10 +350,12 @@ def set_data(self, data, reset_vmin_vmax: bool = True): self._data = weakref.proxy(data) if self._colorbar is not None: + self._colorbar.clear_event_handlers() self.world_object.remove(self._colorbar.world_object) if self.image_graphic.data.value.ndim != 3: self._colorbar: ImageGraphic = self._make_colorbar(edges_flanked) + self._colorbar.add_event_handler(self._open_cmap_picker, "click") self.world_object.add(self._colorbar.world_object) else: @@ -370,7 +373,7 @@ def image_graphic(self) -> ImageGraphic: def image_graphic(self, graphic): if not isinstance(graphic, ImageGraphic): raise TypeError( - f"HistogramLUT can only use ImageGraphic types, you have passed: {type(graphic)}" + f"HistogramLUTTool can only use ImageGraphic types, you have passed: {type(graphic)}" ) if self._image_graphic is not None: @@ -392,6 +395,15 @@ def disconnect_image_graphic(self): del self._image_graphic # self._image_graphic = None + def _open_cmap_picker(self, ev): + # check if right click + if ev.button != 2: + return + + pos = ev.x, ev.y + + self._plot_area.get_figure().open_popup("colormap-picker", pos, lut_tool=self) + def _fpl_prepare_del(self): self._linear_region_selector._fpl_prepare_del() self._histogram_line._fpl_prepare_del() diff --git a/fastplotlib/ui/__init__.py b/fastplotlib/ui/__init__.py new file mode 100644 index 000000000..a1e57a9c5 --- /dev/null +++ b/fastplotlib/ui/__init__.py @@ -0,0 +1,3 @@ +from ._base import BaseGUI, Window, EdgeWindow, Popup, GUI_EDGES +from ._subplot_toolbar import SubplotToolbar +from .right_click_menus import StandardRightClickMenu, ColormapPicker diff --git a/fastplotlib/ui/_base.py b/fastplotlib/ui/_base.py new file mode 100644 index 000000000..4ca9fbeca --- /dev/null +++ b/fastplotlib/ui/_base.py @@ -0,0 +1,282 @@ +from typing import Literal +import numpy as np + +from imgui_bundle import imgui + +from ..layouts._figure import Figure + + +GUI_EDGES = ["top", "right", "bottom", "left"] + + +class BaseGUI: + """ + Base class for all ImGUI based GUIs, windows and popups + + The main purpose of this base is for setting a unique ID between multiple figs with identical UI elements + + This ID can be pushed in subclasses within the `update()` method + """ + + ID_COUNTER: int = 0 + + def __init__(self): + BaseGUI.ID_COUNTER += 1 + self._id_counter = BaseGUI.ID_COUNTER + + def update(self): + """must be implemented in subclass""" + raise NotImplementedError + + +class Window(BaseGUI): + """Base class for imgui windows drawn within Figures""" + + pass + + +class EdgeWindow(Window): + def __init__( + self, + figure: Figure, + size: int, + location: Literal["top", "bottom", "left", "right"], + title: str, + window_flags: int = imgui.WindowFlags_.no_collapse + | imgui.WindowFlags_.no_resize, + *args, + **kwargs, + ): + """ + A base class for imgui windows displayed at one of the four edges of a Figure + + Parameters + ---------- + figure: Figure + Figure instance that this window will be placed in + + size: int + width or height of the window, depending on its location + + location: str, "top" | "bottom" | "left" | "right" + location of the window + + title: str + window title + + window_flags: int + window flag enum, valid flags are: + + .. code-block:: py + + imgui.WindowFlags_.no_title_bar + imgui.WindowFlags_.no_resize + imgui.WindowFlags_.no_move + imgui.WindowFlags_.no_scrollbar + imgui.WindowFlags_.no_scroll_with_mouse + imgui.WindowFlags_.no_collapse + imgui.WindowFlags_.always_auto_resize + imgui.WindowFlags_.no_background + imgui.WindowFlags_.no_saved_settings + imgui.WindowFlags_.no_mouse_inputs + imgui.WindowFlags_.menu_bar + imgui.WindowFlags_.horizontal_scrollbar + imgui.WindowFlags_.no_focus_on_appearing + imgui.WindowFlags_.no_bring_to_front_on_focus + imgui.WindowFlags_.always_vertical_scrollbar + imgui.WindowFlags_.always_horizontal_scrollbar + imgui.WindowFlags_.no_nav_inputs + imgui.WindowFlags_.no_nav_focus + imgui.WindowFlags_.unsaved_document + imgui.WindowFlags_.no_docking + imgui.WindowFlags_.no_nav + imgui.WindowFlags_.no_decoration + imgui.WindowFlags_.no_inputs + + *args + additional args for the GUI + + **kwargs + additional kwargs for teh GUI + """ + super().__init__() + + if location not in GUI_EDGES: + f"GUI does not have a valid location, valid locations are: {GUI_EDGES}, you have passed: {location}" + + self._figure = figure + self._size = size + self._location = location + self._title = title + self._window_flags = window_flags + self._fa_icons = self._figure._fa_icons + + self._x, self._y, self._width, self._height = self.get_rect() + + self._figure.canvas.add_event_handler(self._set_rect, "resize") + + @property + def size(self) -> int | None: + """width or height of the edge window""" + return self._size + + @size.setter + def size(self, value): + if not isinstance(value, int): + raise TypeError + self._size = value + + @property + def location(self) -> str: + """location of the window""" + return self._location + + @property + def x(self) -> int: + """canvas x position of the window""" + return self._x + + @property + def y(self) -> int: + """canvas y position of the window""" + return self._y + + @property + def width(self) -> int: + """with the window""" + return self._width + + @property + def height(self) -> int: + """height of the window""" + return self._height + + def _set_rect(self, *args): + self._x, self._y, self._width, self._height = self.get_rect() + + def get_rect(self) -> tuple[int, int, int, int]: + """ + Compute the rect that defines the area this GUI is drawn to + + Returns + ------- + int, int, int, int + x_pos, y_pos, width, height + + """ + + width_canvas, height_canvas = self._figure.canvas.get_logical_size() + + match self._location: + case "top": + x_pos, y_pos = (0, 0) + width, height = (width_canvas, self.size) + + case "bottom": + x_pos = 0 + y_pos = height_canvas - self.size + width, height = (width_canvas, self.size) + + case "right": + x_pos, y_pos = (width_canvas - self.size, 0) + + if self._figure.guis["top"]: + # if there is a GUI in the top edge, make this one below + y_pos += self._figure.guis["top"].size + + width, height = (self.size, height_canvas) + if self._figure.guis["bottom"] is not None: + height -= self._figure.guis["bottom"].size + + case "left": + x_pos, y_pos = (0, 0) + if self._figure.guis["top"]: + # if there is a GUI in the top edge, make this one below + y_pos += self._figure.guis["top"].size + + width, height = (self.size, height_canvas) + if self._figure.guis["bottom"] is not None: + height -= self._figure.guis["bottom"].size + + return x_pos, y_pos, width, height + + def draw_window(self): + """helps simplify using imgui by managing window creation & position, and pushing/popping the ID""" + # window position & size + imgui.set_next_window_size((self.width, self.height)) + imgui.set_next_window_pos((self.x, self.y)) + flags = self._window_flags + + # begin window + imgui.begin(self._title, p_open=None, flags=flags) + + # push ID to prevent conflict between multiple figs with same UI + imgui.push_id(self._id_counter) + + # draw stuff from subclass into window + self.update() + + # pop ID + imgui.pop_id() + + # end the window + imgui.end() + + def update(self): + """Implement your GUI here and it will be drawn within the window. See the GUI examples""" + raise NotImplementedError + + +class Popup(BaseGUI): + def __init__(self, figure: Figure, *args, **kwargs): + """ + Base class for creating ImGUI popups within Figures + + Parameters + ---------- + figure: Figure + Figure instance + *args + any args to pass to subclass constructor + + **kwargs + any kwargs to pass to subclass constructor + """ + + super().__init__() + + self._figure = figure + self._fa_icons = self._figure._fa_icons + + self._event_filter_names = set() + + self.is_open = False + + def set_event_filter(self, name: str): + """Filter out events under the popup from being handled by pygfx renderer""" + # get popup window position & size + x1, y1 = imgui.get_window_pos() + width, height = imgui.get_window_size() + x2, y2 = x1 + width, y1 + height + + # add or modify event filter + if name not in self._figure.renderer.event_filters.keys(): + self._figure.renderer.event_filters[name] = np.array( + [[x1 - 1, y1 - 1], [x2 + 4, y2 + 4]] + ) + else: + self._figure.renderer.event_filters[name][:] = [x1 - 1, y1 - 1], [ + x2 + 4, + y2 + 4, + ] + + self._event_filter_names.add(name) + + def clear_event_filters(self): + """clear event filters when the popup is not shown""" + for name in self._event_filter_names: + self._figure.renderer.event_filters[name][:] = [-1, -1], [-1, -1] + + def open(self, pos: tuple[int, int], *args, **kwargs): + """implement in subclass""" + raise NotImplementedError diff --git a/fastplotlib/ui/_subplot_toolbar.py b/fastplotlib/ui/_subplot_toolbar.py new file mode 100644 index 000000000..6c1a81f73 --- /dev/null +++ b/fastplotlib/ui/_subplot_toolbar.py @@ -0,0 +1,73 @@ +from imgui_bundle import imgui, icons_fontawesome_6 as fa, imgui_ctx + +from ..layouts._subplot import Subplot +from ._base import Window + + +class SubplotToolbar(Window): + def __init__(self, subplot: Subplot, fa_icons: imgui.ImFont): + """ + Subplot toolbar shown below all subplots + """ + super().__init__() + + self._subplot = subplot + self._fa_icons = fa_icons + + def update(self): + # get subplot rect + x, y, width, height = self._subplot.get_rect() + + # place the toolbar window below the subplot + pos = (x, y + height) + + imgui.set_next_window_size((width, 0)) + imgui.set_next_window_pos(pos) + flags = imgui.WindowFlags_.no_collapse | imgui.WindowFlags_.no_title_bar + + imgui.begin(f"Toolbar-{self._subplot.position}", p_open=None, flags=flags) + + # icons for buttons + imgui.push_font(self._fa_icons) + + # push ID to prevent conflict between multiple figs with same UI + imgui.push_id(self._id_counter) + with imgui_ctx.begin_horizontal(f"toolbar-{self._subplot.position}"): + # autoscale button + if imgui.button(fa.ICON_FA_MAXIMIZE): + self._subplot.auto_scale() + imgui.pop_font() + if imgui.is_item_hovered(0): + imgui.set_tooltip("autoscale scene") + + # center scene + imgui.push_font(self._fa_icons) + if imgui.button(fa.ICON_FA_ALIGN_CENTER): + self._subplot.center_scene() + imgui.pop_font() + if imgui.is_item_hovered(0): + imgui.set_tooltip("center scene") + + imgui.push_font(self._fa_icons) + # checkbox controller + _, self._subplot.controller.enabled = imgui.checkbox( + fa.ICON_FA_COMPUTER_MOUSE, self._subplot.controller.enabled + ) + imgui.pop_font() + if imgui.is_item_hovered(0): + imgui.set_tooltip("enable/disable controller") + + imgui.push_font(self._fa_icons) + # checkbox maintain_apsect + _, self._subplot.camera.maintain_aspect = imgui.checkbox( + fa.ICON_FA_EXPAND, self._subplot.camera.maintain_aspect + ) + imgui.pop_font() + if imgui.is_item_hovered(0): + imgui.set_tooltip("maintain aspect") + + # pop id when all UI has been written to window + imgui.pop_id() + + # end window + imgui.end() diff --git a/fastplotlib/ui/right_click_menus/__init__.py b/fastplotlib/ui/right_click_menus/__init__.py new file mode 100644 index 000000000..6ccc50646 --- /dev/null +++ b/fastplotlib/ui/right_click_menus/__init__.py @@ -0,0 +1,2 @@ +from ._colormap_picker import ColormapPicker +from ._standard_menu import StandardRightClickMenu diff --git a/fastplotlib/ui/right_click_menus/_colormap_picker.py b/fastplotlib/ui/right_click_menus/_colormap_picker.py new file mode 100644 index 000000000..5a14705c7 --- /dev/null +++ b/fastplotlib/ui/right_click_menus/_colormap_picker.py @@ -0,0 +1,180 @@ +import ctypes + +import numpy as np +import cmap + +import wgpu +from imgui_bundle import imgui +from wgpu import GPUTexture + +from .. import Popup +from ...utils.functions import ( + COLORMAP_NAMES, + SEQUENTIAL_CMAPS, + CYCLIC_CMAPS, + DIVERGING_CMAPS, + MISC_CMAPS, +) + +all_cmaps = [*SEQUENTIAL_CMAPS, *CYCLIC_CMAPS, *DIVERGING_CMAPS, *MISC_CMAPS] + + +class ColormapPicker(Popup): + """Colormap picker menu popup tool""" + + # name used to trigger this popup after it has been registered with a Figure + name = "colormap-picker" + + def __init__(self, figure): + super().__init__(figure=figure, fa_icons=None) + + self.renderer = self._figure.renderer + self.imgui_renderer = self._figure.imgui_renderer + + # maps str cmap names -> int texture IDs + self._texture_ids: dict[str, int] = {} + self._textures = list() + + # make all colormaps and upload representative texture for each cmap to the GPU + for name in all_cmaps: + # get data that represents cmap + colormap = cmap.Colormap(name) + data = colormap(np.linspace(0, 1)) * 255 + + # needs to be 2D to create a texture + data = np.vstack([[data]] * 2).astype(np.uint8) + + # upload the texture to the GPU, get the texture ID and texture + self._texture_ids[name], texture = self._create_texture_and_upload(data) + self._textures.append(texture) + + # used to set the states of the UI + self._lut_tool = None + self._pos: tuple[int, int] = -1, -1 + self._open_new: bool = False + + self.is_open = False + + self._popup_state = "never-opened" + + self._texture_height = None + + def _create_texture_and_upload(self, data: np.ndarray) -> tuple[int, GPUTexture]: + """crates a GPUTexture from the 2D data and uploads it""" + + # create a GPUTexture + texture = self.renderer.device.create_texture( + size=(data.shape[1], data.shape[0], 4), + usage=wgpu.TextureUsage.COPY_DST | wgpu.TextureUsage.TEXTURE_BINDING, + dimension=wgpu.TextureDimension.d2, + format=wgpu.TextureFormat.rgba8unorm, + mip_level_count=1, + sample_count=1, + ) + + # upload to the GPU + self.renderer.device.queue.write_texture( + {"texture": texture, "mip_level": 0, "origin": (0, 0, 0)}, + data, + {"offset": 0, "bytes_per_row": data.shape[1] * 4}, + (data.shape[1], data.shape[0], 1), + ) + + # get a view + texture_view = texture.create_view() + + # get the id so that imgui can display it + id_texture = ctypes.c_int32(id(texture_view)).value + # add texture view to the backend so that it can be retrieved for rendering + self.imgui_renderer.backend._texture_views[id_texture] = texture_view + + return id_texture, texture + + def open(self, pos: tuple[int, int], lut_tool): + """ + Request that the popup be opened on the next render cycle + + Parameters + ---------- + pos: int, int + (x, y) position + + lut_tool: HistogramLUTTool + instance of the LUT tool + + Returns + ------- + + """ + self._lut_tool = lut_tool + + self._pos = pos + + self._open_new = True + + def close(self): + """cleanup after popup has closed""" + self._lut_tool = None + self._open_new = False + self._pos = -1, -1 + + self.is_open = False + + self.clear_event_filters() + + def _add_cmap_menu_item(self, cmap_name: str): + texture_id = self._texture_ids[cmap_name] + imgui.image( + texture_id, image_size=(50, self._texture_height), border_col=(1, 1, 1, 1) + ) + + imgui.same_line() + + clicked, selected = imgui.selectable( + label=cmap_name, + p_selected=cmap_name == self._lut_tool.cmap, + ) + + if clicked and selected: + self._lut_tool.cmap = cmap_name + + def update(self): + if self._open_new: + # new popup has been triggered by a LUT tool + self._open_new = False + + imgui.set_next_window_pos(self._pos) + imgui.open_popup("cmap-picker") + + if imgui.begin_popup("cmap-picker"): + self.is_open = True + + # event filter so click events in the menu aren't propagated down to pygfx + self.set_event_filter("cmap-picker-filter") + + # make the cmap image height the same as the text height + self._texture_height = ( + self.imgui_renderer.backend.io.font_global_scale + * imgui.get_font().font_size + ) - 2 + + if imgui.menu_item("Reset vmin-vmax", None, False)[0]: + self._lut_tool.image_graphic.reset_vmin_vmax() + + # add all the cmap options + for cmap_type in COLORMAP_NAMES.keys(): + if cmap_type == "qualitative": + continue + + imgui.separator() + imgui.text(cmap_type.capitalize()) + + for cmap_name in COLORMAP_NAMES[cmap_type]: + self._add_cmap_menu_item(cmap_name) + + imgui.end_popup() + + else: + # popup went from open to closed + if self.is_open == True: + self.close() diff --git a/fastplotlib/ui/right_click_menus/_standard_menu.py b/fastplotlib/ui/right_click_menus/_standard_menu.py new file mode 100644 index 000000000..71e8df632 --- /dev/null +++ b/fastplotlib/ui/right_click_menus/_standard_menu.py @@ -0,0 +1,181 @@ +from imgui_bundle import imgui + +from ...layouts._utils import controller_types +from ...layouts._plot_area import PlotArea +from ...ui import Popup + + +def flip_axis(subplot: PlotArea, axis: str, flip: bool): + camera = subplot.camera + axis_attr = f"scale_{axis}" + scale = getattr(camera.local, axis_attr) + + if flip and scale > 0: + # flip is checked and axis is not already flipped + setattr(camera.local, axis_attr, scale * -1) + + elif not flip and scale < 0: + # flip not checked and axis is flipped + setattr(camera.local, axis_attr, scale * -1) + + +class StandardRightClickMenu(Popup): + """Right click menu that is shown on subplots""" + + def __init__(self, figure, fa_icons): + super().__init__(figure=figure, fa_icons=fa_icons) + + self._last_right_click_pos = None + self._mouse_down: bool = False + + # whether the right click menu is currently open or not + self.is_open: bool = False + + def get_subplot(self) -> PlotArea | bool: + """get the subplot that a click occurred in""" + if self._last_right_click_pos is None: + return False + + for subplot in self._figure: + if subplot.viewport.is_inside(*self._last_right_click_pos): + return subplot + + def cleanup(self): + """called when the popup disappears""" + self.clear_event_filters() + self.is_open = False + + def update(self): + if imgui.is_mouse_down(1) and not self._mouse_down: + # mouse button was pressed down, store this position + self._mouse_down = True + self._last_right_click_pos = imgui.get_mouse_pos() + + if imgui.is_mouse_released(1) and self._mouse_down: + self._mouse_down = False + + # open popup only if mouse was not moved between mouse_down and mouse_up events + if self._last_right_click_pos == imgui.get_mouse_pos(): + if self.get_subplot(): + # open only if right click was inside a subplot + imgui.open_popup(f"right-click-menu") + + # TODO: call this just once when going from open -> closed state + if not imgui.is_popup_open("right-click-menu"): + self.cleanup() + + if imgui.begin_popup(f"right-click-menu"): + # set event filter so event in the popup region are not handled by pygfx.WgpuRenderer + self.set_event_filter("right-click-menu") + + if not self.get_subplot(): + # for some reason it will still trigger at certain locations + # despite open_popup() only being called when an actual + # subplot is returned + imgui.end_popup() + imgui.close_current_popup() + self.cleanup() + return + + name = self.get_subplot().name + if name is None: + name = self.get_subplot().position + + # text label at the top of the menu + imgui.text(f"subplot: {name}") + imgui.separator() + + # autoscale, center, maintain aspect + if imgui.menu_item(f"Autoscale", None, False)[0]: + self.get_subplot().auto_scale() + + if imgui.menu_item(f"Center", None, False)[0]: + self.get_subplot().center_scene() + + _, maintain_aspect = imgui.menu_item( + "Maintain Aspect", None, self.get_subplot().camera.maintain_aspect + ) + self.get_subplot().camera.maintain_aspect = maintain_aspect + + imgui.separator() + + # toggles to flip axes cameras + for axis in ["x", "y", "z"]: + scale = getattr(self.get_subplot().camera.local, f"scale_{axis}") + changed, flip = imgui.menu_item(f"Flip {axis} axis", None, scale < 0) + + if changed: + flip_axis(self.get_subplot(), axis, flip) + + imgui.separator() + + # toggles to show/hide the grid + for plane in ["xy", "xz", "yz"]: + grid = getattr(self.get_subplot().axes.grids, plane) + visible = grid.visible + changed, new_visible = imgui.menu_item(f"Grid {plane}", None, visible) + + if changed: + grid.visible = new_visible + + imgui.separator() + + # camera FOV + changed, fov = imgui.slider_float( + "FOV", v=self.get_subplot().camera.fov, v_min=0.0, v_max=180.0 + ) + + imgui.separator() + + if changed: + # FOV between 0 and 1 is numerically unstable + if 0 < fov < 1: + fov = 1 + + # need to update FOV via controller, if FOV is directly set + # on the camera the controller will immediately set it back + self.get_subplot().controller.update_fov( + fov - self.get_subplot().camera.fov, + animate=False, + ) + + imgui.separator() + + # controller options + if imgui.begin_menu("Controller"): + self.set_event_filter("controller-menu") + _, enabled = imgui.menu_item( + "Enabled", None, self.get_subplot().controller.enabled + ) + + self.get_subplot().controller.enabled = enabled + + changed, damping = imgui.slider_float( + "Damping", + v=self.get_subplot().controller.damping, + v_min=0.0, + v_max=10.0, + ) + + if changed: + self.get_subplot().controller.damping = damping + + imgui.separator() + imgui.text("Controller type:") + # switching between different controllers + for name, controller_type_iter in controller_types.items(): + current_type = type(self.get_subplot().controller) + + clicked, _ = imgui.menu_item( + label=name, + shortcut=None, + p_selected=current_type is controller_type_iter, + ) + + if clicked and (current_type is not controller_type_iter): + # menu item was clicked and the desired controller isn't the current one + self.get_subplot().controller = name + + imgui.end_menu() + + imgui.end_popup() diff --git a/fastplotlib/utils/functions.py b/fastplotlib/utils/functions.py index 64f9a94c3..d93f09da3 100644 --- a/fastplotlib/utils/functions.py +++ b/fastplotlib/utils/functions.py @@ -2,11 +2,54 @@ from typing import * import numpy as np -from cmap import Colormap +import cmap as cmap_lib from pygfx import Texture, Color +cmap_catalog = cmap_lib.Catalog() + +COLORMAPS = cmap_catalog.short_keys() + +SEQUENTIAL_CMAPS = list() +QUALITATIVE_CMAPS = list() +CYCLIC_CMAPS = list() +DIVERGING_CMAPS = list() +MISC_CMAPS = list() + + +for name in COLORMAPS: + _colormap = cmap_lib.Colormap(name) + match _colormap.category: + case "sequential": + if _colormap.interpolation == "nearest": + continue + SEQUENTIAL_CMAPS.append(name) + case "cyclic": + if _colormap.interpolation == "nearest": + continue + CYCLIC_CMAPS.append(name) + case "diverging": + if _colormap.interpolation == "nearest": + continue + DIVERGING_CMAPS.append(name) + case "qualitative": + QUALITATIVE_CMAPS.append(name) + case "miscellaneous": + if _colormap.interpolation == "nearest": + continue + MISC_CMAPS.append(name) + + +COLORMAP_NAMES = { + "sequential": sorted(SEQUENTIAL_CMAPS), + "cyclic": sorted(CYCLIC_CMAPS), + "diverging": sorted(DIVERGING_CMAPS), + "qualitative": sorted(QUALITATIVE_CMAPS), + "miscellaneous": sorted(MISC_CMAPS), +} + + def get_cmap(name: str, alpha: float = 1.0, gamma: float = 1.0) -> np.ndarray: """ Get a colormap as numpy array @@ -26,7 +69,8 @@ def get_cmap(name: str, alpha: float = 1.0, gamma: float = 1.0) -> np.ndarray: [n_colors, 4], i.e. [n_colors, RGBA] """ - cmap = Colormap(name).lut(256, gamma=gamma) + + cmap = cmap_lib.Colormap(name).lut(256, gamma=gamma) cmap[:, -1] = alpha return cmap.astype(np.float32) @@ -53,7 +97,8 @@ def make_colors(n_colors: int, cmap: str, alpha: float = 1.0) -> np.ndarray: shape is [n_colors, 4], where the last dimension is RGBA """ - cm = Colormap(cmap) + + cm = cmap_lib.Colormap(cmap) # can also use cm.category == "qualitative", but checking for non-interpolated # colormaps is a bit more general. (and not all "custom" colormaps will be @@ -63,7 +108,7 @@ def make_colors(n_colors: int, cmap: str, alpha: float = 1.0) -> np.ndarray: if n_colors > max_colors: raise ValueError( f"You have requested <{n_colors}> colors but only <{max_colors}> exist for the " - f"chosen cmap: <{name}>" + f"chosen cmap: <{cmap}>" ) return np.asarray(cm.color_stops, dtype=np.float32)[:n_colors, 1:] @@ -72,7 +117,7 @@ def make_colors(n_colors: int, cmap: str, alpha: float = 1.0) -> np.ndarray: def get_cmap_texture(name: str, alpha: float = 1.0) -> Texture: - return Colormap(name).to_pygfx() + return cmap_lib.Colormap(name).to_pygfx() def make_colors_dict(labels: Sequence, cmap: str, **kwargs) -> OrderedDict: @@ -247,7 +292,8 @@ def parse_cmap_values( n_colors = colormap.shape[0] - 1 # can also use cm.category == "qualitative" - if Colormap(cmap_name).interpolation == "nearest": + if cmap_lib.Colormap(cmap_name).interpolation == "nearest": + # check that cmap_values are and within the number of colors `n_colors` # do not scale, use directly diff --git a/fastplotlib/widgets/__init__.py b/fastplotlib/widgets/__init__.py index 30a68d672..766620ea6 100644 --- a/fastplotlib/widgets/__init__.py +++ b/fastplotlib/widgets/__init__.py @@ -1,3 +1,3 @@ -from .image import ImageWidget +from .image_widget import ImageWidget __all__ = ["ImageWidget"] diff --git a/fastplotlib/widgets/_image_widget_ipywidget_toolbar.py b/fastplotlib/widgets/_image_widget_ipywidget_toolbar.py deleted file mode 100644 index 24f7a6279..000000000 --- a/fastplotlib/widgets/_image_widget_ipywidget_toolbar.py +++ /dev/null @@ -1,135 +0,0 @@ -from functools import partial - -from ipywidgets import ( - VBox, - Button, - Layout, - IntSlider, - BoundedIntText, - Play, - jslink, - HBox, -) - - -class IpywidgetImageWidgetToolbar(VBox): - def __init__(self, iw): - """ - Basic toolbar for a ImageWidget instance. - - Parameters - ---------- - plot: - """ - self.iw = iw - - self.reset_vminvmax_button = Button( - value=False, - disabled=False, - icon="adjust", - layout=Layout(width="auto"), - tooltip="reset vmin/vmax", - ) - - self.reset_vminvmax_hlut_button = Button( - value=False, - icon="adjust", - description="reset", - layout=Layout(width="auto"), - tooltip="reset vmin/vmax and reset histogram using current frame", - ) - - self.sliders: dict[str, IntSlider] = dict() - - # only for xy data, no time point slider needed - if self.iw.ndim == 2: - widgets = [self.reset_vminvmax_button] - # for txy, tzxy, etc. data - else: - for dim in self.iw.slider_dims: - slider = IntSlider( - min=0, - max=self.iw._dims_max_bounds[dim] - 1, - step=1, - value=0, - description=f"dimension: {dim}", - orientation="horizontal", - ) - - slider.observe( - partial(self.iw._slider_value_changed, dim), names="value" - ) - - self.sliders[dim] = slider - - self.step_size_setter = BoundedIntText( - value=1, - min=1, - max=self.sliders["t"].max, - step=1, - description="Step Size:", - disabled=False, - description_tooltip="set slider step", - layout=Layout(width="150px"), - ) - self.speed_text = BoundedIntText( - value=100, - min=1, - max=1_000, - step=50, - description="Speed", - disabled=False, - description_tooltip="Playback speed, this is NOT framerate.\nArbitrary units between 1 - 1,000", - layout=Layout(width="150px"), - ) - self.play_button = Play( - value=0, - min=self.sliders["t"].min, - max=self.sliders["t"].max, - step=self.sliders["t"].step, - description="play/pause", - disabled=False, - ) - widgets = [ - self.reset_vminvmax_button, - self.reset_vminvmax_hlut_button, - self.play_button, - self.step_size_setter, - self.speed_text, - ] - - self.play_button.interval = 10 - - self.step_size_setter.observe(self._change_stepsize, "value") - self.speed_text.observe(self._change_framerate, "value") - jslink((self.play_button, "value"), (self.sliders["t"], "value")) - jslink((self.play_button, "max"), (self.sliders["t"], "max")) - - self.reset_vminvmax_button.on_click(self._reset_vminvmax) - self.reset_vminvmax_hlut_button.on_click(self._reset_vminvmax_frame) - - self.iw.figure.renderer.add_event_handler(self._set_slider_layout, "resize") - - # the buttons - self.hbox = HBox(widgets) - - super().__init__((self.hbox, *list(self.sliders.values()))) - - def _reset_vminvmax(self, obj): - self.iw.reset_vmin_vmax() - - def _reset_vminvmax_frame(self, obj): - self.iw.reset_vmin_vmax_frame() - - def _change_stepsize(self, obj): - self.sliders["t"].step = self.step_size_setter.value - - def _change_framerate(self, change): - interval = int(1000 / change["new"]) - self.play_button.interval = interval - - def _set_slider_layout(self, *args): - w, h = self.iw.figure.renderer.logical_size - - for k, v in self.sliders.items(): - v.layout = Layout(width=f"{w}px") diff --git a/fastplotlib/widgets/_image_widget_qt_toolbar.py b/fastplotlib/widgets/_image_widget_qt_toolbar.py deleted file mode 100644 index 2117f95ab..000000000 --- a/fastplotlib/widgets/_image_widget_qt_toolbar.py +++ /dev/null @@ -1,127 +0,0 @@ -from functools import partial -from typing import Dict - -from fastplotlib.utils.gui import QtWidgets, QtCore - - -# TODO: There must be a better way to do this -# TODO: Check if an interface exists between ipywidgets and Qt -# TODO: Or we won't need it anyways once we have UI in pygfx -class SliderInterface: - """ - This exists so that ImageWidget has a common interface for Sliders. - - This interface makes a QSlider behave somewhat like a ipywidget IntSlider, enough for ImageWidget to function. - """ - - def __init__(self, qslider): - self.qslider = qslider - - @property - def value(self) -> int: - return self.qslider.value() - - @value.setter - def value(self, value: int): - self.qslider.setValue(value) - - @property - def max(self) -> int: - return self.qslider.maximum() - - @max.setter - def max(self, value: int): - self.qslider.setMaximum(value) - - @property - def min(self): - return self.qslider.minimum() - - @min.setter - def min(self, value: int): - self.qslider.setMinimum(value) - - -class QToolbarImageWidget(QtWidgets.QWidget): - """Toolbar for ImageWidget""" - - def __init__(self, image_widget): - QtWidgets.QWidget.__init__(self) - - # vertical layout - self.vlayout = QtWidgets.QVBoxLayout(self) - - self.image_widget = image_widget - - hlayout_buttons = QtWidgets.QHBoxLayout() - - self.reset_vmin_vmax_button = QtWidgets.QPushButton(self) - self.reset_vmin_vmax_button.setText("auto-contrast") - self.reset_vmin_vmax_button.clicked.connect(self.image_widget.reset_vmin_vmax) - hlayout_buttons.addWidget(self.reset_vmin_vmax_button) - - self.reset_vmin_vmax_hlut_button = QtWidgets.QPushButton(self) - self.reset_vmin_vmax_hlut_button.setText("reset histogram-lut") - self.reset_vmin_vmax_hlut_button.clicked.connect( - self.image_widget.reset_vmin_vmax_frame - ) - hlayout_buttons.addWidget(self.reset_vmin_vmax_hlut_button) - - self.vlayout.addLayout(hlayout_buttons) - - self.sliders: Dict[str, SliderInterface] = dict() - - # has time and/or z-volume - if self.image_widget.ndim > 2: - # create a slider, spinbox and dimension label for each dimension in the ImageWidget - for dim in self.image_widget.slider_dims: - hlayout = ( - QtWidgets.QHBoxLayout() - ) # horizontal stack for label, slider, spinbox - - # max value for current dimension - max_val = self.image_widget._dims_max_bounds[dim] - 1 - - # make slider - slider = QtWidgets.QSlider(self) - slider.setOrientation(QtCore.Qt.Orientation.Horizontal) - slider.setMinimum(0) - slider.setMaximum(max_val) - slider.setValue(0) - slider.setSingleStep(1) - slider.setPageStep(10) - - # make spinbox - spinbox = QtWidgets.QSpinBox(self) - spinbox.setMinimum(0) - spinbox.setMaximum(max_val) - spinbox.setValue(0) - spinbox.setSingleStep(1) - - # link slider and spinbox - slider.valueChanged.connect(spinbox.setValue) - spinbox.valueChanged.connect(slider.setValue) - - # connect slider to change the index within the dimension - slider.valueChanged.connect( - partial(self.image_widget._slider_value_changed, dim) - ) - - # slider dimension label - slider_label = QtWidgets.QLabel(self) - slider_label.setText(dim) - - # add the widgets to the horizontal layout - hlayout.addWidget(slider_label) - hlayout.addWidget(slider) - hlayout.addWidget(spinbox) - - # add horizontal layout to the vertical layout - self.vlayout.addLayout(hlayout) - - # add to sliders dict for easier access to users - self.sliders[dim] = SliderInterface(slider) - - max_height = 35 + (35 * len(self.sliders.keys())) - - self.setMaximumHeight(max_height) diff --git a/fastplotlib/widgets/image_widget/__init__.py b/fastplotlib/widgets/image_widget/__init__.py new file mode 100644 index 000000000..93aaa4ce1 --- /dev/null +++ b/fastplotlib/widgets/image_widget/__init__.py @@ -0,0 +1 @@ +from ._widget import ImageWidget diff --git a/fastplotlib/widgets/image_widget/_sliders.py b/fastplotlib/widgets/image_widget/_sliders.py new file mode 100644 index 000000000..c8ad67f39 --- /dev/null +++ b/fastplotlib/widgets/image_widget/_sliders.py @@ -0,0 +1,177 @@ +import os +from time import perf_counter + +from imgui_bundle import imgui, icons_fontawesome_6 as fa + +from ...ui import EdgeWindow + + +class ImageWidgetSliders(EdgeWindow): + def __init__(self, figure, size, location, title, image_widget): + super().__init__(figure=figure, size=size, location=location, title=title) + self._image_widget = image_widget + + # whether or not a dimension is in play mode + self._playing: dict[str, bool] = {"t": False, "z": False} + + # approximate framerate for playing + self._fps: dict[str, int] = {"t": 20, "z": 20} + # framerate converted to frame time + self._frame_time: dict[str, float] = {"t": 1 / 20, "z": 1 / 20} + + # last timepoint that a frame was displayed from a given dimension + self._last_frame_time: dict[str, float] = {"t": 0, "z": 0} + + self._loop = False + + if "RTD_BUILD" in os.environ.keys(): + if os.environ["RTD_BUILD"] == "1": + self._playing["t"] = True + self._loop = True + + def set_index(self, dim: str, index: int): + """set the current_index of the ImageWidget""" + + # make sure the max index for this dim is not exceeded + max_index = self._image_widget._dims_max_bounds[dim] - 1 + if index > max_index: + if self._loop: + # loop back to index zero if looping is enabled + index = 0 + else: + # if looping not enabled, stop playing this dimension + self._playing[dim] = False + return + + # set current_index + self._image_widget.current_index = {dim: min(index, max_index)} + + def update(self): + """called on every render cycle to update the GUI elements""" + + # store the new index of the image widget ("t" and "z") + new_index = dict() + + # flag if the index changed + flag_index_changed = False + + # reset vmin-vmax using full orig data + imgui.push_font(self._fa_icons) + if imgui.button(label=fa.ICON_FA_CIRCLE_HALF_STROKE + fa.ICON_FA_FILM): + self._image_widget.reset_vmin_vmax() + imgui.pop_font() + if imgui.is_item_hovered(0): + imgui.set_tooltip("reset contrast limits using full movie/stack") + + # reset vmin-vmax using currently displayed ImageGraphic data + imgui.push_font(self._fa_icons) + imgui.same_line() + if imgui.button(label=fa.ICON_FA_CIRCLE_HALF_STROKE): + self._image_widget.reset_vmin_vmax_frame() + imgui.pop_font() + if imgui.is_item_hovered(0): + imgui.set_tooltip("reset contrast limits using current frame") + + # time now + now = perf_counter() + + # buttons and slider UI elements for each dim + for dim in self._image_widget.slider_dims: + imgui.push_id(f"{self._id_counter}_{dim}") + imgui.push_font(self._fa_icons) + + if self._playing[dim]: + # show pause button if playing + if imgui.button(label=fa.ICON_FA_PAUSE): + # if pause button clicked, then set playing to false + self._playing[dim] = False + + # if in play mode and enough time has elapsed w.r.t. the desired framerate, increment the index + if now - self._last_frame_time[dim] >= self._frame_time[dim]: + self.set_index(dim, self._image_widget.current_index[dim] + 1) + self._last_frame_time[dim] = now + + else: + # we are not playing, so display play button + if imgui.button(label=fa.ICON_FA_PLAY): + # if play button is clicked, set last frame time to 0 so that index increments on next render + self._last_frame_time[dim] = 0 + # set playing to True since play button was clicked + self._playing[dim] = True + + imgui.same_line() + # step back one frame button + if imgui.button(label=fa.ICON_FA_BACKWARD_STEP) and not self._playing[dim]: + self.set_index(dim, self._image_widget.current_index[dim] - 1) + + imgui.same_line() + # step forward one frame button + if imgui.button(label=fa.ICON_FA_FORWARD_STEP) and not self._playing[dim]: + self.set_index(dim, self._image_widget.current_index[dim] + 1) + + imgui.same_line() + # stop button + if imgui.button(label=fa.ICON_FA_STOP): + self._playing[dim] = False + self._last_frame_time[dim] = 0 + self.set_index(dim, 0) + + imgui.same_line() + # loop checkbox + _, self._loop = imgui.checkbox(label=fa.ICON_FA_ROTATE, v=self._loop) + imgui.pop_font() + if imgui.is_item_hovered(0): + imgui.set_tooltip("loop playback") + + imgui.same_line() + imgui.text("framerate :") + imgui.same_line() + imgui.set_next_item_width(100) + # framerate int entry + fps_changed, value = imgui.input_int( + label="fps", v=self._fps[dim], step_fast=5 + ) + if imgui.is_item_hovered(0): + imgui.set_tooltip( + "framerate is approximate and less reliable as it approaches your monitor refresh rate" + ) + if fps_changed: + if value < 1: + value = 1 + if value > 50: + value = 50 + self._fps[dim] = value + self._frame_time[dim] = 1 / value + + val = self._image_widget.current_index[dim] + vmax = self._image_widget._dims_max_bounds[dim] - 1 + + imgui.text(f"{dim}: ") + imgui.same_line() + # so that slider occupies full width + imgui.set_next_item_width(self.width * 0.85) + + if "Jupyter" in self._image_widget.figure.canvas.__class__.__name__: + # until https://github.com/pygfx/wgpu-py/issues/530 + flags = imgui.SliderFlags_.no_input + else: + # clamps to min, max if user inputs value outside these bounds + flags = imgui.SliderFlags_.always_clamp + + # slider for this dimension + changed, index = imgui.slider_int( + f"{dim}", v=val, v_min=0, v_max=vmax, flags=flags + ) + + new_index[dim] = index + + # if the slider value changed for this dimension + flag_index_changed |= changed + + imgui.pop_id() + + if flag_index_changed: + # if any slider dim changed set the new index of the image widget + self._image_widget.current_index = new_index + + self.size = int(imgui.get_window_height()) diff --git a/fastplotlib/widgets/image.py b/fastplotlib/widgets/image_widget/_widget.py similarity index 90% rename from fastplotlib/widgets/image.py rename to fastplotlib/widgets/image_widget/_widget.py index 1819f8742..e40495be5 100644 --- a/fastplotlib/widgets/image.py +++ b/fastplotlib/widgets/image_widget/_widget.py @@ -1,12 +1,15 @@ -from typing import Any, Literal, Callable +from typing import Callable from warnings import warn import numpy as np -from ..layouts import Figure -from ..graphics import ImageGraphic -from ..utils import calculate_figure_shape -from .histogram_lut import HistogramLUT +from wgpu.gui import WgpuCanvasBase + +from ... import Figure +from ...graphics import ImageGraphic +from ...utils import calculate_figure_shape +from ...tools import HistogramLUTTool +from ._sliders import ImageWidgetSliders # Number of dimensions that represent one image/one frame. For grayscale shape will be [x, y], i.e. 2 dims, for RGB(A) @@ -105,13 +108,6 @@ def figure(self) -> Figure: """ return self._figure - @property - def widget(self): - """ - Output context, either an ipywidget or QWidget - """ - return self._output - @property def managed_graphics(self) -> list[ImageGraphic]: """List of ``ImageWidget`` managed graphics.""" @@ -170,11 +166,6 @@ def n_scrollable_dims(self) -> list[int]: """ return self._n_scrollable_dims - @property - def sliders(self) -> dict[str, Any]: - """the ipywidget IntSlider or QSlider instances used by the widget for indexing the desired dimensions""" - return self._image_widget_toolbar.sliders - @property def slider_dims(self) -> list[str]: """the dimensions that the sliders index""" @@ -197,6 +188,39 @@ def current_index(self) -> dict[str, int]: """ return self._current_index + @current_index.setter + def current_index(self, index: dict[str, int]): + if not self._initialized: + return + + if not set(index.keys()).issubset(set(self._current_index.keys())): + raise KeyError( + f"All dimension keys for setting `current_index` must be present in the widget sliders. " + f"The dimensions currently used for sliders are: {list(self.current_index.keys())}" + ) + + for k, val in index.items(): + if not isinstance(val, int): + raise TypeError("Indices for all dimensions must be int") + if val < 0: + raise IndexError("negative indexing is not supported for ImageWidget") + if val > self._dims_max_bounds[k]: + raise IndexError( + f"index {val} is out of bounds for dimension '{k}' " + f"which has a max bound of: {self._dims_max_bounds[k]}" + ) + + self._current_index.update(index) + + for i, (ig, data) in enumerate(zip(self.managed_graphics, self.data)): + frame = self._process_indices(data, self._current_index) + frame = self._process_frame_apply(frame, i) + ig.data = frame + + # call any event handlers + for handler in self._current_index_changed_handlers: + handler(self.current_index) + @property def n_img_dims(self) -> list[int]: """ @@ -247,42 +271,6 @@ def _get_n_scrollable_dims(self, curr_arr: np.ndarray, rgb: bool) -> list[int]: return n_scrollable_dims - @current_index.setter - def current_index(self, index: dict[str, int]): - # ignore if output context has not been created yet - if self.widget is None: - return - - if not set(index.keys()).issubset(set(self._current_index.keys())): - raise KeyError( - f"All dimension keys for setting `current_index` must be present in the widget sliders. " - f"The dimensions currently used for sliders are: {list(self.current_index.keys())}" - ) - - for k, val in index.items(): - if not isinstance(val, int): - raise TypeError("Indices for all dimensions must be int") - if val < 0: - raise IndexError("negative indexing is not supported for ImageWidget") - if val > self._dims_max_bounds[k]: - raise IndexError( - f"index {val} is out of bounds for dimension '{k}' " - f"which has a max bound of: {self._dims_max_bounds[k]}" - ) - - self._current_index.update(index) - - # can make a callback_block decorator later - self.block_sliders = True - for k in index.keys(): - self.sliders[k].value = index[k] - self.block_sliders = False - - for i, (ig, data) in enumerate(zip(self.managed_graphics, self.data)): - frame = self._process_indices(data, self._current_index) - frame = self._process_frame_apply(frame, i) - ig.data = frame - def __init__( self, data: np.ndarray | list[np.ndarray], @@ -355,14 +343,13 @@ def __init__( passed to each ImageGraphic in the ImageWidget figure subplots """ + self._initialized = False + self._names = None if figure_kwargs is None: figure_kwargs = dict() - # output context - self._output = None - if _is_arraylike(data): data = [data] @@ -493,8 +480,6 @@ def __init__( self._window_funcs = None self.window_funcs = window_funcs - self._sliders: dict[str, Any] = dict() - # get max bound for all data arrays for all slider dimensions and ensure compatibility across slider dims self._dims_max_bounds: dict[str, int] = {k: 0 for k in self.slider_dims} for i, _dim in enumerate(list(self._dims_max_bounds.keys())): @@ -538,15 +523,34 @@ def __init__( subplot.set_title(name) if self._histogram_widget: - hlut = HistogramLUT(data=d, image_graphic=ig, name="histogram_lut") + hlut = HistogramLUTTool(data=d, image_graphic=ig, name="histogram_lut") subplot.docks["right"].add_graphic(hlut) subplot.docks["right"].size = 80 subplot.docks["right"].auto_scale(maintain_aspect=False) subplot.docks["right"].controller.enabled = False - self.block_sliders = False - self._image_widget_toolbar = None + # hard code the expected height so that the first render looks right in tests, docs etc. + if len(self.slider_dims) == 0: + ui_size = 57 + if len(self.slider_dims) == 1: + ui_size = 106 + elif len(self.slider_dims) == 2: + ui_size = 155 + + self._image_widget_sliders = ImageWidgetSliders( + figure=self.figure, + size=ui_size, + location="bottom", + title="ImageWidget Controls", + image_widget=self, + ) + + self.figure.add_gui(self._image_widget_sliders) + + self._initialized = True + + self._current_index_changed_handlers = set() @property def frame_apply(self) -> dict | None: @@ -749,21 +753,66 @@ def _process_frame_apply(self, array, data_ix) -> np.ndarray: return array - def _slider_value_changed(self, dimension: str, change: dict | int): - if self.block_sliders: - return - if isinstance(change, dict): - value = change["new"] - else: - value = change - self.current_index = {dimension: value} + def add_event_handler(self, handler: callable, event: str = "current_index"): + """ + Register an event handler. + + Currently the only event that ImageWidget supports is "current_index". This event is + emitted whenever the index of the ImageWidget changes. + + Parameters + ---------- + handler: callable + callback function, must take a dict as the only argument. This dict will be the `current_index` + + event: str, "current_index" + the only supported event is "current_index" + + Example + ------- + + .. code-block:: py + + def my_handler(index): + print(index) + # example prints: {"t": 100} if data has only time dimension + # "z" index will be another key if present in the data, ex: {"t": 100, "z": 5} + + # create an image widget + iw = ImageWidget(...) + + # add event handler + iw.add_event_handler(my_handler) + + """ + if event != "current_index": + raise ValueError( + "`current_index` is the only event supported by `ImageWidget`" + ) + + self._current_index_changed_handlers.add(handler) + + def remove_event_handler(self, handler: callable): + """Remove a registered event handler""" + self._current_index_changed_handlers.remove(handler) + + def clear_event_handlers(self): + """Clear all registered event handlers""" + self._current_index_changed_handlers.clear() def reset_vmin_vmax(self): """ Reset the vmin and vmax w.r.t. the full data """ - for ig in self.managed_graphics: - ig.reset_vmin_vmax() + for data, subplot in zip(self.data, self.figure): + if "histogram_lut" not in subplot.docks["right"]: + continue + hlut = subplot.docks["right"]["histogram_lut"] + hlut.set_data(data, reset_vmin_vmax=True) + + else: + for ig in self.managed_graphics: + ig.reset_vmin_vmax() def reset_vmin_vmax_frame(self): """ @@ -808,8 +857,6 @@ def set_data( if reset_indices: for key in self.current_index: self.current_index[key] = 0 - for key in self.sliders: - self.sliders[key].value = 0 # set slider max according to new data max_lengths = dict() @@ -880,18 +927,14 @@ def set_data( f"New arrays have differing values along dim {scroll_dim}" ) + self._dims_max_bounds[scroll_dim] = max_lengths[scroll_dim] + # set histogram widget if self._histogram_widget: subplot.docks["right"]["histogram_lut"].set_data( new_array, reset_vmin_vmax=reset_vmin_vmax ) - # set slider maxes - # TODO: maybe make this stuff a property, like ndims, n_frames etc. and have it set the sliders - for key in self.sliders.keys(): - self.sliders[key].max = max_lengths[key] - self._dims_max_bounds[key] = max_lengths[key] - # force graphics to update self.current_index = self.current_index @@ -903,28 +946,16 @@ def show( Returns ------- - OutputContext - ImageWidget just uses the Gridplot output context - """ - if self.figure.canvas.__class__.__name__ == "JupyterWgpuCanvas": - from ._image_widget_ipywidget_toolbar import IpywidgetImageWidgetToolbar - - self._image_widget_toolbar = IpywidgetImageWidgetToolbar(self) + WgpuCanvasBase + canvas used by the Figure - elif self.figure.canvas.__class__.__name__ == "QWgpuCanvas": - from ._image_widget_qt_toolbar import QToolbarImageWidget - - self._image_widget_toolbar = QToolbarImageWidget(self) + """ - self._output = self.figure.show( - toolbar=toolbar, + return self.figure.show( sidecar=sidecar, sidecar_kwargs=sidecar_kwargs, - add_widgets=[self._image_widget_toolbar], ) - return self._output - def close(self): """Close Widget""" self.figure.close() diff --git a/setup.py b/setup.py index 6f7f64468..46b68fae6 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,8 @@ install_requires = [ "numpy>=1.23.0", - "pygfx>=0.4.0", + "pygfx>=0.5.0", + "wgpu>=0.18.1", "cmap>=0.1.3", ] @@ -22,9 +23,10 @@ "pandoc", "jupyterlab", "sidecar", - "imageio", + "imageio[pyav]", "matplotlib", - "scikit-learn" + "scikit-learn", + "imgui-bundle", ], "notebook": [ "jupyterlab", @@ -44,14 +46,17 @@ "scikit-learn", "tqdm", "sidecar", + "imgui-bundle", ], "tests-desktop": [ "pytest<8.0.0", "scipy", - "imageio", + "imageio[pyav]", "scikit-learn", "tqdm", + "imgui-bundle", ], + "imgui": ["imgui-bundle"], } diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 000000000..ffc34d464 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,8 @@ +import pygfx + + +MAX_TEXTURE_SIZE = 1024 + + +def pytest_sessionstart(session): + pygfx.renderers.wgpu.set_wgpu_limits(**{"max-texture-dimension2d": MAX_TEXTURE_SIZE}) diff --git a/tests/test_texture_array.py b/tests/test_texture_array.py index e1a6a1753..c85fc7652 100644 --- a/tests/test_texture_array.py +++ b/tests/test_texture_array.py @@ -5,10 +5,13 @@ import pygfx import fastplotlib as fpl -from fastplotlib.graphics._features import TextureArray, WGPU_MAX_TEXTURE_SIZE +from fastplotlib.graphics._features import TextureArray from fastplotlib.graphics.image import _ImageTile +MAX_TEXTURE_SIZE = 1024 + + def make_data(n_rows: int, n_cols: int) -> np.ndarray: """ Makes a 2D array where the amplitude of the sine wave @@ -50,14 +53,14 @@ def check_texture_array( assert ta.buffer[chunk_index] is texture chunk_row, chunk_col = chunk_index - data_row_start_index = chunk_row * WGPU_MAX_TEXTURE_SIZE - data_col_start_index = chunk_col * WGPU_MAX_TEXTURE_SIZE + data_row_start_index = chunk_row * MAX_TEXTURE_SIZE + data_col_start_index = chunk_col * MAX_TEXTURE_SIZE data_row_stop_index = min( - data.shape[0], data_row_start_index + WGPU_MAX_TEXTURE_SIZE + data.shape[0], data_row_start_index + MAX_TEXTURE_SIZE ) data_col_stop_index = min( - data.shape[1], data_col_start_index + WGPU_MAX_TEXTURE_SIZE + data.shape[1], data_col_start_index + MAX_TEXTURE_SIZE ) row_slice = slice(data_row_start_index, data_row_stop_index) @@ -96,7 +99,7 @@ def check_image_graphic(texture_array, graphic): @pytest.mark.parametrize("test_graphic", [False, True]) def test_small_texture(test_graphic): # tests TextureArray with dims that requires only 1 texture - data = make_data(1_000, 1_000) + data = make_data(500, 500) if test_graphic: graphic = make_image_graphic(data) @@ -118,13 +121,13 @@ def test_small_texture(test_graphic): if test_graphic: check_image_graphic(ta, graphic) - check_set_slice(data, ta, slice(50, 200), slice(600, 800)) + check_set_slice(data, ta, slice(50, 200), slice(200, 400)) @pytest.mark.parametrize("test_graphic", [False, True]) def test_texture_at_limit(test_graphic): - # tests TextureArray with data that is 8192 x 8192 - data = make_data(WGPU_MAX_TEXTURE_SIZE, WGPU_MAX_TEXTURE_SIZE) + # tests TextureArray with data that is 1024 x 1024 + data = make_data(MAX_TEXTURE_SIZE, MAX_TEXTURE_SIZE) if test_graphic: graphic = make_image_graphic(data) @@ -146,12 +149,12 @@ def test_texture_at_limit(test_graphic): if test_graphic: check_image_graphic(ta, graphic) - check_set_slice(data, ta, slice(5000, 8000), slice(2000, 3000)) + check_set_slice(data, ta, slice(500, 800), slice(200, 300)) @pytest.mark.parametrize("test_graphic", [False, True]) def test_wide(test_graphic): - data = make_data(10_000, 20_000) + data = make_data(1_200, 2_200) if test_graphic: graphic = make_image_graphic(data) @@ -166,19 +169,19 @@ def test_wide(test_graphic): buffer_shape=(2, 3), row_indices_size=2, col_indices_size=3, - row_indices_values=np.array([0, 8192]), - col_indices_values=np.array([0, 8192, 16384]), + row_indices_values=np.array([0, MAX_TEXTURE_SIZE]), + col_indices_values=np.array([0, MAX_TEXTURE_SIZE, 2 * MAX_TEXTURE_SIZE]), ) if test_graphic: check_image_graphic(ta, graphic) - check_set_slice(data, ta, slice(6_000, 9_000), slice(12_000, 18_000)) + check_set_slice(data, ta, slice(600, 1_100), slice(100, 2_100)) @pytest.mark.parametrize("test_graphic", [False, True]) def test_tall(test_graphic): - data = make_data(20_000, 10_000) + data = make_data(2_200, 1_200) if test_graphic: graphic = make_image_graphic(data) @@ -193,19 +196,19 @@ def test_tall(test_graphic): buffer_shape=(3, 2), row_indices_size=3, col_indices_size=2, - row_indices_values=np.array([0, 8192, 16384]), - col_indices_values=np.array([0, 8192]), + row_indices_values=np.array([0, MAX_TEXTURE_SIZE, 2 * MAX_TEXTURE_SIZE]), + col_indices_values=np.array([0, MAX_TEXTURE_SIZE]), ) if test_graphic: check_image_graphic(ta, graphic) - check_set_slice(data, ta, slice(12_000, 18_000), slice(6_000, 9_000)) + check_set_slice(data, ta, slice(100, 2_100), slice(600, 1_100)) @pytest.mark.parametrize("test_graphic", [False, True]) def test_square(test_graphic): - data = make_data(20_000, 20_000) + data = make_data(2_200, 2_200) if test_graphic: graphic = make_image_graphic(data) @@ -220,11 +223,11 @@ def test_square(test_graphic): buffer_shape=(3, 3), row_indices_size=3, col_indices_size=3, - row_indices_values=np.array([0, 8192, 16384]), - col_indices_values=np.array([0, 8192, 16384]), + row_indices_values=np.array([0, MAX_TEXTURE_SIZE, 2 * MAX_TEXTURE_SIZE]), + col_indices_values=np.array([0, MAX_TEXTURE_SIZE, 2 * MAX_TEXTURE_SIZE]), ) if test_graphic: check_image_graphic(ta, graphic) - check_set_slice(data, ta, slice(12_000, 18_000), slice(16_000, 19_000)) + check_set_slice(data, ta, slice(100, 2_100), slice(100, 2_100)) From cd6e1055369505ddbc69d7b6af72618caa5feaa9 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Tue, 1 Oct 2024 14:39:26 -0400 Subject: [PATCH 110/185] fix (#630) --- fastplotlib/widgets/image_widget/_widget.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/fastplotlib/widgets/image_widget/_widget.py b/fastplotlib/widgets/image_widget/_widget.py index e40495be5..21c6add44 100644 --- a/fastplotlib/widgets/image_widget/_widget.py +++ b/fastplotlib/widgets/image_widget/_widget.py @@ -280,7 +280,7 @@ def __init__( names: list[str] = None, figure_kwargs: dict = None, histogram_widget: bool = True, - rgb: list[bool] = None, + rgb: bool | list[bool] = None, cmap: str = "plasma", graphic_kwargs: dict = None, ): @@ -336,8 +336,8 @@ def __init__( make histogram LUT widget for each subplot rgb: bool | list[bool], default None - Includes a True or False for each ``array`` in the ImageWidget, indicating whether images are displayed as - grayscale or RGB(A). + bool or list of bool for each input data array in the ImageWidget, indicating whether the corresponding + data arrays are grayscale or RGB(A). graphic_kwargs: Any passed to each ImageGraphic in the ImageWidget figure subplots @@ -379,15 +379,15 @@ def __init__( # Establish number of image dimensions and number of scrollable dimensions for each array if rgb is None: rgb = [False] * len(self.data) - if rgb is bool: - rgb = [rgb] + if isinstance(rgb, bool): + rgb = [rgb] * len(self.data) if not isinstance(rgb, list): raise TypeError( - f"rgb_disp parameter must be a list, a {type(rgb)} was provided" + f"`rgb` parameter must be a bool or list of bool, a <{type(rgb)}> was provided" ) if not len(rgb) == len(self.data): raise ValueError( - f"rgb had length {len(rgb)} but there are {len(self.data)} data arrays; these must be equal" + f"len(rgb) != len(data), {len(rgb)} != {len(self.data)}. These must be equal" ) self._rgb = rgb From 0769177a8f49a3ed2c0e35e13367e2c1d74e1df6 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Sun, 6 Oct 2024 20:28:19 -0400 Subject: [PATCH 111/185] Fix lfs file (#632) * remove iris file * re-add iris data file so lfs picks it up properly --- examples/desktop/data/iris.npy | Bin 4928 -> 129 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/examples/desktop/data/iris.npy b/examples/desktop/data/iris.npy index a8c7e5ab0a48138e424a1ed78323e4a7f6158259..052b84bacd31cd7c60d1e92e376734e3ab2c0cb7 100644 GIT binary patch literal 129 zcmWN^%MHUI3;@tOQ?Nh-F`wAk1~3$)wnT;G(CM4g)4TF_wEmHG&SUIKJ==V|%2;me z8JE=GY8*MKYZbk*95sORJ28MO#WFeLD4@n@G($GoS`W-C_(B`50bvU%YMs$Q!a!^b M-4~<%va|#!{;nb?5&!@I literal 4928 zcmbW5y>4Sw6oqY%^XG&lc5KJ_nc&VKr2qjELP&N+2cn^%LJ^aQ0x_cm(IB3J3f?4- z5DBWO(joBzi86`?C5o72t#5DLbCm*ab9L@Md+)W^{yE3~^^31Q|MKf*sopL}=l;_%J-o%cU_Z-4yZ zyZhr`#{cu_96db!S>xeZnQ47J{r$K5=gjX7%NJ|%KmWLYdNb``nrZzi^kuK-?~Atc zI^^=(xBM~xgV_fU&zj#i)<2(OU-IR^r}fJz_K|Cy{)M}1y9=RT#;>?a{HMk-aQtu8 z;*;ME+h6v+e_@`GI8`{s2u_(3k7$?tOL!$103m;8c{`dYF5i-;e6y*pvQ@$md*eltJX zcNhBFNBr=g`9rRF%KV<2AN0Wo|7RgbU;eZI#BcVOcu4=!+lc-u-x#H`ALs+%d287w66Es6Y8{S#Le^AUoT}HSc%Yug<4= z>oYH2ncpqzb!->D=riWSlh`M}7tY_d>x=Uv?aP1P@y~hRn(bUif5=|L_?Q#SG3Gn< zoqa03k?l9Fj~slXz{fnH4)h)t^6Ufc-!Agg4gBZ}2j^q*ulm}I__aUr$$p7H^{e*V z!Jqnwa}fK?7v|xu^D{DD@}Ko1d|T$NZJb$u@}K(fCq2&Dt>91JyHTIg-?n|~JNeW8 zoxrE{M(_*&ReqUgH?E(|ul6N>(r13RjU($z`^i7`%^bOm{u8bz=7)3m!uM`! z75J2|Imbmm&|d@Ns@IS35g+qA`xAcjrav-%;h?`;#$V=#^N0DG^#MP6!(Ux4|I`on z$*ZV8>0>|VqwsCIUebP*KhB4i`RmMNkN)O%1o-m%oKa^x=QObzbtv z`38RK{Ia+oGM^J)H9y2Pa(wiA)|dJTKJLu#uJ_{?J{!iH{mDF0+_$dJ>`(HcdTpBT zy|7O|OTSUf3*ztD?~VK8X)zzR%=4}J!GFdJKD~FnpZibpqr7e#$5Ql{zJrhRm;M}h z-<|R8M10a?UNr2N{vbZ`4ZoYVM}C=4%nST8pYh+aU*f0#@ee7y8xwa)06eLHyXy`<3jD><51G zd{X_^&p+wWA1PPd%t!K9_m6(HKbYT7i~NI!-%)Qz{_qDstkqZPU*DV2zg2nWPv7CA zZ9nAe+Wl4bFZfzvUvcsOL7s=;Q@-l;Nj|Fll>C Date: Mon, 7 Oct 2024 09:29:21 -0400 Subject: [PATCH 112/185] fix auto canvas detection (#638) * fix auto canvas detection * black --- fastplotlib/__init__.py | 3 ++- fastplotlib/utils/__init__.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/fastplotlib/__init__.py b/fastplotlib/__init__.py index a85de93c2..3cee71b28 100644 --- a/fastplotlib/__init__.py +++ b/fastplotlib/__init__.py @@ -1,6 +1,7 @@ from pathlib import Path -from .utils.gui import run # noqa +# this must be the first import for auto-canvas detection +from .utils import run # noqa from .graphics import * from .graphics.selectors import * from .graphics.utils import pause_events diff --git a/fastplotlib/utils/__init__.py b/fastplotlib/utils/__init__.py index 3ae83fb6b..276397e88 100644 --- a/fastplotlib/utils/__init__.py +++ b/fastplotlib/utils/__init__.py @@ -1,6 +1,7 @@ from dataclasses import dataclass - +# this MUST be imported as early as possible in fpl.__init__ before any other wgpu stuff +from .gui import run from .functions import * from .gpu import enumerate_adapters, select_adapter, print_wgpu_report from ._plot_helpers import * From 56abbd976cd2a4c8ce938ef392b3ff7c9ed2ce6f Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Mon, 7 Oct 2024 23:33:10 -0400 Subject: [PATCH 113/185] docs reorganization take 3 (#639) * misc -> misc-dev * remove empty README file * examples/desktop -> examples * update examples * remove old nb example stuff * set offset for both dims for selectors * update actions * rename lint -> black * update docs to use pydata sphinx theme and switcher * update toctrees w.r.t pydata theme * update deploy docs to deploy to version dir * fix github var name * update headings * update links and homepage * tryign to learn github actions properly blargh * add a / * update docs deploy * update readme and guide * update readme * comments in workflow * force fail if there are doc build warnings * update job name * no longer need separate doc CI test * fix headings * activate minigalleries * comment on PR with link to built docs * proper link * update intersphinx for fastplotlib * remove fastplotlib from intersphinx inventory for now --- .github/workflows/black.yml | 2 +- .github/workflows/ci.yml | 37 +-- .github/workflows/docs-deploy.yml | 104 ++++++ .github/workflows/screenshots.yml | 2 +- README.md | 93 +++--- docs/source/_static/switcher.json | 7 + docs/source/_templates/autosummary/class.rst | 3 + docs/source/_templates/autosummary/method.rst | 3 + .../_templates/autosummary/property.rst | 3 + docs/source/api/index.rst | 15 + docs/source/api/layouts/index.rst | 9 + docs/source/conf.py | 47 +-- docs/source/generate_api.py | 60 ++++ docs/source/index.rst | 43 +-- docs/source/user_guide/faq.rst | 42 +-- docs/source/user_guide/gpu.rst | 18 +- docs/source/user_guide/guide.rst | 43 ++- docs/source/user_guide/index.rst | 10 + examples/README.rst | 4 +- examples/{desktop => }/data/iris.npy | 0 examples/desktop/README.rst | 0 examples/{desktop => }/gridplot/README.rst | 0 examples/{desktop => }/gridplot/gridplot.py | 0 .../gridplot/gridplot_non_square.py | 0 .../gridplot/multigraphic_gridplot.py | 0 examples/{desktop => }/guis/README.rst | 2 +- .../{desktop => }/guis/image_widget_imgui.py | 0 examples/{desktop => }/guis/imgui_basic.py | 0 examples/{desktop => }/heatmap/README.rst | 0 examples/{desktop => }/heatmap/heatmap.py | 0 examples/{desktop => }/image/README.rst | 0 examples/{desktop => }/image/image_cmap.py | 0 examples/{desktop => }/image/image_rgb.py | 0 .../{desktop => }/image/image_rgbvminvmax.py | 0 examples/{desktop => }/image/image_simple.py | 0 examples/{desktop => }/image/image_small.py | 0 .../{desktop => }/image/image_vminvmax.py | 0 .../{desktop => }/image_widget/README.rst | 0 .../image_widget/image_widget.py | 0 .../image_widget/image_widget_grid.py | 0 .../image_widget/image_widget_single_video.py | 0 .../image_widget/image_widget_videos.py | 0 examples/{desktop => }/line/README.rst | 0 examples/{desktop => }/line/line.py | 0 examples/{desktop => }/line/line_cmap.py | 2 +- examples/line/line_cmap_more.py | 56 ++++ .../{desktop => }/line/line_colorslice.py | 0 examples/{desktop => }/line/line_dataslice.py | 0 .../{desktop => }/line_collection/README.rst | 0 .../line_collection/line_collection.py | 0 .../line_collection_cmap_values.py | 0 ...line_collection_cmap_values_qualitative.py | 0 .../line_collection/line_collection_colors.py | 0 .../line_collection_slicing.py | 0 .../line_collection/line_stack.py | 0 .../line_collection/line_stack_3d.py | 0 .../{misc => misc-dev}/garbage_collection.py | 0 examples/{misc => misc-dev}/large_img.py | 0 .../selector_performance.ipynb | 0 examples/{desktop => }/misc/README.rst | 0 .../{desktop => }/misc/cycle_animation.py | 0 .../{desktop => }/misc/em_wave_animation.py | 0 .../{desktop => }/misc/image_animation.py | 0 .../{desktop => }/misc/line3d_animation.py | 0 examples/{desktop => }/misc/line_animation.py | 0 .../{desktop => }/misc/lorenz_animation.py | 0 .../{desktop => }/misc/multiplot_animation.py | 0 .../{desktop => }/misc/scatter_animation.py | 0 examples/misc/scatter_sizes_animation.py | 47 +++ examples/{desktop => }/misc/simple_event.py | 0 examples/notebooks/heatmap.ipynb | 210 ------------ .../notebooks/linear_region_selector.ipynb | 182 ----------- examples/notebooks/linear_selector.ipynb | 150 --------- examples/notebooks/lineplot.ipynb | 124 ------- examples/notebooks/lines_cmap.ipynb | 306 ------------------ examples/notebooks/scatter.ipynb | 190 ----------- .../notebooks/scatter_sizes_animation.ipynb | 61 ---- examples/notebooks/scatter_sizes_grid.ipynb | 82 ----- .../nb-lines-cmap-jet-values-cosine.png | 3 - .../screenshots/nb-lines-cmap-jet-values.png | 3 - .../screenshots/nb-lines-cmap-jet.png | 3 - .../screenshots/nb-lines-cmap-tab-10.png | 3 - .../nb-lines-cmap-viridis-values.png | 3 - .../screenshots/nb-lines-cmap-viridis.png | 3 - .../screenshots/nb-lines-cmap-white.png | 3 - examples/notebooks/subplots.ipynb | 218 ------------- examples/notebooks/subplots_simple.ipynb | 259 --------------- examples/qt/README.rst | 2 + examples/qt/embed.py | 14 +- examples/qt/imagewidget.py | 24 +- examples/qt/minimal.py | 20 +- examples/{desktop => }/scatter/README.rst | 0 examples/{desktop => }/scatter/scatter.py | 0 .../{desktop => }/scatter/scatter_cmap.py | 0 .../scatter/scatter_cmap_iris.py | 0 .../scatter/scatter_colorslice.py | 0 .../scatter/scatter_colorslice_iris.py | 0 .../scatter/scatter_dataslice.py | 0 .../scatter/scatter_dataslice_iris.py | 0 .../{desktop => }/scatter/scatter_iris.py | 0 .../{desktop => }/scatter/scatter_size.py | 0 .../{desktop => }/screenshots/gridplot.png | 0 .../screenshots/gridplot_non_square.png | 0 .../{desktop => }/screenshots/heatmap.png | 0 .../{desktop => }/screenshots/image_cmap.png | 0 .../{desktop => }/screenshots/image_rgb.png | 0 .../screenshots/image_rgbvminvmax.png | 0 .../screenshots/image_simple.png | 0 .../{desktop => }/screenshots/image_small.png | 0 .../screenshots/image_vminvmax.png | 0 .../screenshots/image_widget.png | 0 .../screenshots/image_widget_grid.png | 0 .../screenshots/image_widget_imgui.png | 0 .../screenshots/image_widget_single_video.png | 0 .../screenshots/image_widget_videos.png | 0 .../{desktop => }/screenshots/imgui_basic.png | 0 examples/{desktop => }/screenshots/line.png | 0 .../{desktop => }/screenshots/line_cmap.png | 0 examples/screenshots/line_cmap_more.png | 3 + .../screenshots/line_collection.png | 0 .../line_collection_cmap_values.png | 0 ...ine_collection_cmap_values_qualitative.png | 0 .../screenshots/line_collection_colors.png | 0 .../screenshots/line_collection_slicing.png | 0 .../screenshots/line_colorslice.png | 0 .../screenshots/line_dataslice.png | 0 .../{desktop => }/screenshots/line_stack.png | 0 .../linear_region_selectors_match_offsets.png | 3 + .../screenshots/linear_selector.png | 0 .../screenshots/scatter_cmap_iris.png | 0 .../screenshots/scatter_colorslice_iris.png | 0 .../screenshots/scatter_dataslice_iris.png | 0 .../screenshots/scatter_iris.png | 0 .../screenshots/scatter_size.png | 0 .../selectors => selection_tools}/README.rst | 0 .../linear_region_line_collection.py | 81 +++++ .../selection_tools/linear_region_selector.py | 114 +++++++ .../linear_region_selectors_match_offsets.py | 109 +++++++ .../linear_selector.py | 0 .../linear_selector_image.py | 0 .../rectangle_selector.py | 0 .../rectangle_selector_zoom.py | 5 +- examples/tests/test_examples.py | 26 +- examples/tests/testutils.py | 4 +- fastplotlib/graphics/selectors/_linear.py | 4 +- .../graphics/selectors/_linear_region.py | 4 +- setup.py | 2 +- 147 files changed, 850 insertions(+), 2020 deletions(-) create mode 100644 .github/workflows/docs-deploy.yml create mode 100644 docs/source/_static/switcher.json create mode 100644 docs/source/api/index.rst create mode 100644 docs/source/api/layouts/index.rst create mode 100644 docs/source/user_guide/index.rst rename examples/{desktop => }/data/iris.npy (100%) delete mode 100644 examples/desktop/README.rst rename examples/{desktop => }/gridplot/README.rst (100%) rename examples/{desktop => }/gridplot/gridplot.py (100%) rename examples/{desktop => }/gridplot/gridplot_non_square.py (100%) rename examples/{desktop => }/gridplot/multigraphic_gridplot.py (100%) rename examples/{desktop => }/guis/README.rst (50%) rename examples/{desktop => }/guis/image_widget_imgui.py (100%) rename examples/{desktop => }/guis/imgui_basic.py (100%) rename examples/{desktop => }/heatmap/README.rst (100%) rename examples/{desktop => }/heatmap/heatmap.py (100%) rename examples/{desktop => }/image/README.rst (100%) rename examples/{desktop => }/image/image_cmap.py (100%) rename examples/{desktop => }/image/image_rgb.py (100%) rename examples/{desktop => }/image/image_rgbvminvmax.py (100%) rename examples/{desktop => }/image/image_simple.py (100%) rename examples/{desktop => }/image/image_small.py (100%) rename examples/{desktop => }/image/image_vminvmax.py (100%) rename examples/{desktop => }/image_widget/README.rst (100%) rename examples/{desktop => }/image_widget/image_widget.py (100%) rename examples/{desktop => }/image_widget/image_widget_grid.py (100%) rename examples/{desktop => }/image_widget/image_widget_single_video.py (100%) rename examples/{desktop => }/image_widget/image_widget_videos.py (100%) rename examples/{desktop => }/line/README.rst (100%) rename examples/{desktop => }/line/line.py (100%) rename examples/{desktop => }/line/line_cmap.py (95%) create mode 100644 examples/line/line_cmap_more.py rename examples/{desktop => }/line/line_colorslice.py (100%) rename examples/{desktop => }/line/line_dataslice.py (100%) rename examples/{desktop => }/line_collection/README.rst (100%) rename examples/{desktop => }/line_collection/line_collection.py (100%) rename examples/{desktop => }/line_collection/line_collection_cmap_values.py (100%) rename examples/{desktop => }/line_collection/line_collection_cmap_values_qualitative.py (100%) rename examples/{desktop => }/line_collection/line_collection_colors.py (100%) rename examples/{desktop => }/line_collection/line_collection_slicing.py (100%) rename examples/{desktop => }/line_collection/line_stack.py (100%) rename examples/{desktop => }/line_collection/line_stack_3d.py (100%) rename examples/{misc => misc-dev}/garbage_collection.py (100%) rename examples/{misc => misc-dev}/large_img.py (100%) rename examples/{misc => misc-dev}/selector_performance.ipynb (100%) rename examples/{desktop => }/misc/README.rst (100%) rename examples/{desktop => }/misc/cycle_animation.py (100%) rename examples/{desktop => }/misc/em_wave_animation.py (100%) rename examples/{desktop => }/misc/image_animation.py (100%) rename examples/{desktop => }/misc/line3d_animation.py (100%) rename examples/{desktop => }/misc/line_animation.py (100%) rename examples/{desktop => }/misc/lorenz_animation.py (100%) rename examples/{desktop => }/misc/multiplot_animation.py (100%) rename examples/{desktop => }/misc/scatter_animation.py (100%) create mode 100644 examples/misc/scatter_sizes_animation.py rename examples/{desktop => }/misc/simple_event.py (100%) delete mode 100644 examples/notebooks/heatmap.ipynb delete mode 100644 examples/notebooks/linear_region_selector.ipynb delete mode 100644 examples/notebooks/linear_selector.ipynb delete mode 100644 examples/notebooks/lineplot.ipynb delete mode 100644 examples/notebooks/lines_cmap.ipynb delete mode 100644 examples/notebooks/scatter.ipynb delete mode 100644 examples/notebooks/scatter_sizes_animation.ipynb delete mode 100644 examples/notebooks/scatter_sizes_grid.ipynb delete mode 100644 examples/notebooks/screenshots/nb-lines-cmap-jet-values-cosine.png delete mode 100644 examples/notebooks/screenshots/nb-lines-cmap-jet-values.png delete mode 100644 examples/notebooks/screenshots/nb-lines-cmap-jet.png delete mode 100644 examples/notebooks/screenshots/nb-lines-cmap-tab-10.png delete mode 100644 examples/notebooks/screenshots/nb-lines-cmap-viridis-values.png delete mode 100644 examples/notebooks/screenshots/nb-lines-cmap-viridis.png delete mode 100644 examples/notebooks/screenshots/nb-lines-cmap-white.png delete mode 100644 examples/notebooks/subplots.ipynb delete mode 100644 examples/notebooks/subplots_simple.ipynb create mode 100644 examples/qt/README.rst rename examples/{desktop => }/scatter/README.rst (100%) rename examples/{desktop => }/scatter/scatter.py (100%) rename examples/{desktop => }/scatter/scatter_cmap.py (100%) rename examples/{desktop => }/scatter/scatter_cmap_iris.py (100%) rename examples/{desktop => }/scatter/scatter_colorslice.py (100%) rename examples/{desktop => }/scatter/scatter_colorslice_iris.py (100%) rename examples/{desktop => }/scatter/scatter_dataslice.py (100%) rename examples/{desktop => }/scatter/scatter_dataslice_iris.py (100%) rename examples/{desktop => }/scatter/scatter_iris.py (100%) rename examples/{desktop => }/scatter/scatter_size.py (100%) rename examples/{desktop => }/screenshots/gridplot.png (100%) rename examples/{desktop => }/screenshots/gridplot_non_square.png (100%) rename examples/{desktop => }/screenshots/heatmap.png (100%) rename examples/{desktop => }/screenshots/image_cmap.png (100%) rename examples/{desktop => }/screenshots/image_rgb.png (100%) rename examples/{desktop => }/screenshots/image_rgbvminvmax.png (100%) rename examples/{desktop => }/screenshots/image_simple.png (100%) rename examples/{desktop => }/screenshots/image_small.png (100%) rename examples/{desktop => }/screenshots/image_vminvmax.png (100%) rename examples/{desktop => }/screenshots/image_widget.png (100%) rename examples/{desktop => }/screenshots/image_widget_grid.png (100%) rename examples/{desktop => }/screenshots/image_widget_imgui.png (100%) rename examples/{desktop => }/screenshots/image_widget_single_video.png (100%) rename examples/{desktop => }/screenshots/image_widget_videos.png (100%) rename examples/{desktop => }/screenshots/imgui_basic.png (100%) rename examples/{desktop => }/screenshots/line.png (100%) rename examples/{desktop => }/screenshots/line_cmap.png (100%) create mode 100644 examples/screenshots/line_cmap_more.png rename examples/{desktop => }/screenshots/line_collection.png (100%) rename examples/{desktop => }/screenshots/line_collection_cmap_values.png (100%) rename examples/{desktop => }/screenshots/line_collection_cmap_values_qualitative.png (100%) rename examples/{desktop => }/screenshots/line_collection_colors.png (100%) rename examples/{desktop => }/screenshots/line_collection_slicing.png (100%) rename examples/{desktop => }/screenshots/line_colorslice.png (100%) rename examples/{desktop => }/screenshots/line_dataslice.png (100%) rename examples/{desktop => }/screenshots/line_stack.png (100%) create mode 100644 examples/screenshots/linear_region_selectors_match_offsets.png rename examples/{desktop => }/screenshots/linear_selector.png (100%) rename examples/{desktop => }/screenshots/scatter_cmap_iris.png (100%) rename examples/{desktop => }/screenshots/scatter_colorslice_iris.png (100%) rename examples/{desktop => }/screenshots/scatter_dataslice_iris.png (100%) rename examples/{desktop => }/screenshots/scatter_iris.png (100%) rename examples/{desktop => }/screenshots/scatter_size.png (100%) rename examples/{desktop/selectors => selection_tools}/README.rst (100%) create mode 100644 examples/selection_tools/linear_region_line_collection.py create mode 100644 examples/selection_tools/linear_region_selector.py create mode 100644 examples/selection_tools/linear_region_selectors_match_offsets.py rename examples/{desktop/selectors => selection_tools}/linear_selector.py (100%) rename examples/{desktop/selectors => selection_tools}/linear_selector_image.py (100%) rename examples/{desktop/selectors => selection_tools}/rectangle_selector.py (100%) rename examples/{desktop/selectors => selection_tools}/rectangle_selector_zoom.py (95%) diff --git a/.github/workflows/black.yml b/.github/workflows/black.yml index bec47fdc5..cebee50a0 100644 --- a/.github/workflows/black.yml +++ b/.github/workflows/black.yml @@ -1,4 +1,4 @@ -name: Lint +name: black on: push: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 23bfc8ae7..ad199d28f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,39 +14,6 @@ on: - ready_for_review jobs: - docs-build: - name: Docs - runs-on: ubuntu-latest - timeout-minutes: 10 - if: ${{ !github.event.pull_request.draft }} - strategy: - fail-fast: false - steps: - - uses: actions/checkout@v3 - - name: Set up Python 3.11 - uses: actions/setup-python@v4 - with: - python-version: 3.11 - - name: Install llvmpipe and lavapipe for offscreen canvas, and git lfs - run: | - sudo apt-get update -y -qq - sudo apt-get install --no-install-recommends -y libegl1-mesa libgl1-mesa-dri libxcb-xfixes0-dev mesa-vulkan-drivers git-lfs - - name: Install pandoc v3.14, nbsphinx complains about older pandoc versions - run: | - wget https://github.com/jgm/pandoc/releases/download/3.1.4/pandoc-3.1.4-1-amd64.deb - sudo apt-get install ./pandoc-3.1.4-1-amd64.deb - - name: Install dev dependencies - run: | - python -m pip install --upgrade pip setuptools - # remove pygfx from install_requires, we install using pygfx@main - sed -i "/pygfx/d" ./setup.py - pip install git+https://github.com/pygfx/pygfx.git@main - pip install -e ".[notebook,docs,tests]" - - name: Build docs - run: | - cd docs - make html SPHINXOPTS="-W --keep-going" - test-build-full: name: Test Linux, notebook + offscreen runs-on: ubuntu-latest @@ -101,7 +68,7 @@ jobs: with: name: screenshot-diffs path: | - examples/desktop/diffs + examples/diffs examples/notebooks/diffs test-build-desktop: @@ -157,7 +124,7 @@ jobs: with: name: screenshot-diffs path: | - examples/desktop/diffs + examples/diffs # test-build-full-mac: # name: Test Mac, notebook + glfw diff --git a/.github/workflows/docs-deploy.yml b/.github/workflows/docs-deploy.yml new file mode 100644 index 000000000..b13c60849 --- /dev/null +++ b/.github/workflows/docs-deploy.yml @@ -0,0 +1,104 @@ +name: Deploy docs + +on: + push: + branches: + - main + pull_request: + branches: + - main + types: + - opened + - reopened + - synchronize + - ready_for_review + release: + types: [published] + +jobs: + build-docs: + name: "Build and deploy docs" + runs-on: ubuntu-latest + if: ${{ !github.event.pull_request.draft }} + permissions: + pull-requests: write + strategy: + fail-fast: false + steps: + - uses: actions/checkout@v4 + with: + lfs: true + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: 3.12 + - name: Install llvmpipe and lavapipe for offscreen canvas + run: | + sudo apt-get update -y -qq + sudo apt-get install --no-install-recommends -y ffmpeg libegl1-mesa libgl1-mesa-dri libxcb-xfixes0-dev mesa-vulkan-drivers git-lfs + - name: Install dev dependencies + run: | + python -m pip install --upgrade pip setuptools + # remove pygfx from install_requires, we install using pygfx@main + sed -i "/pygfx/d" ./setup.py + pip install git+https://github.com/pygfx/pygfx.git@main + pip install -e ".["docs"]" + - name: Show wgpu backend + run: + python -c "from examples.tests.testutils import wgpu_backend; print(wgpu_backend)" + - name: build docs + run: | + cd docs + RTD_BUILD=1 make html SPHINXOPTS="-W --keep-going" + + # set environment variable `DOCS_VERSION_DIR` to either the pr-branch name, "dev", or the release version tag + - name: set output pr + if: ${{ github.ref != 'refs/heads/main' }} + # sets dir to the branch name when it's a PR + # ex: fastplotlib.org/ver/feature-branch + run: echo "DOCS_VERSION_DIR=$GITHUB_HEAD_REF" >> "$GITHUB_ENV" + + - name: set output release + if: ${{ github.ref_type == 'tag' }} + # sets dir to the release version tag, ex. v0.3.0 (I think...) + # ex: fastplotlib.org/ver/v0.3.0 + run: echo "DOCS_VERSION_DIR=$GITHUB_REF_NAME" >> "$GITHUB_ENV" + + - name: set output dev + if: ${{ github.ref == 'refs/heads/main' }} + # any push to main goes to fastplotlib.org/ver/dev + run: echo "DOCS_VERSION_DIR=dev" >> "$GITHUB_ENV" + + # upload docs via FTP + - name: Deploy docs + uses: SamKirkland/FTP-Deploy-Action@v4.3.5 + with: + server: ${{ secrets.DOCS_SERVER }} + username: ${{ secrets.DOCS_USERNAME }} + password: ${{ secrets.DOCS_PASSWORD }} + # built docs + local-dir: docs/build/html/ + # output subdir based on the previous if statements + server-dir: ./ver/${{ env.DOCS_VERSION_DIR }}/ + + # comment on PR to provide link to built docs + - name: Add PR link in comment + if: ${{ github.event_name == 'pull_request' }} + uses: mshick/add-pr-comment@v2 + with: + message: | + 📚 Docs preview built and uploaded! https://www.fastplotlib.org/ver/${{ env.DOCS_VERSION_DIR }} + + # also deploy to root if this is a new release + # i.e., fastplotlib.org/ points to docs for the latest release + - name: Deploy docs + if: ${{ github.ref_type == 'tag' }} + uses: SamKirkland/FTP-Deploy-Action@v4.3.5 + with: + server: ${{ secrets.DOCS_SERVER }} + username: ${{ secrets.DOCS_USERNAME }} + password: ${{ secrets.DOCS_PASSWORD }} + local-dir: docs/build/html/ + server-dir: ./ # deploy to the root dir + exclude: | # don't delete the /ver/ dir + **/ver/** diff --git a/.github/workflows/screenshots.yml b/.github/workflows/screenshots.yml index 0686b4445..25a3cf517 100644 --- a/.github/workflows/screenshots.yml +++ b/.github/workflows/screenshots.yml @@ -55,5 +55,5 @@ jobs: with: name: screenshots path: | - examples/desktop/screenshots/ + examples/screenshots/ examples/notebooks/screenshots/ diff --git a/README.md b/README.md index 2a5e33b9c..23aec8ac9 100644 --- a/README.md +++ b/README.md @@ -4,12 +4,12 @@ --- -[![CI](https://github.com/kushalkolar/fastplotlib/actions/workflows/ci.yml/badge.svg)](https://github.com/kushalkolar/fastplotlib/actions/workflows/ci.yml) +[![CI](https://github.com/fastplotlib/fastplotlib/actions/workflows/ci.yml/badge.svg)](https://github.com/fastplotlib/fastplotlib/actions/workflows/ci.yml) [![PyPI version](https://badge.fury.io/py/fastplotlib.svg)](https://badge.fury.io/py/fastplotlib) [![Documentation Status](https://readthedocs.org/projects/fastplotlib/badge/?version=latest)](https://fastplotlib.readthedocs.io/en/latest/?badge=latest) [![DOI](https://zenodo.org/badge/485481453.svg)](https://zenodo.org/doi/10.5281/zenodo.13365890) -[**Installation**](https://github.com/kushalkolar/fastplotlib#installation) | +[**Installation**](https://github.com/fastplotlib/fastplotlib#installation) | [**GPU Drivers**](https://github.com/kushalkolar/fastplotlib#graphics-drivers) | [**Documentation**](https://github.com/fastplotlib/fastplotlib#documentation) | [**Examples**](https://github.com/kushalkolar/fastplotlib#examples) | @@ -17,14 +17,9 @@ Next-gen plotting library built using the [`pygfx`](https://github.com/pygfx/pygfx) rendering engine that can utilize [Vulkan](https://en.wikipedia.org/wiki/Vulkan), [DX12](https://en.wikipedia.org/wiki/DirectX#DirectX_12), or [Metal](https://developer.apple.com/metal/) via WGPU, so it is very fast! `fastplotlib` is an expressive plotting library that enables rapid prototyping for large scale explorative scientific visualization. -![scipy-fpl](https://github.com/fastplotlib/fastplotlib/assets/9403332/b981a54c-05f9-443f-a8e4-52cd01cd802a) - -### SciPy 2023 Talk - -[![fpl_thumbnail](http://i3.ytimg.com/vi/Q-UJpAqljsU/hqdefault.jpg)](https://www.youtube.com/watch?v=Q-UJpAqljsU) - -Note that the API is currently evolving quickly. We recommend using the latest notebooks from the repo but the general -concepts are similar to those from the API shown in the video. +> **Note** +> +> `fastplotlib` is currently in the **late alpha stage**, but you're welcome to try it out or contribute! See our [Roadmap](https://github.com/kushalkolar/fastplotlib/issues/55). See this for a discussion on API stability: https://github.com/fastplotlib/fastplotlib/issues/121 # Supported frameworks @@ -36,43 +31,52 @@ concepts are similar to those from the API shown in the video. :heavy_check_mark: `wxPython` **Notes:**\ -:heavy_check_mark: Non-blocking Qt/PySide output is supported in ipython and notebooks by using [`%gui qt`](https://ipython.readthedocs.io/en/stable/interactive/magics.html#magic-gui). This **must** be called *before* importing `fastplotlib`! +:heavy_check_mark: Non-blocking interactive Qt/PySide output is supported in ipython and notebooks, see http://fastplotlib.org/ver/dev/user_guide/guide.html#using-fastplotlib-in-an-interactive-shell \ :grey_exclamation: We do not officially support `jupyter notebook` through `jupyter_rfb`, this may change with notebook v7\ +:grey_exclamation: We only officially support jupyterlab for use in notebook. This means we do not support vscode notebooks etc. Jupyterlab is the most reliable way to use `fastplotlib` in notebooks.\ :disappointed: [`jupyter_rfb`](https://github.com/vispy/jupyter_rfb) does not work in collab, see https://github.com/vispy/jupyter_rfb/pull/77 -> **Note** -> -> `fastplotlib` is currently in the **late alpha stage**, but you're welcome to try it out or contribute! See our [Roadmap](https://github.com/kushalkolar/fastplotlib/issues/55). See this for a discussion on API stability: https://github.com/fastplotlib/fastplotlib/issues/121 +We recommend sticking to jupyter-lab for notebooks. From our experience the usage on other platforms, such as vscode +notebooks, is not optimal. -# Documentation -http://fastplotlib.readthedocs.io/ +# Documentation -The examples are interactive if you run them locally on your computer. If someone wants to integrate `pyodide` with `pygfx` we would be able to have live interactive examples on the website! +http://www.fastplotlib.org/ Questions, issues, ideas? You are welcome to post an [issue](https://github.com/fastplotlib/fastplotlib/issues) or post on the [discussion forum](https://github.com/fastplotlib/fastplotlib/discussions)! :smiley: # Installation -### Minimal, use with your own `Qt` or `glfw` applications +To install use pip: + ```bash -pip install fastplotlib -``` +# with imgui and jupyterlab +pip install -U "fastplotlib[notebook,imgui]" -**This does not give you `PyQt`/`PySide` or `glfw`, you will have to install your preferred GUI framework separately**. +# minimal install, install glfw, pyqt6 or pyside6 separately +pip install -U fastplotlib -### Notebook -```bash -pip install "fastplotlib[notebook]" +# with imgui +pip install -U "fastplotlib[imgui]" + +# to use in jupyterlab, no imgui +pip install -U "fastplotlib[notebook]" ``` -**Strongly recommended: install `simplejpeg` for much faster notebook visualization, this requires you to first install [libjpeg-turbo](https://libjpeg-turbo.org/)** +We strongly recommend installing ``simplejpeg`` for use in notebooks, you must first install [libjpeg-turbo](https://libjpeg-turbo.org/) + +- If you use ``conda``, you can get ``libjpeg-turbo`` through conda. +- If you are on linux you can get it through your distro's package manager. +- For Windows and Mac compiled binaries are available on their release page: https://github.com/libjpeg-turbo/libjpeg-turbo/releases + +Once you have ``libjpeg-turbo``: ```bash pip install simplejpeg ``` -> **Note** +> **Note**[guide.rst](docs%2Fsource%2Fuser_guide%2Fguide.rst) > > `fastplotlib` and `pygfx` are fast evolving projects, the version available through pip might be outdated, you will need to follow the "For developers" instructions below if you want the latest features. You can find the release history here: https://github.com/fastplotlib/fastplotlib/releases @@ -95,42 +99,23 @@ Se [Contributing](https://github.com/fastplotlib/fastplotlib?tab=readme-ov-file# # Examples -Examples gallery: https://fastplotlib.readthedocs.io/en/latest/_gallery/index.html - -> **Note:** `fastplotlib` and `pygfx` are fast evolving, you will probably require the latest `pygfx` and `fastplotlib` from github to use the examples in the main branch. - -`fastplotlib` code is identical across notebook (`jupyter`), and desktop use with `Qt`/`PySide` or `glfw`. - -Even if you do not intend to use notebooks with `fastplotlib`, the `quickstart.ipynb` tutorial notebook is the best way to get familiar with the API: https://github.com/fastplotlib/fastplotlib/tree/main/examples/notebooks/quickstart.ipynb - -The specifics for running `fastplotlib` in different GUI frameworks are: -- Running in `glfw` requires a `fastplotlib.run()` call (which is really just a `wgpu` `run()` call) -- With `Qt` you can encapsulate it within a `QApplication`, see `examples/qt` -- Notebooks plots have ipywidget-based toolbars and widgets. There are plans to move toward an identical in-canvas toolbar with UI elements across all supported frameworks 😄 - -### Embedding in a `Qt` app - -See these for examples on embedding within a Qt app. Note that you can also use `fastplotlib` with qt interactively using `%gui qt` in jupyter or ipython. - -https://github.com/fastplotlib/fastplotlib/tree/main/examples/qt - -### Notebook examples +Examples gallery: http://fastplotlib.org/ver/dev/_gallery/index.html -Notebook examples are here, these include examples on selector tools. +User guide: http://fastplotlib.org/ver/dev/user_guide/guide.html -https://github.com/fastplotlib/fastplotlib/tree/main/examples/notebooks +`fastplotlib` code is identical across notebook (`jupyterlab`), and desktop use with `Qt`/`PySide` or `glfw`. -### Video +**Notebooks** -Our SciPy 2023 talk walks through numerous demos: https://github.com/fastplotlib/fastplotlib#scipy-talk +The `quickstart.ipynb` tutorial notebook is a great way to get familiar with the API: https://github.com/fastplotlib/fastplotlib/tree/main/examples/notebooks/quickstart.ipynb ## Graphics drivers -You will need a relatively modern GPU (newer integrated GPUs in CPUs are usually fine). Generally if your GPU is from 2017 or later it should be fine. +You will need a relatively modern GPU, modern integrated graphics are usually fine for many use cases. Generally if your GPU is from 2017 or later it should be fine. For more detailed information, such as use on cloud computing infrastructure, see: https://wgpu-py.readthedocs.io/en/stable/start.html#platform-requirements -Some more information on GPUs is here: https://fastplotlib.readthedocs.io/en/latest/user_guide/gpu.html +Some more information on GPUs is here: http://fastplotlib.org/ver/dev/user_guide/gpu.html ### Windows: Vulkan drivers should be installed by default on Windows 11, but you will need to install your GPU manufacturer's driver package (Nvidia or AMD). If you have an integrated GPU within your CPU, you might still need to install a driver package too, check your CPU manufacturer's info. @@ -161,6 +146,6 @@ WGPU uses Metal instead of Vulkan on Mac. You will need at least Mac OSX 10.13. # :heart: Contributing -We welcome contributions! See the contributing guide: https://github.com/kushalkolar/fastplotlib/blob/main/CONTRIBUTING.md +We welcome contributions! See the contributing guide: https://github.com/fastplotlib/fastplotlib/blob/main/CONTRIBUTING.md -You can also take a look at our [**Roadmap for 2025**](https://github.com/kushalkolar/fastplotlib/issues/55) and [**Issues**](https://github.com/kushalkolar/fastplotlib/issues) for ideas on how to contribute! +You can also take a look at our [**Roadmap for 2025**](https://github.com/fastplotlib/fastplotlib/issues/55) and [**Issues**](https://github.com/fastplotlib/fastplotlib/issues) for ideas on how to contribute! diff --git a/docs/source/_static/switcher.json b/docs/source/_static/switcher.json new file mode 100644 index 000000000..67f723e2f --- /dev/null +++ b/docs/source/_static/switcher.json @@ -0,0 +1,7 @@ +[ + { + "name": "dev/main", + "version": "dev", + "url": "http://www.fastplotlib.org/versions/dev" + } +] diff --git a/docs/source/_templates/autosummary/class.rst b/docs/source/_templates/autosummary/class.rst index d4fd5208b..3693c0102 100644 --- a/docs/source/_templates/autosummary/class.rst +++ b/docs/source/_templates/autosummary/class.rst @@ -3,3 +3,6 @@ .. currentmodule:: {{ module }} .. autoclass:: {{ objname }} + +.. minigallery:: fastplotlib.{{ objname }} + :add-heading: Examples diff --git a/docs/source/_templates/autosummary/method.rst b/docs/source/_templates/autosummary/method.rst index 306d2aab5..39daedd4b 100644 --- a/docs/source/_templates/autosummary/method.rst +++ b/docs/source/_templates/autosummary/method.rst @@ -3,3 +3,6 @@ .. currentmodule:: {{ module }} .. automethod:: {{ objname }} + +.. minigallery:: fastplotlib.{{ objname }} + :add-heading: Examples diff --git a/docs/source/_templates/autosummary/property.rst b/docs/source/_templates/autosummary/property.rst index c31bebe07..509e46b8a 100644 --- a/docs/source/_templates/autosummary/property.rst +++ b/docs/source/_templates/autosummary/property.rst @@ -3,3 +3,6 @@ .. currentmodule:: {{ module }} .. autoproperty:: {{ objname }} + +.. minigallery:: fastplotlib.{{ objname }} + :add-heading: Examples diff --git a/docs/source/api/index.rst b/docs/source/api/index.rst new file mode 100644 index 000000000..87c134782 --- /dev/null +++ b/docs/source/api/index.rst @@ -0,0 +1,15 @@ +API Reference +************* + +.. toctree:: + :caption: API Reference + :maxdepth: 2 + + layouts/index + graphics/index + graphic_features/index + selectors/index + ui/index + widgets/index + fastplotlib + utils diff --git a/docs/source/api/layouts/index.rst b/docs/source/api/layouts/index.rst new file mode 100644 index 000000000..51265fbf0 --- /dev/null +++ b/docs/source/api/layouts/index.rst @@ -0,0 +1,9 @@ +Layouts +******** + +.. toctree:: + :maxdepth: 1 + + imgui_figure + figure + subplot diff --git a/docs/source/conf.py b/docs/source/conf.py index 913cfd50f..36d965b83 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -21,7 +21,7 @@ pygfx.renderers.wgpu.set_wgpu_limits(**{"max-texture-dimension2d": MAX_TEXTURE_SIZE}) ROOT_DIR = Path(__file__).parents[1].parents[0] # repo root -EXAMPLES_DIR = Path.joinpath(ROOT_DIR, "examples", "desktop") +EXAMPLES_DIR = Path.joinpath(ROOT_DIR, "examples") sys.path.insert(0, str(ROOT_DIR)) @@ -55,16 +55,17 @@ "remove_config_comments": True, "subsection_order": ExplicitOrder( [ - "../../examples/desktop/image", - "../../examples/desktop/image_widget", - "../../examples/desktop/gridplot", - "../../examples/desktop/line", - "../../examples/desktop/line_collection", - "../../examples/desktop/scatter", - "../../examples/desktop/heatmap", - "../../examples/desktop/misc", - "../../examples/desktop/selectors", - "../../examples/desktop/guis" + "../../examples/image", + "../../examples/image_widget", + "../../examples/gridplot", + "../../examples/line", + "../../examples/line_collection", + "../../examples/scatter", + "../../examples/heatmap", + "../../examples/misc", + "../../examples/selection_tools", + "../../examples/guis", + "../../examples/qt", ] ), "ignore_pattern": r'__init__\.py', @@ -91,7 +92,17 @@ # -- Options for HTML output ------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output -html_theme = "furo" +html_theme = "pydata_sphinx_theme" + +html_theme_options = { + "navbar_end": ["theme-switcher", "version-switcher", "navbar-icon-links"], + "show_version_warning_banner": True, + "check_switcher": True, + "switcher": { + "json_url": "http://www.fastplotlib.org/_static/switcher.json", + "version_match": release + } +} html_static_path = ["_static"] html_logo = "_static/logo.png" @@ -106,14 +117,8 @@ intersphinx_mapping = { "python": ("https://docs.python.org/3", None), - "numpy": ("https://numpy.org/doc/stable/", None), - "pygfx": ("https://docs.pygfx.org/stable/", None), + "numpy": ("https://numpy.org/doc/stable", None), + "pygfx": ("https://docs.pygfx.org/stable", None), "wgpu": ("https://wgpu-py.readthedocs.io/en/latest", None), - "fastplotlib": ("https://fastplotlib.readthedocs.io/en/latest/", None), -} - -html_theme_options = { - "source_repository": "https://github.com/fastplotlib/fastplotlib", - "source_branch": "main", - "source_directory": "docs/", + # "fastplotlib": ("https://www.fastplotlib.org/", None), } diff --git a/docs/source/generate_api.py b/docs/source/generate_api.py index dbe1d8005..488b28466 100644 --- a/docs/source/generate_api.py +++ b/docs/source/generate_api.py @@ -36,6 +36,35 @@ os.makedirs(source_dir, exist_ok=True) +# this way we can just add the entire api dir to gitignore and generate before pushing +with open(API_DIR.joinpath("fastplotlib.rst"), "w") as f: + f.write( + "fastplotlib\n" + "***********\n\n" + ".. currentmodule:: fastplotlib\n\n" + + ".. autofunction:: fastplotlib.pause_events\n\n" + + ".. autofunction:: fastplotlib.enumerate_adapters\n\n" + + ".. autofunction:: fastplotlib.select_adapter\n\n" + + ".. autofunction:: fastplotlib.print_wgpu_report\n\n" + + ".. autofunction:: fastplotlib.run\n" + ) + +with open(API_DIR.joinpath("utils.rst"), "w") as f: + f.write( + "fastplotlib.utils\n" + "*****************\n\n" + + "..currentmodule:: fastplotlib.utils\n" + "..automodule:: fastplotlib.utils.functions\n" + " : members:\n" + ) + + def get_public_members(cls) -> Tuple[List[str], List[str]]: """ Returns (public_methods, public_properties) @@ -165,6 +194,20 @@ def main(): source_path=LAYOUTS_DIR.joinpath("subplot.rst"), ) + # layouts classes index file + with open(LAYOUTS_DIR.joinpath("index.rst"), "w") as f: + f.write( + f"Layouts\n" + f"********\n" + f"\n" + f".. toctree::\n" + f" :maxdepth: 1\n" + f"\n" + f" imgui_figure\n" + f" figure\n" + f" subplot\n" + ) + # the rest of this is a mess and can be refactored later graphic_classes = [getattr(graphics, g) for g in graphics.__all__] @@ -299,6 +342,23 @@ def main(): with open(API_DIR.joinpath("utils.rst"), "w") as f: f.write(utils_str) + # nake API index file + with open(API_DIR.joinpath("index.rst"), "w") as f: + f.write( + "API Reference\n" + "*************\n\n" + ".. toctree::\n" + " :caption: API Reference\n" + " :maxdepth: 2\n\n" + " layouts/index\n" + " graphics/index\n" + " graphic_features/index\n" + " selectors/index\n" + " ui/index\n" + " widgets/index\n" + " fastplotlib\n" + " utils\n" + ) if __name__ == "__main__": main() diff --git a/docs/source/index.rst b/docs/source/index.rst index 4caa7fc7e..2794bc17e 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -2,52 +2,39 @@ Welcome to fastplotlib's documentation! ======================================= .. toctree:: - :caption: User Guide - :maxdepth: 1 + :caption: Getting started + :maxdepth: 2 - Guide - GPU Info - FAQ + user_guide/index .. toctree:: - :maxdepth: 1 + :maxdepth: 2 :caption: API - fastplotlib - Figure - ImguiFigure - Subplot - Graphics - Graphic Features - Selectors - UI - Widgets - Utils + api/index .. toctree:: :caption: Gallery - :maxdepth: 1 + :maxdepth: 1 - Gallery <_gallery/index> + _gallery/index Summary ======= -Next-gen plotting library built using the `pygfx `_ render engine utilizing `Vulkan `_, `DX12 `_, or `Metal `_ via `WGPU `_, so it is very fast! ``fastplotlib`` is an expressive plotting library that enables rapid prototyping for large scale exploratory scientific visualization. ``fastplotlib`` will run on any framework that ``pygfx`` runs on, this includes ``glfw``, ``Qt`` and ``jupyter lab`` - -Installation -============ - -For installation please see the instructions on GitHub: - -https://github.com/kushalkolar/fastplotlib#installation +Next-gen plotting library built using the `pygfx `_ render engine utilizing +`Vulkan `_, `DX12 `_, or +`Metal `_ via `WGPU `_, so it is very fast! +``fastplotlib`` is an expressive plotting library that enables rapid prototyping for large scale exploratory scientific +visualization. ``fastplotlib`` will run on any framework that ``pygfx`` runs on, this includes ``glfw``, ``Qt`` +and ``jupyter lab`` Contributing ============ -Contributions are welcome! See the contributing guide on GitHub: https://github.com/kushalkolar/fastplotlib/blob/master/CONTRIBUTING.md. +Contributions are welcome! See the `contributing guide on GitHub `_ -Also take a look at the `Roadmap 2025 `_ for future plans or ways in which you could contribute. +Also take a look at the `Roadmap 2025 `_ for future plans or ways in which you could contribute. Indices and tables ================== diff --git a/docs/source/user_guide/faq.rst b/docs/source/user_guide/faq.rst index ebf17fcd6..ffa140ac2 100644 --- a/docs/source/user_guide/faq.rst +++ b/docs/source/user_guide/faq.rst @@ -1,15 +1,15 @@ -fastplotlib FAQ -=============== +FAQ +=== -What is `fastplotlib`? ----------------------- +What is ``fastplotlib``? +------------------------ `fastplotlib` is a scientific plotting library built on top of the `pygfx `_ rendering engine that leverages new graphics APIs and modern GPU hardware to create fast and interactive visualizations. -What can I do with `fastplotlib`? ---------------------------------- +What can I do with ``fastplotlib``? +----------------------------------- `fastplotlib` allows for: - GPU accelerated visualization @@ -28,8 +28,8 @@ Do I need a GPU? Limited software rendering using just the CPU is supported on linux using lavapipe, but this is mostly only useful for testing purposes. -How does `fastplotlib` relate to `matplotlib`? ----------------------------------------------- +How does ``fastplotlib`` relate to ``matplotlib``? +-------------------------------------------------- `fastplotlib` is **not** related to `matplotlib` in any way. @@ -41,8 +41,8 @@ How does `fastplotlib` relate to `matplotlib`? why `fastplotlib` can have an array-like API for plotting. We believe that these design choices make it much easier to learn how to use the library and provide fine-grained control over your visualizations. See the "How can I learn to use `fastplotlib`?" section below. -How can I learn to use `fastplotlib`? -------------------------------------- +How can I learn to use ``fastplotlib``? +--------------------------------------- We want `fastplotlib` to be easy to learn and use. To get started with the library we recommend taking a look at our `guide `_ and `examples gallery `_. @@ -50,14 +50,14 @@ How can I learn to use `fastplotlib`? In general, if you are familiar with numpy and array notation you will already have a intuitive understanding of interacting with your data in `fastplotlib`. If you have any questions, please do not hesitate to post an issue or discussion forum post. -Should I use `fastplotlib` for making publication figures? ----------------------------------------------------------- +Should I use ``fastplotlib`` for making publication figures? +------------------------------------------------------------ While `fastplotlib` figures can be exported to PNG using ``figure.export()``, `fastplotlib` is not intended for creating *static* publication figures. There are many other libraries that are well-suited for this task. -How does `fastplotlib` handle data loading? -------------------------------------------- +How does ``fastplotlib`` handle data loading? +--------------------------------------------- `fastplotlib` is a plotting library and not a data handling or data loading library. These tasks are outside of the scope of the library. @@ -65,8 +65,8 @@ How does `fastplotlib` handle data loading? In general, if your data is an array-like object, `fastplotlib` should be able to use it. However, if you have any problems using your data objects, please do not hesitate to post an issue! See this `issue `_ for more details. -What is the scope of `fastplotlib`? ------------------------------------ +What is the scope of ``fastplotlib``? +------------------------------------- While the capabilities are very far-reaching, we would like to emphasize that `fastplotlib` is a general-purpose plotting library focused on scientific visualization. More specifically, we aim to develop the tools necessary for users to build fast and interactive visualizations for a variety of scientific domains including but not limited to @@ -86,8 +86,8 @@ What types of PRs are we willing to accept? Lastly, documentation is a critical part of open-source software and makes learning/using our tool much easier. We welcome all PRs that add missing or needed documentation of the codebase. If you find a piece of the codebase that is confusing or does not have proper documentation, please also feel free to post an issue on the repo! -What frameworks does `fastplotlib` support? -------------------------------------------- +What frameworks does ``fastplotlib`` support? +--------------------------------------------- The short answer is that `fastplotlib` can run on anything that `pygfx` runs on. This includes, - `jupyter lab` using `jupyter_rfb` @@ -98,8 +98,8 @@ What frameworks does `fastplotlib` support? Note: Use in Google Colab is not highly functional. We recommend using an inexpensive alternative cloud provider such as CodeOcean or Lambda Cloud. We have tested these and `fastplotlib` works very well. -How can I use `fastplotlib` interactively? ------------------------------------------- +How can I use ``fastplotlib`` interactively? +-------------------------------------------- There are multiple ways to use fastplotlib interactively. @@ -127,4 +127,4 @@ How can I use `fastplotlib` interactively? Why the parrot logo? -------------------- - The logo is a `swift parrot `_, they are the fastest species of parrot and they are colorful like fastplotlib visualizations :D \ No newline at end of file + The logo is a `swift parrot `_, they are the fastest species of parrot and they are colorful like fastplotlib visualizations :D diff --git a/docs/source/user_guide/gpu.rst b/docs/source/user_guide/gpu.rst index f4ef89a0c..00653133c 100644 --- a/docs/source/user_guide/gpu.rst +++ b/docs/source/user_guide/gpu.rst @@ -1,14 +1,14 @@ GPU Info and selection -********************** +====================== FAQ -=== +--- 1. Do I need a GPU? Technically no, you can perform limited software rendering on linux using lavapipe (see drivers link below). However ``fastplotlib`` is intentionally built for realtime rendering using the latest GPU technologies, so we strongly -recommend that you use a GPU. +recommend that you use a GPU. Note that modern integrated graphics is often sufficient for many use-cases. 2. My kernel keeps crashing. @@ -24,7 +24,7 @@ If you aren't able to solve it please post an issue on GitHub. :) - Probably driver issues (see next section). Drivers -======= +------- See the README: https://github.com/fastplotlib/fastplotlib?tab=readme-ov-file#graphics-drivers @@ -32,10 +32,10 @@ If you notice weird graphic artifacts, things not rendering, or other glitches t drivers. GPU Info -======== +-------- View available adapters ------------------------ +^^^^^^^^^^^^^^^^^^^^^^^ You can get a summary of all adapters that are available to ``WGPU`` like this:: @@ -71,7 +71,7 @@ General description of the fields: For more information on the fields see: https://gpuweb.github.io/gpuweb/#gpuadapterinfo Adapter currently in use ------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^ If you want to know the adapter that a figure is using you can check the adapter on the renderer:: @@ -85,7 +85,7 @@ If you want to know the adapter that a figure is using you can check the adapter Diagnostic info ---------------- +^^^^^^^^^^^^^^^ After creating a figure you can view WGPU diagnostic info like this:: @@ -259,7 +259,7 @@ Example output:: Select GPU (adapter) -==================== +-------------------- You can select an adapter by passing one of the ``wgpu.GPUAdapter`` instances returned by ``fpl.enumerate_adapters()`` to ``fpl.select_adapter()``:: diff --git a/docs/source/user_guide/guide.rst b/docs/source/user_guide/guide.rst index 4c482e03a..0496054a7 100644 --- a/docs/source/user_guide/guide.rst +++ b/docs/source/user_guide/guide.rst @@ -1,5 +1,5 @@ -The `fastplotlib` guide -======================= +User Guide +========== Installation ------------ @@ -8,14 +8,29 @@ To install use pip: .. code-block:: + # with imgui and jupyterlab + pip install -U "fastplotlib[notebook,imgui]" + + # minimal install, install glfw, pyqt6 or pyside6 separately pip install -U fastplotlib -or install the bleeding edge from Github: + # with imgui + pip install -U "fastplotlib[imgui]" -.. code-block:: + # to use in jupyterlab, no imgui + pip install -U "fastplotlib[notebook]" + +We strongly recommend installing ``simplejpeg`` for use in notebooks, you must first install `libjpeg-turbo `_. - pip install git+https://github.com/fastplotlib/fastplotlib.git@main +- If you use ``conda``, you can get ``libjpeg-turbo`` through conda. +- If you are on linux you can get it through your distro's package manager. +- For Windows and Mac compiled binaries are available on their release page: https://github.com/libjpeg-turbo/libjpeg-turbo/releases +Once you have ``libjpeg-turbo``: + +.. code-block:: + + pip install simplejpeg What is ``fastplotlib``? ------------------------ @@ -56,7 +71,8 @@ This is just a simple example of how the ``fastplotlib`` API works to create a p However, we are just scratching the surface of what is possible with ``fastplotlib``. Next, let's take a look at the building blocks of ``fastplotlib`` and how they can be used to create more complex visualizations. -**Figure** +Figure +------ The starting point for creating any visualization in ``fastplotlib`` is a ``Figure`` object. This can be a single plot or a grid of subplots. The ``Figure`` object houses and takes care of the underlying rendering components such as the camera, controller, renderer, and canvas. @@ -68,7 +84,8 @@ indexing (i.e. ``fig_object[i ,j]``). After defining a ``Figure``, we can begin to add ``Graphic`` objects. -**Graphics** +Graphics +-------- A ``Graphic`` can be an image, a line, a scatter, a collection of lines, and more. All graphics can also be given a convenient ``name``. This allows graphics to be easily accessed from figures:: @@ -90,6 +107,8 @@ to be easily accessed from figures:: .. +See the examples gallery for examples on how to create and interactive with all the various types of graphics. + Graphics also have mutable properties that can be linked to events. Some of these properties, such as the ``data`` or ``colors`` of a line can even be indexed, allowing for the creation of very powerful visualizations. @@ -408,7 +427,8 @@ event_type: "selection" | value | np.ndarray | new [min, max] of selection | +----------+------------+-----------------------------+ -**Renderer Events:** +Renderer Events +^^^^^^^^^^^^^^^ You can also add events to a ``Figure`` object's renderer. This is useful for defining click events where you want to map your click position to the nearest graphic object for example. @@ -572,10 +592,10 @@ You can also make custom GUIs and embed them within the canvas, see the examples .. image:: ../_static/guide_imgui.png -Using ``fastplotlib`` interactively ------------------------------------ +Using ``fastplotlib`` in an interactive shell +--------------------------------------------- -There are multiple ways to use ``fastplotlib`` interactively. +There are multiple ways to use ``fastplotlib`` in interactive shells, such as ipython. 1) Jupyter @@ -597,4 +617,3 @@ Lastly, users can also force using ``glfw`` by specifying this as an argument wh 2) IPython Users can select between using a Qt backend or gflw using the same methods as above. - diff --git a/docs/source/user_guide/index.rst b/docs/source/user_guide/index.rst new file mode 100644 index 000000000..59189be22 --- /dev/null +++ b/docs/source/user_guide/index.rst @@ -0,0 +1,10 @@ +User Guide +********** + +.. toctree:: + :caption: User Guide + :maxdepth: 2 + + guide + gpu + faq diff --git a/examples/README.rst b/examples/README.rst index 138ec748b..fd8eb8da2 100644 --- a/examples/README.rst +++ b/examples/README.rst @@ -1,2 +1,2 @@ -Examples that use fastplotlib -============================= +Examples Gallery +**************** diff --git a/examples/desktop/data/iris.npy b/examples/data/iris.npy similarity index 100% rename from examples/desktop/data/iris.npy rename to examples/data/iris.npy diff --git a/examples/desktop/README.rst b/examples/desktop/README.rst deleted file mode 100644 index e69de29bb..000000000 diff --git a/examples/desktop/gridplot/README.rst b/examples/gridplot/README.rst similarity index 100% rename from examples/desktop/gridplot/README.rst rename to examples/gridplot/README.rst diff --git a/examples/desktop/gridplot/gridplot.py b/examples/gridplot/gridplot.py similarity index 100% rename from examples/desktop/gridplot/gridplot.py rename to examples/gridplot/gridplot.py diff --git a/examples/desktop/gridplot/gridplot_non_square.py b/examples/gridplot/gridplot_non_square.py similarity index 100% rename from examples/desktop/gridplot/gridplot_non_square.py rename to examples/gridplot/gridplot_non_square.py diff --git a/examples/desktop/gridplot/multigraphic_gridplot.py b/examples/gridplot/multigraphic_gridplot.py similarity index 100% rename from examples/desktop/gridplot/multigraphic_gridplot.py rename to examples/gridplot/multigraphic_gridplot.py diff --git a/examples/desktop/guis/README.rst b/examples/guis/README.rst similarity index 50% rename from examples/desktop/guis/README.rst rename to examples/guis/README.rst index 9cbf4d424..0271f959d 100644 --- a/examples/desktop/guis/README.rst +++ b/examples/guis/README.rst @@ -1,2 +1,2 @@ -ImGUI for within-canvas GUIs +ImGUI for within canvas GUIs ============================ diff --git a/examples/desktop/guis/image_widget_imgui.py b/examples/guis/image_widget_imgui.py similarity index 100% rename from examples/desktop/guis/image_widget_imgui.py rename to examples/guis/image_widget_imgui.py diff --git a/examples/desktop/guis/imgui_basic.py b/examples/guis/imgui_basic.py similarity index 100% rename from examples/desktop/guis/imgui_basic.py rename to examples/guis/imgui_basic.py diff --git a/examples/desktop/heatmap/README.rst b/examples/heatmap/README.rst similarity index 100% rename from examples/desktop/heatmap/README.rst rename to examples/heatmap/README.rst diff --git a/examples/desktop/heatmap/heatmap.py b/examples/heatmap/heatmap.py similarity index 100% rename from examples/desktop/heatmap/heatmap.py rename to examples/heatmap/heatmap.py diff --git a/examples/desktop/image/README.rst b/examples/image/README.rst similarity index 100% rename from examples/desktop/image/README.rst rename to examples/image/README.rst diff --git a/examples/desktop/image/image_cmap.py b/examples/image/image_cmap.py similarity index 100% rename from examples/desktop/image/image_cmap.py rename to examples/image/image_cmap.py diff --git a/examples/desktop/image/image_rgb.py b/examples/image/image_rgb.py similarity index 100% rename from examples/desktop/image/image_rgb.py rename to examples/image/image_rgb.py diff --git a/examples/desktop/image/image_rgbvminvmax.py b/examples/image/image_rgbvminvmax.py similarity index 100% rename from examples/desktop/image/image_rgbvminvmax.py rename to examples/image/image_rgbvminvmax.py diff --git a/examples/desktop/image/image_simple.py b/examples/image/image_simple.py similarity index 100% rename from examples/desktop/image/image_simple.py rename to examples/image/image_simple.py diff --git a/examples/desktop/image/image_small.py b/examples/image/image_small.py similarity index 100% rename from examples/desktop/image/image_small.py rename to examples/image/image_small.py diff --git a/examples/desktop/image/image_vminvmax.py b/examples/image/image_vminvmax.py similarity index 100% rename from examples/desktop/image/image_vminvmax.py rename to examples/image/image_vminvmax.py diff --git a/examples/desktop/image_widget/README.rst b/examples/image_widget/README.rst similarity index 100% rename from examples/desktop/image_widget/README.rst rename to examples/image_widget/README.rst diff --git a/examples/desktop/image_widget/image_widget.py b/examples/image_widget/image_widget.py similarity index 100% rename from examples/desktop/image_widget/image_widget.py rename to examples/image_widget/image_widget.py diff --git a/examples/desktop/image_widget/image_widget_grid.py b/examples/image_widget/image_widget_grid.py similarity index 100% rename from examples/desktop/image_widget/image_widget_grid.py rename to examples/image_widget/image_widget_grid.py diff --git a/examples/desktop/image_widget/image_widget_single_video.py b/examples/image_widget/image_widget_single_video.py similarity index 100% rename from examples/desktop/image_widget/image_widget_single_video.py rename to examples/image_widget/image_widget_single_video.py diff --git a/examples/desktop/image_widget/image_widget_videos.py b/examples/image_widget/image_widget_videos.py similarity index 100% rename from examples/desktop/image_widget/image_widget_videos.py rename to examples/image_widget/image_widget_videos.py diff --git a/examples/desktop/line/README.rst b/examples/line/README.rst similarity index 100% rename from examples/desktop/line/README.rst rename to examples/line/README.rst diff --git a/examples/desktop/line/line.py b/examples/line/line.py similarity index 100% rename from examples/desktop/line/line.py rename to examples/line/line.py diff --git a/examples/desktop/line/line_cmap.py b/examples/line/line_cmap.py similarity index 95% rename from examples/desktop/line/line_cmap.py rename to examples/line/line_cmap.py index b7dfe4424..81895e17b 100644 --- a/examples/desktop/line/line_cmap.py +++ b/examples/line/line_cmap.py @@ -2,7 +2,7 @@ Line Plot Colormap ================== -Example showing cosine, sine, sinc lines. +Example showing basic colormapping with lines """ # test_example = true diff --git a/examples/line/line_cmap_more.py b/examples/line/line_cmap_more.py new file mode 100644 index 000000000..c05f36797 --- /dev/null +++ b/examples/line/line_cmap_more.py @@ -0,0 +1,56 @@ +""" +Lines more colormapping +======================= + +Example showing more on colormapping with lines +""" + +# test_example = true +# sphinx_gallery_pygfx_docs = 'screenshot' + +import numpy as np +import fastplotlib as fpl + +xs = np.linspace(0, 10 * np.pi, 100) +# sine wave +ys = np.sin(xs) +sine = np.column_stack([xs, ys]) + +# cosine wave +ys = np.cos(xs) +cosine = np.column_stack([xs, ys]) + +figure = fpl.Figure(size=(700, 560)) + +# basic white line +line0 = figure[0, 0].add_line(sine, thickness=10) + +# set colormap along line datapoints, use an offset to place it above the previous line +line1 = figure[0, 0].add_line(sine, thickness=10, cmap="jet", offset=(0, 2, 0)) + +# set colormap by mapping data using a transform +# here we map the color using the y-values of the sine data +# i.e., the color is a function of sine(x) +line2 = figure[0, 0].add_line(sine, thickness=10, cmap="jet", cmap_transform=sine[:, 1], offset=(0, 4, 0)) + +# make a line and change the cmap afterward, here we are using the cosine instead fot the transform +line3 = figure[0, 0].add_line(sine, thickness=10, cmap="jet", cmap_transform=cosine[:, 1], offset=(0, 6, 0)) +# change the cmap +line3.cmap = "bwr" + +# use quantitative colormaps with categorical cmap_transforms +labels = [0] * 25 + [1] * 5 + [2] * 50 + [3] * 20 +line4 = figure[0, 0].add_line(sine, thickness=10, cmap="tab10", cmap_transform=labels, offset=(0, 8, 0)) + +# some text labels +for i in range(5): + figure[0, 0].add_text(f"line{i}", font_size=20, offset=(1, (i * 2) + 1.5, 0)) + +figure.show(maintain_aspect=False) + + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.run() diff --git a/examples/desktop/line/line_colorslice.py b/examples/line/line_colorslice.py similarity index 100% rename from examples/desktop/line/line_colorslice.py rename to examples/line/line_colorslice.py diff --git a/examples/desktop/line/line_dataslice.py b/examples/line/line_dataslice.py similarity index 100% rename from examples/desktop/line/line_dataslice.py rename to examples/line/line_dataslice.py diff --git a/examples/desktop/line_collection/README.rst b/examples/line_collection/README.rst similarity index 100% rename from examples/desktop/line_collection/README.rst rename to examples/line_collection/README.rst diff --git a/examples/desktop/line_collection/line_collection.py b/examples/line_collection/line_collection.py similarity index 100% rename from examples/desktop/line_collection/line_collection.py rename to examples/line_collection/line_collection.py diff --git a/examples/desktop/line_collection/line_collection_cmap_values.py b/examples/line_collection/line_collection_cmap_values.py similarity index 100% rename from examples/desktop/line_collection/line_collection_cmap_values.py rename to examples/line_collection/line_collection_cmap_values.py diff --git a/examples/desktop/line_collection/line_collection_cmap_values_qualitative.py b/examples/line_collection/line_collection_cmap_values_qualitative.py similarity index 100% rename from examples/desktop/line_collection/line_collection_cmap_values_qualitative.py rename to examples/line_collection/line_collection_cmap_values_qualitative.py diff --git a/examples/desktop/line_collection/line_collection_colors.py b/examples/line_collection/line_collection_colors.py similarity index 100% rename from examples/desktop/line_collection/line_collection_colors.py rename to examples/line_collection/line_collection_colors.py diff --git a/examples/desktop/line_collection/line_collection_slicing.py b/examples/line_collection/line_collection_slicing.py similarity index 100% rename from examples/desktop/line_collection/line_collection_slicing.py rename to examples/line_collection/line_collection_slicing.py diff --git a/examples/desktop/line_collection/line_stack.py b/examples/line_collection/line_stack.py similarity index 100% rename from examples/desktop/line_collection/line_stack.py rename to examples/line_collection/line_stack.py diff --git a/examples/desktop/line_collection/line_stack_3d.py b/examples/line_collection/line_stack_3d.py similarity index 100% rename from examples/desktop/line_collection/line_stack_3d.py rename to examples/line_collection/line_stack_3d.py diff --git a/examples/misc/garbage_collection.py b/examples/misc-dev/garbage_collection.py similarity index 100% rename from examples/misc/garbage_collection.py rename to examples/misc-dev/garbage_collection.py diff --git a/examples/misc/large_img.py b/examples/misc-dev/large_img.py similarity index 100% rename from examples/misc/large_img.py rename to examples/misc-dev/large_img.py diff --git a/examples/misc/selector_performance.ipynb b/examples/misc-dev/selector_performance.ipynb similarity index 100% rename from examples/misc/selector_performance.ipynb rename to examples/misc-dev/selector_performance.ipynb diff --git a/examples/desktop/misc/README.rst b/examples/misc/README.rst similarity index 100% rename from examples/desktop/misc/README.rst rename to examples/misc/README.rst diff --git a/examples/desktop/misc/cycle_animation.py b/examples/misc/cycle_animation.py similarity index 100% rename from examples/desktop/misc/cycle_animation.py rename to examples/misc/cycle_animation.py diff --git a/examples/desktop/misc/em_wave_animation.py b/examples/misc/em_wave_animation.py similarity index 100% rename from examples/desktop/misc/em_wave_animation.py rename to examples/misc/em_wave_animation.py diff --git a/examples/desktop/misc/image_animation.py b/examples/misc/image_animation.py similarity index 100% rename from examples/desktop/misc/image_animation.py rename to examples/misc/image_animation.py diff --git a/examples/desktop/misc/line3d_animation.py b/examples/misc/line3d_animation.py similarity index 100% rename from examples/desktop/misc/line3d_animation.py rename to examples/misc/line3d_animation.py diff --git a/examples/desktop/misc/line_animation.py b/examples/misc/line_animation.py similarity index 100% rename from examples/desktop/misc/line_animation.py rename to examples/misc/line_animation.py diff --git a/examples/desktop/misc/lorenz_animation.py b/examples/misc/lorenz_animation.py similarity index 100% rename from examples/desktop/misc/lorenz_animation.py rename to examples/misc/lorenz_animation.py diff --git a/examples/desktop/misc/multiplot_animation.py b/examples/misc/multiplot_animation.py similarity index 100% rename from examples/desktop/misc/multiplot_animation.py rename to examples/misc/multiplot_animation.py diff --git a/examples/desktop/misc/scatter_animation.py b/examples/misc/scatter_animation.py similarity index 100% rename from examples/desktop/misc/scatter_animation.py rename to examples/misc/scatter_animation.py diff --git a/examples/misc/scatter_sizes_animation.py b/examples/misc/scatter_sizes_animation.py new file mode 100644 index 000000000..aeb0466b0 --- /dev/null +++ b/examples/misc/scatter_sizes_animation.py @@ -0,0 +1,47 @@ +""" +Scatter sizes animation +======================= + +Animate scatter sizes +""" + +# test_example = false +# sphinx_gallery_pygfx_docs = 'animate 6s' + +import numpy as np +import fastplotlib as fpl + +xs = np.linspace(0, 10 * np.pi, 1_000) +# sine wave +ys = np.sin(xs) +data = np.column_stack([xs, ys]) + +sizes = np.abs(ys) * 5 + +figure = fpl.Figure(size=(700, 560)) + +figure[0, 0].add_scatter(data, sizes=sizes, name="sine") + + +i = 0 +def update_sizes(subplot): + global i + + xs = np.linspace(0.1 * i, (10 * np.pi) + (0.1 * i), 1_000) + sizes = np.abs(np.sin(xs)) * 5 + + subplot["sine"].sizes = sizes + + i += 1 + + +figure[0, 0].add_animations(update_sizes) + +figure.show(maintain_aspect=False) + + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.run() diff --git a/examples/desktop/misc/simple_event.py b/examples/misc/simple_event.py similarity index 100% rename from examples/desktop/misc/simple_event.py rename to examples/misc/simple_event.py diff --git a/examples/notebooks/heatmap.ipynb b/examples/notebooks/heatmap.ipynb deleted file mode 100644 index 08fd72501..000000000 --- a/examples/notebooks/heatmap.ipynb +++ /dev/null @@ -1,210 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "d8c90f4b-b635-4027-b7d5-080d77bd40a3", - "metadata": {}, - "source": [ - "# Looking at very large arrays" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "49b2498d-56ae-4559-9282-c8484f3e6b6d", - "metadata": { - "tags": [] - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "WARNING: The name 'ylorbr' is an alias for 'colorbrewer:YlOrBr', but is also available as: 'tol:YlOrBr'.\n", - "To silence this warning, use a fully namespaced name.\n", - "WARNING: The name 'rdbu' is an alias for 'colorbrewer:RdBu', but is also available as: 'vispy:RdBu'.\n", - "To silence this warning, use a fully namespaced name.\n", - "WARNING: The name 'rainbow' is an alias for 'gnuplot:rainbow', but is also available as: 'yorick:rainbow'.\n", - "To silence this warning, use a fully namespaced name.\n", - "WARNING: The name 'ice' is an alias for 'cmocean:ice', but is also available as: 'imagej:ice, vispy:ice'.\n", - "To silence this warning, use a fully namespaced name.\n", - "WARNING: The name 'fire' is an alias for 'imagej:fire', but is also available as: 'vispy:fire'.\n", - "To silence this warning, use a fully namespaced name.\n", - "WARNING: The name 'prgn' is an alias for 'colorbrewer:PRGn', but is also available as: 'tol:PRGn'.\n", - "To silence this warning, use a fully namespaced name.\n" - ] - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "7e56de31fa0c41fa8ac48dc276c157b9", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Image(value=b'\\x89PNG\\r\\n\\x1a\\n\\x00\\x00\\x00\\rIHDR\\x00\\x00\\x01,\\x00\\x00\\x007\\x08\\x06\\x00\\x00\\x00\\xb6\\x1bw\\x99\\x…" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "WGPU: enumerate_adapters() is deprecated, use enumerate_adapters_sync() instead.\n", - "Unable to find extension: VK_EXT_swapchain_colorspace\n", - "WGPU: request_adapter() is deprecated, use request_adapter_sync() instead.\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Available devices:\n", - "✅ (default) | AMD RADV POLARIS10 (ACO) | DiscreteGPU | Vulkan | Mesa 20.3.5 (ACO)\n", - "❗ | llvmpipe (LLVM 11.0.1, 256 bits) | CPU | Vulkan | Mesa 20.3.5 (LLVM 11.0.1)\n", - "✅ | NVIDIA GeForce RTX 3080 | DiscreteGPU | Vulkan | 530.30.02\n", - "❗ | Radeon RX 570 Series (POLARIS10, DRM 3.40.0, 5.10.0-21-amd64, LLVM 11.0.1) | Unknown | OpenGL | 4.6 (Core Profile) Mesa 20.3.5\n" - ] - } - ], - "source": [ - "import numpy as np\n", - "import fastplotlib as fpl" - ] - }, - { - "cell_type": "markdown", - "id": "908f93f8-68c3-4a36-8f40-e0aab560955d", - "metadata": {}, - "source": [ - "## Generate some sine and cosine data" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "40718465-abf6-4727-8bd7-4acdd59843d5", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "xs = np.linspace(0, 1_000, 20_000)\n", - "\n", - "sine = np.sin(np.sqrt(xs))\n", - "\n", - "data = np.vstack([sine * i for i in range(10_000)])" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "02b072eb-2909-40c8-8739-950f07efbbc2", - "metadata": { - "tags": [] - }, - "outputs": [ - { - "data": { - "text/plain": [ - "(10000, 20000)" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "data.shape" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "84deb31b-5464-4cce-a938-694371011021", - "metadata": { - "tags": [] - }, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "956570245b55414e9b89bca0dc227535", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "RFBOutputContext()" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "WGPU: request_device() is deprecated, use request_device_sync() instead.\n" - ] - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "ce013002bc6f4d1893c14a25bd3ae55b", - "version_major": 2, - "version_minor": 0 - }, - "text/html": [ - "
snapshot
" - ], - "text/plain": [ - "JupyterWgpuCanvas()" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "fig = fpl.Figure()\n", - "\n", - "fig[0, 0].add_image(data, cmap=\"viridis\")\n", - "\n", - "fig.show(maintain_aspect=False)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "df3f8994-0f5b-4578-a36d-4cd9bf0733c0", - "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.11.3" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/examples/notebooks/linear_region_selector.ipynb b/examples/notebooks/linear_region_selector.ipynb deleted file mode 100644 index 74b304a35..000000000 --- a/examples/notebooks/linear_region_selector.ipynb +++ /dev/null @@ -1,182 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "1db50ec4-8754-4421-9f5e-6ba8ca6b81e3", - "metadata": {}, - "source": [ - "# `LinearRegionSelector` with single lines" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b7bbfeb4-1ad0-47db-9a82-3d3f642a1f63", - "metadata": {}, - "outputs": [], - "source": [ - "import fastplotlib as fpl\n", - "import numpy as np\n", - "# from ipywidgets import IntRangeSlider, FloatRangeSlider, VBox\n", - "\n", - "fig = fpl.Figure((2, 2))\n", - "\n", - "# preallocated size for zoomed data\n", - "zoomed_prealloc = 1_000\n", - "\n", - "# data to plot\n", - "xs = np.linspace(0, 10* np.pi, 1_000)\n", - "sine = np.sin(xs)\n", - "sine += 100\n", - "\n", - "# make sine along x axis\n", - "sine_graphic_x = fig[0, 0].add_line(np.column_stack([xs, sine]), offset=(10, 0, 0))\n", - "\n", - "# just something that looks different for line along y-axis\n", - "sine_y = sine\n", - "sine_y[sine_y > 0] = 0\n", - "\n", - "# sine along y axis\n", - "sine_graphic_y = fig[0, 1].add_line(np.column_stack([sine_y, xs]))\n", - "\n", - "# offset the position of the graphic to demonstrate `get_selected_data()` later\n", - "sine_graphic_y.position_x = 50\n", - "sine_graphic_y.position_y = 50\n", - "\n", - "# add linear selectors\n", - "ls_x = sine_graphic_x.add_linear_region_selector() # default axis is \"x\"\n", - "ls_y = sine_graphic_y.add_linear_region_selector(axis=\"y\")\n", - "\n", - "# preallocate array for storing zoomed in data\n", - "zoomed_init = np.column_stack([np.arange(zoomed_prealloc), np.zeros(zoomed_prealloc)])\n", - "\n", - "# make line graphics for displaying zoomed data\n", - "zoomed_x = fig[1, 0].add_line(zoomed_init)\n", - "zoomed_y = fig[1, 1].add_line(zoomed_init)\n", - "\n", - "\n", - "def interpolate(subdata: np.ndarray, axis: int):\n", - " \"\"\"1D interpolation to display within the preallocated data array\"\"\"\n", - " x = np.arange(0, zoomed_prealloc)\n", - " xp = np.linspace(0, zoomed_prealloc, subdata.shape[0])\n", - " \n", - " # interpolate to preallocated size\n", - " return np.interp(x, xp, fp=subdata[:, axis]) # use the y-values\n", - "\n", - "@ls_x.add_event_handler(\"selection\")\n", - "def set_zoom_x(ev):\n", - " \"\"\"sets zoomed x selector data\"\"\"\n", - " # get the selected data\n", - " selected_data = ev.get_selected_data()\n", - " if selected_data.size == 0:\n", - " # no data selected\n", - " zoomed_x.data[:, 1] = 0\n", - "\n", - " # set the y-values\n", - " zoomed_x.data[:, 1] = interpolate(selected_data, axis=1)\n", - " fig[1, 0].auto_scale()\n", - "\n", - "\n", - "def set_zoom_y(ev):\n", - " \"\"\"sets zoomed x selector data\"\"\"\n", - " # get the selected data\n", - " selected_data = ev.get_selected_data()\n", - " if selected_data.size == 0:\n", - " # no data selected\n", - " zoomed_y.data[:, 0] = 0\n", - "\n", - " # set the x-values\n", - " zoomed_y.data[:, 0] = -interpolate(selected_data, axis=1)\n", - " fig[1, 1].auto_scale()\n", - "\n", - "\n", - "fig.show(maintain_aspect=False)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a9583d2e-ec52-405c-a875-f3fec5e3aa16", - "metadata": {}, - "outputs": [], - "source": [ - "# data to plot\n", - "xs = np.linspace(0, 100, 1_000)\n", - "sine = np.sin(xs) * 20\n", - "cosine = np.cos(xs) * 20\n", - "\n", - "fig_stack = fpl.Figure((5, 1))\n", - "\n", - "# sines and cosines\n", - "sines = [sine] * 2\n", - "cosines = [cosine] * 2\n", - "\n", - "# make line stack\n", - "line_stack = fig_stack[0, 0].add_line_stack(sines + cosines, separation=50)\n", - "\n", - "# make selector\n", - "selector = line_stack.add_linear_region_selector()\n", - "\n", - "# populate subplots with preallocated graphics\n", - "for i, subplot in enumerate(fig_stack):\n", - " if i == 0:\n", - " # skip the first one\n", - " continue\n", - " # make line graphics for displaying zoomed data\n", - " subplot.add_line(zoomed_init, name=\"zoomed\")\n", - "\n", - "\n", - "@selector.add_event_handler(\"selection\")\n", - "def update_zoomed_subplots(ev):\n", - " \"\"\"update the zoomed subplots\"\"\"\n", - " zoomed_data = ev.get_selected_data()\n", - " \n", - " for i in range(len(zoomed_data)):\n", - " # interpolate y-vals\n", - " data = interpolate(zoomed_data[i], axis=1)\n", - " fig_stack[i + 1, 0][\"zoomed\"].data[:, 1] = data\n", - " fig_stack[i + 1, 0].auto_scale()\n", - "\n", - "\n", - "fig_stack.show()" - ] - }, - { - "cell_type": "markdown", - "id": "0fa051b5-d6bc-4e4e-8f12-44f638a00c88", - "metadata": {}, - "source": [ - "# Large line stack with selector" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "cbcd6309-fb47-4941-9fd1-2b091feb3ae7", - "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.11.3" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/examples/notebooks/linear_selector.ipynb b/examples/notebooks/linear_selector.ipynb deleted file mode 100644 index bac8df182..000000000 --- a/examples/notebooks/linear_selector.ipynb +++ /dev/null @@ -1,150 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "a06e1fd9-47df-42a3-a76c-19e23d7b89fd", - "metadata": {}, - "source": [ - "## `LinearSelector`, draggable selector that can also be linked to an ipywidget slider" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "eb95ba19-14b5-4bf4-93d9-05182fa500cb", - "metadata": {}, - "outputs": [], - "source": [ - "import fastplotlib as fpl\n", - "\n", - "import numpy as np\n", - "from ipywidgets import VBox, IntSlider, FloatSlider\n", - "\n", - "fig = fpl.Figure()\n", - "\n", - "# data to plot\n", - "xs = np.linspace(0, 100, 1000)\n", - "sine = np.sin(xs) * 20\n", - "\n", - "# make sine along x axis\n", - "sine_graphic = fig[0, 0].add_line(np.column_stack([xs, sine]).astype(np.float32))\n", - "\n", - "# make some selectors\n", - "selector = sine_graphic.add_linear_selector()\n", - "selector2 = sine_graphic.add_linear_selector(20)\n", - "selector3 = sine_graphic.add_linear_selector(40)\n", - "\n", - "# one of the selectors will change the line colors when it moves\n", - "@selector.add_event_handler(\"selection\")\n", - "def set_color_at_index(ev):\n", - " # changes the color at the index where the slider is\n", - " ix = ev.get_selected_index()\n", - " g = ev.graphic.parent\n", - " g.colors[ix] = \"green\"\n", - "\n", - "# fastplotlib LineSelector can make an ipywidget slider and return it :D \n", - "ipywidget_slider = selector.make_ipywidget_slider()\n", - "ipywidget_slider.description = \"slider1\"\n", - "\n", - "# or you can make your own ipywidget sliders and connect them to the linear selector\n", - "ipywidget_slider2 = IntSlider(min=0, max=100, description=\"slider2\")\n", - "ipywidget_slider3 = FloatSlider(min=0, max=100, description=\"slider3\")\n", - "\n", - "selector2.add_ipywidget_handler(ipywidget_slider2, step=5)\n", - "selector3.add_ipywidget_handler(ipywidget_slider3, step=0.1)\n", - "\n", - "fig[0, 0].auto_scale()\n", - "VBox([fig.show(), ipywidget_slider, ipywidget_slider2, ipywidget_slider3])" - ] - }, - { - "cell_type": "markdown", - "id": "d83caca6-e9b6-45df-b93c-0dfe0498d20e", - "metadata": {}, - "source": [ - "Double click the first selctor, and then use `Shift` + Right/Left Arrow Key to move it!" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7ab9f141-f92f-4c4c-808b-97dafd64ca25", - "metadata": {}, - "outputs": [], - "source": [ - "# this controls the step-size of arrow key movements\n", - "selector.step = 0.1" - ] - }, - { - "cell_type": "markdown", - "id": "3b0f448f-bbe4-4b87-98e3-093f561c216c", - "metadata": { - "jp-MarkdownHeadingCollapsed": true - }, - "source": [ - "### Drag linear selectors with the mouse, hold \"Shift\" to synchronize movement of all the selectors" - ] - }, - { - "cell_type": "markdown", - "id": "c6f041b7-8779-46f1-8454-13cec66f53fd", - "metadata": {}, - "source": [ - "## Also works for line collections" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e36da217-f82a-4dfa-9556-1f4a2c7c4f1c", - "metadata": {}, - "outputs": [], - "source": [ - "sines = [sine] * 10\n", - "\n", - "fig = fpl.Figure()\n", - "\n", - "sine_stack = fig[0, 0].add_line_stack(sines)\n", - "\n", - "colors = \"y\", \"blue\", \"red\", \"green\"\n", - "\n", - "selectors = list()\n", - "for i, c in enumerate(colors):\n", - " sel = sine_stack.add_linear_selector(i * 100, color=c, name=str(i))\n", - " selectors.append(sel)\n", - "\n", - "fig.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "71ae4fca-f644-4d4f-8f32-f9d069bbc2f1", - "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.11.3" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/examples/notebooks/lineplot.ipynb b/examples/notebooks/lineplot.ipynb deleted file mode 100644 index e700c866a..000000000 --- a/examples/notebooks/lineplot.ipynb +++ /dev/null @@ -1,124 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "386184a4-bd03-4467-a539-2696c3c5a573", - "metadata": { - "tags": [] - }, - "source": [ - "# A more complex example combing different graphics, subplots and multiple perspectives" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9c974494-712e-4981-bae2-a3ee176a6b20", - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import fastplotlib as fpl" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c3d8f967-f60f-4f0b-b6ba-21b1251b4856", - "metadata": {}, - "outputs": [], - "source": [ - "# create data in the shape of a spiral\n", - "phi = np.linspace(0, 30, 200)\n", - "\n", - "xs = phi * np.cos(phi)\n", - "ys = phi * np.sin(phi)\n", - "zs = phi\n", - "\n", - "# make data 3d, with shape [, 3]\n", - "spiral = np.dstack([xs, ys, zs])[0]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "78cffe56-1147-4255-82c1-53cec6bc986a", - "metadata": {}, - "outputs": [], - "source": [ - "# figure with 2 rows and 2 columns\n", - "shape = (2, 2)\n", - "\n", - "# pan-zoom controllers for each subplot\n", - "# subplots are synced if they have the\n", - "# same controller ID\n", - "# in this example the first view has its own controller\n", - "# and the last 3 views are synced\n", - "controller_ids = [\n", - " [0, 1], # id each controller with an integer\n", - " [1, 1]\n", - "]\n", - "\n", - "# create the figure\n", - "fig = fpl.Figure(\n", - " shape=shape,\n", - " cameras='3d', # 3D view for all subplots within the figure\n", - " controller_ids=controller_ids\n", - ")\n", - "\n", - "for i, subplot in enumerate(fig):\n", - " # create and add the LineGraphic\n", - " line_graphic = subplot.add_line(data=spiral, thickness=3, cmap='jet')\n", - " marker = subplot.add_scatter(data=spiral[0], sizes=10, name=\"marker\")\n", - " \n", - "marker_index = 0\n", - "\n", - "# a function to move the ball along the spiral\n", - "def move_marker():\n", - " global marker_index\n", - " \n", - " marker_index += 1\n", - " \n", - " if marker_index == spiral.shape[0]:\n", - " marker_index = 0\n", - " \n", - " for subplot in fig:\n", - " subplot[\"marker\"].data = spiral[marker_index]\n", - " \n", - "# add `move_marker` to the animations\n", - "fig.add_animations(move_marker)\n", - "\n", - "fig.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e388eb93-7a9b-4ae4-91fc-cf32947f63a9", - "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.11.2" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/examples/notebooks/lines_cmap.ipynb b/examples/notebooks/lines_cmap.ipynb deleted file mode 100644 index 3ceb25326..000000000 --- a/examples/notebooks/lines_cmap.ipynb +++ /dev/null @@ -1,306 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "b169210c-b148-4701-91d2-87f8be2c90da", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "import numpy as np\n", - "import fastplotlib as fpl" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5d2ef4aa-0e4c-4694-ae2e-05da1153a413", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# this is only for testing, you do not need this to use fastplotlib\n", - "from nb_test_utils import plot_test, notebook_finished" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a6615d45-6a6e-4a1e-a998-18f7cc52f6b9", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# linspace, create 100 evenly spaced x values from -10 to 10\n", - "xs = np.linspace(-10, 10, 100)\n", - "# sine wave\n", - "ys = np.sin(xs)\n", - "sine = np.column_stack([xs, ys])\n", - "\n", - "# cosine wave\n", - "ys = np.cos(xs)\n", - "cosine = np.column_stack([xs, ys])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "52a91e8a-25b7-4121-a06f-623d7412b558", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig = fpl.Figure()\n", - "\n", - "fig[0, 0].add_line(sine, thickness=10)\n", - "\n", - "fig.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "727282c3-aadf-420f-a88e-9dd4d4e91263", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "plot_test(\"lines-cmap-white\", fig)" - ] - }, - { - "cell_type": "markdown", - "id": "889b1858-ed64-4d6b-96ad-3883fbe4d38e", - "metadata": {}, - "source": [ - "# Fancy indexing of line colormaps" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "13185547-07bc-4771-ac6d-83314622bf30", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig[0, 0].graphics[0].cmap = \"jet\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3c9b0bc8-b176-425c-8036-63dc55ab7466", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# for testing, ignore\n", - "plot_test(\"lines-cmap-jet\", fig)" - ] - }, - { - "cell_type": "markdown", - "id": "13c1c034-2b3b-4568-b979-7c0bbea698ae", - "metadata": {}, - "source": [ - "map colors from sine data values by setting the cmap transform" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ee9ec4d7-d9a2-417c-92bd-b01a9a019801", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig[0, 0].graphics[0].cmap.transform = sine[:, 1]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6b19d2d4-90e7-40ed-afb9-13abe5474ace", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# for testing, ignore\n", - "plot_test(\"lines-cmap-jet-values\", fig)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ebf9f494-782d-4529-9ef6-a2a4032f097d", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# set transform from cosine\n", - "fig[0, 0].graphics[0].cmap.transform = cosine[:, 1]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0a6c4739-fa61-4532-865e-21107eab76f9", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# for testing, ignore\n", - "plot_test(\"lines-cmap-jet-values-cosine\", fig)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5ddc95cf-b3be-4212-b525-1c628dc1e091", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# change cmap\n", - "fig[0, 0].graphics[0].cmap = \"viridis\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "45acfd2f-09f5-418c-bca5-3e574348b7d5", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# for testing, ignore\n", - "plot_test(\"lines-cmap-viridis\", fig)" - ] - }, - { - "cell_type": "markdown", - "id": "1f52bfdc-8151-4bab-973c-1bac36011802", - "metadata": {}, - "source": [ - "use cmap transform to map for a qualitative transform" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7712d313-16cd-49e5-89ca-91364412f194", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "cmap_transform = [0] * 25 + [1] * 5 + [2] * 50 + [3] * 20" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c8c13c03-56f0-48c3-b44e-65545a3bc3bc", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig[0, 0].graphics[0].cmap.transform = cmap_transform" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7548407f-05ed-4c47-93cc-131c61f8e242", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# for testing, ignore\n", - "plot_test(\"lines-cmap-viridis-values\", fig)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f64d036d-8a9e-4799-b77f-e78afa441fec", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig[0, 0].graphics[0].cmap = \"tab10\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c290c642-ba5f-4a46-9a17-c434cb39de26", - "metadata": {}, - "outputs": [], - "source": [ - "# for testing, ignore\n", - "plot_test(\"lines-cmap-tab-10\", fig)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c4b9e735-72e9-4f0e-aa3e-43db57e65c99", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# for testing, ignore\n", - "notebook_finished()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f6735cc0-910c-4854-ac50-8ee553a6475e", - "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.11.3" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/examples/notebooks/scatter.ipynb b/examples/notebooks/scatter.ipynb deleted file mode 100644 index 0389b462b..000000000 --- a/examples/notebooks/scatter.ipynb +++ /dev/null @@ -1,190 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "eb204b20-160a-48ef-8ac6-54d263e497e4", - "metadata": { - "tags": [] - }, - "source": [ - "# Scatter plots in a `GridPlot` layout with a mix of 2d an 3d cameras" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9b3041ad-d94e-4b2a-af4d-63bcd19bf6c2", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "import numpy as np\n", - "import fastplotlib as fpl" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "51f1d76a-f815-460f-a884-097fe3ea81ac", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# create a random distribution of 10,000 xyz coordinates\n", - "n_points = 10_000\n", - "\n", - "# if you have a good GPU go for 1.2 million points :D \n", - "# this is multiplied by 3\n", - "#n_points = 400_000\n", - "dims = (n_points, 3)\n", - "\n", - "offset = 15\n", - "\n", - "normal = np.random.normal(size=dims, scale=5)\n", - "cloud = np.vstack(\n", - " [\n", - " normal - offset,\n", - " normal,\n", - " normal + offset,\n", - " ]\n", - ")\n", - "\n", - "colors = [\"yellow\"] * n_points + [\"cyan\"] * n_points + [\"magenta\"] * n_points" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "922990b6-24e9-4fa0-977b-6577f9752d84", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# figure with 2 rows and 2 columns\n", - "shape = (2, 2)\n", - "\n", - "# define the camera\n", - "# a mix of 2d and 3d\n", - "cameras = [\n", - " ['2d', '3d'], \n", - " ['3d', '2d']\n", - "]\n", - "\n", - "# pan-zoom controllers for each subplot\n", - "# subplots are synced if they have the\n", - "# same controller ID\n", - "# you can only sync controllers that use the same camera type\n", - "# i.e. you cannot sync between 2d and 3d subplots\n", - "controller_ids = [\n", - " [0, 1],\n", - " [1, 0]\n", - "]\n", - "\n", - "# create the figure\n", - "fig = fpl.Figure(\n", - " shape=shape,\n", - " cameras=cameras,\n", - " controller_ids=controller_ids\n", - ")\n", - "\n", - "for subplot in fig:\n", - " subplot.add_scatter(data=cloud, colors=colors, alpha=0.7, sizes=5)\n", - "\n", - "\n", - "fig.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7b912961-f72e-46ef-889f-c03234831059", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig[0, 1].graphics[0].colors[n_points:int(n_points * 1.5)] = \"r\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c6085806-c001-4632-ab79-420b4692693a", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig[0, 1].graphics[0].colors[:n_points:10] = \"blue\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6f416825-df31-4e5d-b66b-07f23b48e7db", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig[0, 1].graphics[0].colors[n_points:] = \"green\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c0fd611e-73e5-49e6-a25c-9d5b64afa5f4", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig[0, 1].graphics[0].colors[n_points:, -1] = 0" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "cd390542-3a44-4973-8172-89e5583433bc", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig[0, 1].graphics[0].data[:n_points] = fig[0, 1].graphics[0].data[n_points * 2:]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a18e7a17-c2af-4674-a499-bf5f3b27c8ca", - "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.11.2" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/examples/notebooks/scatter_sizes_animation.ipynb b/examples/notebooks/scatter_sizes_animation.ipynb deleted file mode 100644 index 0cd301fb1..000000000 --- a/examples/notebooks/scatter_sizes_animation.ipynb +++ /dev/null @@ -1,61 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from time import time\n", - "\n", - "import numpy as np\n", - "import fastplotlib as fpl\n", - "\n", - "fig = fpl.Figure()\n", - "\n", - "points = np.array([[-1,0,1],[-1,0,1]], dtype=np.float32).swapaxes(0,1)\n", - "size_delta_scales = np.array([10, 40, 100], dtype=np.float32)\n", - "min_sizes = 6\n", - "\n", - "\n", - "def update_positions(subplot):\n", - " g = subplot.graphics[0]\n", - " g.data[:, :-1] += np.sin(((time() / 4))*np.pi)\n", - "\n", - "\n", - "def update_sizes(subplot):\n", - " sin_sample = np.abs(np.sin((time() / 1)*np.pi))\n", - " size_delta = sin_sample * size_delta_scales\n", - " subplot.graphics[0].sizes = size_delta\n", - "\n", - "\n", - "scatter = fig[0, 0].add_scatter(points, colors=[\"red\", \"green\", \"blue\"], sizes=12)\n", - "fig[0, 0].add_animations(update_positions, update_sizes)\n", - "\n", - "fig[0, 0].camera.width = 12\n", - "fig.show(autoscale=False)" - ] - } - ], - "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.11.3" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/examples/notebooks/scatter_sizes_grid.ipynb b/examples/notebooks/scatter_sizes_grid.ipynb deleted file mode 100644 index 21985f189..000000000 --- a/examples/notebooks/scatter_sizes_grid.ipynb +++ /dev/null @@ -1,82 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "\"\"\"\n", - "Scatter Plot\n", - "============\n", - "Example showing point size change for scatter plot.\n", - "\"\"\"\n", - "\n", - "# test_example = true\n", - "import numpy as np\n", - "import fastplotlib as fpl\n", - "\n", - "# figure with 2 rows and 3 columns\n", - "shape = (2, 1)\n", - "\n", - "# you can give string names for each subplot\n", - "names = [\n", - " [\"scalar_size\"],\n", - " [\"array_size\"]\n", - "]\n", - "\n", - "# Create the figure\n", - "fig = fpl.Figure(\n", - " shape=shape,\n", - " names=names,\n", - " size=(1000, 1000)\n", - ")\n", - "\n", - "# get y_values using sin function\n", - "angles = np.arange(0, 20*np.pi+0.001, np.pi / 20)\n", - "y_values = 30*np.sin(angles) # 1 thousand points\n", - "x_values = np.array([x for x in range(len(y_values))], dtype=np.float32)\n", - "\n", - "data = np.column_stack([x_values, y_values])\n", - "\n", - "fig[\"scalar_size\"].add_scatter(data=data, sizes=5, colors=\"blue\") # add a set of scalar sizes\n", - "\n", - "non_scalar_sizes = np.abs((y_values / np.pi)) # ensure minimum size of 5\n", - "fig[\"array_size\"].add_scatter(data=data, sizes=non_scalar_sizes, colors=\"red\")\n", - "\n", - "for subplot in fig:\n", - " subplot.auto_scale(maintain_aspect=True)\n", - "\n", - "fig.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "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.11.3" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/examples/notebooks/screenshots/nb-lines-cmap-jet-values-cosine.png b/examples/notebooks/screenshots/nb-lines-cmap-jet-values-cosine.png deleted file mode 100644 index 08b6e8ac1..000000000 --- a/examples/notebooks/screenshots/nb-lines-cmap-jet-values-cosine.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a499ecad892c779aa857e9074a5e157b02bc914007b28aa4958b2b231b5961a4 -size 18585 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-jet-values.png b/examples/notebooks/screenshots/nb-lines-cmap-jet-values.png deleted file mode 100644 index 83b5c21d9..000000000 --- a/examples/notebooks/screenshots/nb-lines-cmap-jet-values.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9506e9838bd5bb1f79d41c8dfaa92c127d12852758bfcecfa37202d02b0ba325 -size 19914 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-jet.png b/examples/notebooks/screenshots/nb-lines-cmap-jet.png deleted file mode 100644 index 34a6f8b6f..000000000 --- a/examples/notebooks/screenshots/nb-lines-cmap-jet.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8e6a74dad6621df938517558adf89e19e975b39386d1a46d48991f0cffe725dd -size 18549 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-tab-10.png b/examples/notebooks/screenshots/nb-lines-cmap-tab-10.png deleted file mode 100644 index ca41e764d..000000000 --- a/examples/notebooks/screenshots/nb-lines-cmap-tab-10.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5725b52f18e1be1f7bd358b489374c0f733cf9c3f1f127a4fbfbd1daec30a57f -size 17061 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-viridis-values.png b/examples/notebooks/screenshots/nb-lines-cmap-viridis-values.png deleted file mode 100644 index dc7ec0c13..000000000 --- a/examples/notebooks/screenshots/nb-lines-cmap-viridis-values.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f1f08fea3f0f74ab7632725f544c03303334cfcf1e01b188d01113a8bcc84dd7 -size 17353 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-viridis.png b/examples/notebooks/screenshots/nb-lines-cmap-viridis.png deleted file mode 100644 index 912cae1e4..000000000 --- a/examples/notebooks/screenshots/nb-lines-cmap-viridis.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a5d14cb03071ca2ad4c0c2145d68ab3555d30565f8f6d2be6fa7fc649213d748 -size 20499 diff --git a/examples/notebooks/screenshots/nb-lines-cmap-white.png b/examples/notebooks/screenshots/nb-lines-cmap-white.png deleted file mode 100644 index 2edc6903b..000000000 --- a/examples/notebooks/screenshots/nb-lines-cmap-white.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3c7a2c8ccd70787e67800fb0d9625d7816fb6b004c0128b3b254fec7c7fd7c71 -size 16058 diff --git a/examples/notebooks/subplots.ipynb b/examples/notebooks/subplots.ipynb deleted file mode 100644 index c9774029f..000000000 --- a/examples/notebooks/subplots.ipynb +++ /dev/null @@ -1,218 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "3cfc2d9f-6a09-42f4-a47c-3ba51f1a1801", - "metadata": {}, - "source": [ - "### More in-depth on subplots with a Figure" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a635b3b3-33fa-48f0-b1cc-bf83b1e883ab", - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import fastplotlib as fpl" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8de931e2-bdb3-44a3-9538-e0b3965779af", - "metadata": {}, - "outputs": [], - "source": [ - "# grid with 2 rows and 3 columns\n", - "shape = (2, 3)\n", - "\n", - "# pan-zoom controllers for each subplot\n", - "# subplots are synced if they have the\n", - "# same controller ID\n", - "controller_ids = [\n", - " [0, -3, 1], # id each controller with an integer\n", - " [2, 2, -3]\n", - "]\n", - "\n", - "# another way to set controller_ids\n", - "controller_ids = [\n", - " [\"subplot0\", \"subplot4\"],\n", - " [\"subplot1\", \"subplot2\", \"subplot5\"],\n", - "]\n", - "\n", - "\n", - "# you can give string names for each subplot within the figure\n", - "names = [\n", - " [\"subplot0\", \"subplot1\", \"subplot2\"],\n", - " [\"subplot3\", \"subplot4\", \"subplot5\"]\n", - "]\n", - "\n", - "# Create the figure\n", - "fig = fpl.Figure(\n", - " shape=shape,\n", - " controller_ids=controller_ids,\n", - " names=names,\n", - ")\n", - "\n", - "\n", - "# Make a random image graphic for each subplot\n", - "for subplot in fig:\n", - " data = np.random.rand(512, 512)\n", - " # create and add an ImageGraphic\n", - " subplot.add_image(data=data, name=\"rand-image\")\n", - " \n", - "\n", - "# Define a function to update the image graphics \n", - "# with new randomly generated data\n", - "def set_random_frame(gp):\n", - " for subplot in gp:\n", - " new_data = np.random.rand(512, 512)\n", - " subplot[\"rand-image\"].data = new_data\n", - "\n", - "# add the animation\n", - "fig.add_animations(set_random_frame)\n", - "fig.show()" - ] - }, - { - "cell_type": "markdown", - "id": "2867bcd6-7691-4073-91d9-9c33e8fdb896", - "metadata": {}, - "source": [ - "### Accessing subplots" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2a6f7eb5-776e-42a6-b6c2-c26009a26795", - "metadata": { - "is_executing": true - }, - "outputs": [], - "source": [ - "# by name\n", - "fig[\"subplot0\"]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "45e83bca-5a44-48ce-874f-9ae9ca444233", - "metadata": {}, - "outputs": [], - "source": [ - "# by index\n", - "fig[0, 0]" - ] - }, - { - "cell_type": "markdown", - "id": "3272b8b3-3063-47a4-94c8-15ceeeaecc69", - "metadata": {}, - "source": [ - "## getting graphics within subplots!\n", - "this can be used to get graphics if they are named" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c8cf9bfd-e0cc-4173-b64e-a9f2c87bb2c6", - "metadata": {}, - "outputs": [], - "source": [ - "# can access graphic directly via name\n", - "fig[\"subplot0\"][\"rand-image\"]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1cfd1d45-8a60-4fc1-b873-46caa966fe6f", - "metadata": {}, - "outputs": [], - "source": [ - "fig[\"subplot0\"][\"rand-image\"].vmin = 0.6\n", - "fig[\"subplot0\"][\"rand-image\"].vmax = 0.8" - ] - }, - { - "cell_type": "markdown", - "id": "39c8a5acbad7980b", - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false - } - }, - "source": [ - "If they are not named use .graphics" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d27af25002237db5", - "metadata": { - "collapsed": false, - "is_executing": true, - "jupyter": { - "outputs_hidden": false - } - }, - "outputs": [], - "source": [ - "fig[\"subplot0\"].graphics" - ] - }, - { - "cell_type": "markdown", - "id": "2299a8ae23e39c37", - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false - } - }, - "source": [ - "### positional indexing also works" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2fafe992-4783-40f2-b044-26a2835dd50a", - "metadata": {}, - "outputs": [], - "source": [ - "fig[1, 0][\"rand-image\"].vim = 0.1\n", - "fig[1, 0][\"rand-image\"].vmax = 0.3" - ] - } - ], - "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.11.3" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/examples/notebooks/subplots_simple.ipynb b/examples/notebooks/subplots_simple.ipynb deleted file mode 100644 index 9ff4e4284..000000000 --- a/examples/notebooks/subplots_simple.ipynb +++ /dev/null @@ -1,259 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "0e42f03b-9cdf-484f-b158-78b07fdf524d", - "metadata": {}, - "source": [ - "## This notebook shows how you can use more of the `fastplotlib` API to create `Graphic` objects and add them to subplots" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5171a06e-1bdc-4908-9726-3c1fd45dbb9d", - "metadata": { - "ExecuteTime": { - "end_time": "2023-11-26T04:01:19.120171Z", - "start_time": "2023-11-26T04:01:18.618087Z" - }, - "tags": [] - }, - "outputs": [], - "source": [ - "import numpy as np\n", - "import fastplotlib as fpl" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "86a2488f-ae1c-4b98-a7c0-18eae8013af1", - "metadata": { - "ExecuteTime": { - "end_time": "2023-11-26T04:01:19.467264Z", - "start_time": "2023-11-26T04:01:19.121813Z" - }, - "tags": [] - }, - "outputs": [], - "source": [ - "# Figure of shape 2 x 3 with all controllers synced\n", - "fig = fpl.Figure(shape=(2, 3), controller_ids=\"sync\")\n", - "\n", - "# Make a random image graphic for each subplot\n", - "for subplot in fig:\n", - " # create image data\n", - " data = np.random.rand(512, 512)\n", - " # add an image to the subplot\n", - " subplot.add_image(data, name=\"rand-img\")\n", - "\n", - "# Define a function to update the image graphics with new data\n", - "# add_animations will pass the figure to the animation function\n", - "def update_data(f):\n", - " for sp in f:\n", - " new_data = np.random.rand(512, 512)\n", - " # index the image graphic by name and set the data\n", - " sp[\"rand-img\"].data = new_data\n", - " \n", - "# add the animation function\n", - "fig.add_animations(update_data)\n", - "\n", - "# show the figure\n", - "fig.show()" - ] - }, - { - "cell_type": "markdown", - "id": "e7801781-c3e9-490f-ab12-1cd2f480d3e9", - "metadata": {}, - "source": [ - "## Accessing subplots within `Figure`" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "52163bc7-2c77-4699-b7b0-e455a0ed7584", - "metadata": {}, - "outputs": [], - "source": [ - "fig" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "17c6bc4a-5340-49f1-8597-f54528cfe915", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# positional indexing\n", - "# row 0 and col 0\n", - "fig[0, 0]" - ] - }, - { - "cell_type": "markdown", - "id": "276dfede-e9bc-4488-b9e6-3ca5cf91e4dc", - "metadata": {}, - "source": [ - "### You can get the graphics within a subplot" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "34130f12-9ef6-43b0-b929-931de8b7da25", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig[0, 1].graphics" - ] - }, - { - "cell_type": "markdown", - "id": "bf33f3e7-ab16-46b1-9126-f0a1ecc07541", - "metadata": {}, - "source": [ - "### and change their properties" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ef8a29a6-b19c-4e6b-a2ba-fb4823c01451", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig[0, 1].graphics[0].vmax = 0.5" - ] - }, - { - "cell_type": "markdown", - "id": "00506fa1-2dc0-4435-96a0-e50667d3174f", - "metadata": {}, - "source": [ - "### more indexing" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d6c2fa4b-c634-4dcf-8b61-f1986f7c4918", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# you can give subplots human-readable string names\n", - "fig[0, 2].name = \"top-right-plot\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2f6b549c-3165-496d-98aa-45b96c3de674", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig[\"top-right-plot\"]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "be436e04-33a6-4597-8e6a-17e1e5225419", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# view its position\n", - "fig[\"top-right-plot\"].position" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6699cda6-af86-4258-87f5-1832f989a564", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# these are really the same\n", - "fig[\"top-right-plot\"] is fig[0, 2]" - ] - }, - { - "cell_type": "markdown", - "id": "aac2f6bf-a641-4c86-a3d2-2cb7906ba914", - "metadata": {}, - "source": [ - "Indexing with subplot name and graphic name" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "545b627b-d794-459a-a75a-3fde44f0ea95", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig[\"top-right-plot\"][\"rand-img\"].vmin = 0.5" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "36432d5b-b76c-4a2a-a32c-097faf5ab269", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "fig.close()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b507b723-1371-44e7-aa6d-6aeb3196b27d", - "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.11.3" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/examples/qt/README.rst b/examples/qt/README.rst new file mode 100644 index 000000000..5ff5471d5 --- /dev/null +++ b/examples/qt/README.rst @@ -0,0 +1,2 @@ +Qt Examples +=========== diff --git a/examples/qt/embed.py b/examples/qt/embed.py index a3b156021..535c08e5f 100644 --- a/examples/qt/embed.py +++ b/examples/qt/embed.py @@ -1,6 +1,15 @@ """ -Use a simple Plot to display a video frame that can be updated using a QSlider +Embed within a Qt Window +======================== + +When using the Qt canvas, `Figure.show()` just returns a QWidget that behaves like any other Qt widget. So you can +embed it and do other things that you can do with ordinary QWidgets. This example use a simple Plot to display a video +frame that can be updated using a QSlider. """ + +# test_example = false +# sphinx_gallery_pygfx_docs = 'code' + from PyQt6 import QtWidgets, QtCore import fastplotlib as fpl import imageio.v3 as iio @@ -52,3 +61,6 @@ def update_frame(ix): # execute Qt app fpl.run() + +# You can also use Qt interactively/in a non-blocking manner in notebooks and ipython +# by using %gui qt and NOT calling `fpl.run()`, see the user guide for more details diff --git a/examples/qt/imagewidget.py b/examples/qt/imagewidget.py index f82d082a0..d18ae9b15 100644 --- a/examples/qt/imagewidget.py +++ b/examples/qt/imagewidget.py @@ -1,6 +1,13 @@ """ -Use ImageWidget to display one or multiple image sequences +ImageWidget as QtWidget +======================= + +This example opens multiple windows to show multiple ImageWidgets. """ + +# test_example = false +# sphinx_gallery_pygfx_docs = 'code' + import numpy as np from PyQt6 import QtWidgets import fastplotlib as fpl @@ -11,22 +18,25 @@ # fastplotlib and wgpu will auto-detect if Qt is imported and then use the Qt canvas iw = fpl.ImageWidget(images) -iw.show() -iw.widget.resize(800, 800) +widget = iw.show() +widget.resize(800, 800) # another image widget with multiple images -images_list = [np.random.rand(100, 512, 512) for i in range(9)] +images_list = [np.random.rand(100, 512, 512) for i in range(4)] iw_mult = fpl.ImageWidget( images_list, cmap="viridis" ) -iw_mult.show() -iw_mult.widget.resize(800, 800) +widget_multi = iw_mult.show() +widget_multi.resize(800, 800) # image widget with rgb data rgb_video = iio.imread("imageio:cockatoo.mp4") -iw_rgb = fpl.ImageWidget(rgb_video, rgb=[True]) +iw_rgb = fpl.ImageWidget(rgb_video, rgb=[True], figure_kwargs={"size": (800, 500)}) iw_rgb.show() fpl.run() + +# You can also use Qt interactively/in a non-blocking manner in notebooks and ipython +# by using %gui qt and NOT calling `fpl.run()`, see the user guide for more details diff --git a/examples/qt/minimal.py b/examples/qt/minimal.py index 0d9009ba7..01b5de352 100644 --- a/examples/qt/minimal.py +++ b/examples/qt/minimal.py @@ -1,6 +1,15 @@ """ -Minimal PyQt example that displays an image. Press "r" key to autoscale +Minimal Qt +========== + +Minimal PyQt example that displays an image. + +`Figure.show()` returns a QWidget that you can use in a Qt app just like any other QWidget! """ + +# test_example = false +# sphinx_gallery_pygfx_docs = 'code' + # import Qt or PySide from PyQt6 import QtWidgets import fastplotlib as fpl @@ -9,12 +18,12 @@ img = iio.imread("imageio:astronaut.png") # fastplotlib and wgpu will auto-detect if Qt is imported and then use the Qt canvas and Qt output context -fig = fpl.Figure() +figure = fpl.Figure() -fig[0, 0].add_image(img) +figure[0, 0].add_image(img) # must call fig.show() to start rendering loop and show the QWidget containing the fastplotlib figure -qwidget = fig.show() +qwidget = figure.show() # set QWidget initial size from image width and height qwidget.resize(*img.shape[:2]) @@ -22,3 +31,6 @@ # execute Qt app # if this is part of a larger Qt QApplication, you can also call app.exec() where app is the QApplication instance fpl.run() + +# You can also use Qt interactively/in a non-blocking manner in notebooks and ipython +# by using %gui qt and NOT calling `fpl.run()`, see the user guide for more details diff --git a/examples/desktop/scatter/README.rst b/examples/scatter/README.rst similarity index 100% rename from examples/desktop/scatter/README.rst rename to examples/scatter/README.rst diff --git a/examples/desktop/scatter/scatter.py b/examples/scatter/scatter.py similarity index 100% rename from examples/desktop/scatter/scatter.py rename to examples/scatter/scatter.py diff --git a/examples/desktop/scatter/scatter_cmap.py b/examples/scatter/scatter_cmap.py similarity index 100% rename from examples/desktop/scatter/scatter_cmap.py rename to examples/scatter/scatter_cmap.py diff --git a/examples/desktop/scatter/scatter_cmap_iris.py b/examples/scatter/scatter_cmap_iris.py similarity index 100% rename from examples/desktop/scatter/scatter_cmap_iris.py rename to examples/scatter/scatter_cmap_iris.py diff --git a/examples/desktop/scatter/scatter_colorslice.py b/examples/scatter/scatter_colorslice.py similarity index 100% rename from examples/desktop/scatter/scatter_colorslice.py rename to examples/scatter/scatter_colorslice.py diff --git a/examples/desktop/scatter/scatter_colorslice_iris.py b/examples/scatter/scatter_colorslice_iris.py similarity index 100% rename from examples/desktop/scatter/scatter_colorslice_iris.py rename to examples/scatter/scatter_colorslice_iris.py diff --git a/examples/desktop/scatter/scatter_dataslice.py b/examples/scatter/scatter_dataslice.py similarity index 100% rename from examples/desktop/scatter/scatter_dataslice.py rename to examples/scatter/scatter_dataslice.py diff --git a/examples/desktop/scatter/scatter_dataslice_iris.py b/examples/scatter/scatter_dataslice_iris.py similarity index 100% rename from examples/desktop/scatter/scatter_dataslice_iris.py rename to examples/scatter/scatter_dataslice_iris.py diff --git a/examples/desktop/scatter/scatter_iris.py b/examples/scatter/scatter_iris.py similarity index 100% rename from examples/desktop/scatter/scatter_iris.py rename to examples/scatter/scatter_iris.py diff --git a/examples/desktop/scatter/scatter_size.py b/examples/scatter/scatter_size.py similarity index 100% rename from examples/desktop/scatter/scatter_size.py rename to examples/scatter/scatter_size.py diff --git a/examples/desktop/screenshots/gridplot.png b/examples/screenshots/gridplot.png similarity index 100% rename from examples/desktop/screenshots/gridplot.png rename to examples/screenshots/gridplot.png diff --git a/examples/desktop/screenshots/gridplot_non_square.png b/examples/screenshots/gridplot_non_square.png similarity index 100% rename from examples/desktop/screenshots/gridplot_non_square.png rename to examples/screenshots/gridplot_non_square.png diff --git a/examples/desktop/screenshots/heatmap.png b/examples/screenshots/heatmap.png similarity index 100% rename from examples/desktop/screenshots/heatmap.png rename to examples/screenshots/heatmap.png diff --git a/examples/desktop/screenshots/image_cmap.png b/examples/screenshots/image_cmap.png similarity index 100% rename from examples/desktop/screenshots/image_cmap.png rename to examples/screenshots/image_cmap.png diff --git a/examples/desktop/screenshots/image_rgb.png b/examples/screenshots/image_rgb.png similarity index 100% rename from examples/desktop/screenshots/image_rgb.png rename to examples/screenshots/image_rgb.png diff --git a/examples/desktop/screenshots/image_rgbvminvmax.png b/examples/screenshots/image_rgbvminvmax.png similarity index 100% rename from examples/desktop/screenshots/image_rgbvminvmax.png rename to examples/screenshots/image_rgbvminvmax.png diff --git a/examples/desktop/screenshots/image_simple.png b/examples/screenshots/image_simple.png similarity index 100% rename from examples/desktop/screenshots/image_simple.png rename to examples/screenshots/image_simple.png diff --git a/examples/desktop/screenshots/image_small.png b/examples/screenshots/image_small.png similarity index 100% rename from examples/desktop/screenshots/image_small.png rename to examples/screenshots/image_small.png diff --git a/examples/desktop/screenshots/image_vminvmax.png b/examples/screenshots/image_vminvmax.png similarity index 100% rename from examples/desktop/screenshots/image_vminvmax.png rename to examples/screenshots/image_vminvmax.png diff --git a/examples/desktop/screenshots/image_widget.png b/examples/screenshots/image_widget.png similarity index 100% rename from examples/desktop/screenshots/image_widget.png rename to examples/screenshots/image_widget.png diff --git a/examples/desktop/screenshots/image_widget_grid.png b/examples/screenshots/image_widget_grid.png similarity index 100% rename from examples/desktop/screenshots/image_widget_grid.png rename to examples/screenshots/image_widget_grid.png diff --git a/examples/desktop/screenshots/image_widget_imgui.png b/examples/screenshots/image_widget_imgui.png similarity index 100% rename from examples/desktop/screenshots/image_widget_imgui.png rename to examples/screenshots/image_widget_imgui.png diff --git a/examples/desktop/screenshots/image_widget_single_video.png b/examples/screenshots/image_widget_single_video.png similarity index 100% rename from examples/desktop/screenshots/image_widget_single_video.png rename to examples/screenshots/image_widget_single_video.png diff --git a/examples/desktop/screenshots/image_widget_videos.png b/examples/screenshots/image_widget_videos.png similarity index 100% rename from examples/desktop/screenshots/image_widget_videos.png rename to examples/screenshots/image_widget_videos.png diff --git a/examples/desktop/screenshots/imgui_basic.png b/examples/screenshots/imgui_basic.png similarity index 100% rename from examples/desktop/screenshots/imgui_basic.png rename to examples/screenshots/imgui_basic.png diff --git a/examples/desktop/screenshots/line.png b/examples/screenshots/line.png similarity index 100% rename from examples/desktop/screenshots/line.png rename to examples/screenshots/line.png diff --git a/examples/desktop/screenshots/line_cmap.png b/examples/screenshots/line_cmap.png similarity index 100% rename from examples/desktop/screenshots/line_cmap.png rename to examples/screenshots/line_cmap.png diff --git a/examples/screenshots/line_cmap_more.png b/examples/screenshots/line_cmap_more.png new file mode 100644 index 000000000..56e3fe8cc --- /dev/null +++ b/examples/screenshots/line_cmap_more.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:de08452e47799d9afcadfc583e63da1c02513cf73000bd5c2649236e61ed6b34 +size 126725 diff --git a/examples/desktop/screenshots/line_collection.png b/examples/screenshots/line_collection.png similarity index 100% rename from examples/desktop/screenshots/line_collection.png rename to examples/screenshots/line_collection.png diff --git a/examples/desktop/screenshots/line_collection_cmap_values.png b/examples/screenshots/line_collection_cmap_values.png similarity index 100% rename from examples/desktop/screenshots/line_collection_cmap_values.png rename to examples/screenshots/line_collection_cmap_values.png diff --git a/examples/desktop/screenshots/line_collection_cmap_values_qualitative.png b/examples/screenshots/line_collection_cmap_values_qualitative.png similarity index 100% rename from examples/desktop/screenshots/line_collection_cmap_values_qualitative.png rename to examples/screenshots/line_collection_cmap_values_qualitative.png diff --git a/examples/desktop/screenshots/line_collection_colors.png b/examples/screenshots/line_collection_colors.png similarity index 100% rename from examples/desktop/screenshots/line_collection_colors.png rename to examples/screenshots/line_collection_colors.png diff --git a/examples/desktop/screenshots/line_collection_slicing.png b/examples/screenshots/line_collection_slicing.png similarity index 100% rename from examples/desktop/screenshots/line_collection_slicing.png rename to examples/screenshots/line_collection_slicing.png diff --git a/examples/desktop/screenshots/line_colorslice.png b/examples/screenshots/line_colorslice.png similarity index 100% rename from examples/desktop/screenshots/line_colorslice.png rename to examples/screenshots/line_colorslice.png diff --git a/examples/desktop/screenshots/line_dataslice.png b/examples/screenshots/line_dataslice.png similarity index 100% rename from examples/desktop/screenshots/line_dataslice.png rename to examples/screenshots/line_dataslice.png diff --git a/examples/desktop/screenshots/line_stack.png b/examples/screenshots/line_stack.png similarity index 100% rename from examples/desktop/screenshots/line_stack.png rename to examples/screenshots/line_stack.png diff --git a/examples/screenshots/linear_region_selectors_match_offsets.png b/examples/screenshots/linear_region_selectors_match_offsets.png new file mode 100644 index 000000000..9d2371403 --- /dev/null +++ b/examples/screenshots/linear_region_selectors_match_offsets.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1f12310c09c4e84ea2c6f8245d1aa0ce9389a3d9637d7d4f9dc233bea173a0e3 +size 95366 diff --git a/examples/desktop/screenshots/linear_selector.png b/examples/screenshots/linear_selector.png similarity index 100% rename from examples/desktop/screenshots/linear_selector.png rename to examples/screenshots/linear_selector.png diff --git a/examples/desktop/screenshots/scatter_cmap_iris.png b/examples/screenshots/scatter_cmap_iris.png similarity index 100% rename from examples/desktop/screenshots/scatter_cmap_iris.png rename to examples/screenshots/scatter_cmap_iris.png diff --git a/examples/desktop/screenshots/scatter_colorslice_iris.png b/examples/screenshots/scatter_colorslice_iris.png similarity index 100% rename from examples/desktop/screenshots/scatter_colorslice_iris.png rename to examples/screenshots/scatter_colorslice_iris.png diff --git a/examples/desktop/screenshots/scatter_dataslice_iris.png b/examples/screenshots/scatter_dataslice_iris.png similarity index 100% rename from examples/desktop/screenshots/scatter_dataslice_iris.png rename to examples/screenshots/scatter_dataslice_iris.png diff --git a/examples/desktop/screenshots/scatter_iris.png b/examples/screenshots/scatter_iris.png similarity index 100% rename from examples/desktop/screenshots/scatter_iris.png rename to examples/screenshots/scatter_iris.png diff --git a/examples/desktop/screenshots/scatter_size.png b/examples/screenshots/scatter_size.png similarity index 100% rename from examples/desktop/screenshots/scatter_size.png rename to examples/screenshots/scatter_size.png diff --git a/examples/desktop/selectors/README.rst b/examples/selection_tools/README.rst similarity index 100% rename from examples/desktop/selectors/README.rst rename to examples/selection_tools/README.rst diff --git a/examples/selection_tools/linear_region_line_collection.py b/examples/selection_tools/linear_region_line_collection.py new file mode 100644 index 000000000..493062026 --- /dev/null +++ b/examples/selection_tools/linear_region_line_collection.py @@ -0,0 +1,81 @@ +""" +LinearRegionSelectors with LineCollection +========================================= + +Example showing how to use a `LinearRegionSelector` with a `LineCollection` +""" + +# test_example = false +# sphinx_gallery_pygfx_docs = 'screenshot' + + +import fastplotlib as fpl +import numpy as np + +# data to plot +xs = np.linspace(0, 10 * np.pi, 1_000) +sine = np.column_stack([xs, np.sin(xs)]) +cosine = np.column_stack([xs, np.cos(xs)]) + +figure = fpl.Figure((5, 1), size=(700, 1000)) + +# preallocated size for zoomed data +zoomed_prealloc = 1_000 + +# sines and cosines +data = [sine, cosine, sine, cosine] + +# make line stack +line_stack = figure[0, 0].add_line_stack(data, separation=2) + +# make selector +selector = line_stack.add_linear_region_selector() + +# preallocate array for storing zoomed in data +zoomed_init = np.column_stack([np.arange(zoomed_prealloc), np.zeros(zoomed_prealloc)]) + +# populate zoomed view subplots with graphics using preallocated buffer sizes +for i, subplot in enumerate(figure): + if i == 0: + # skip the first one + continue + # make line graphics for displaying zoomed data + subplot.add_line(zoomed_init, name="zoomed") + + +def interpolate(subdata: np.ndarray, axis: int): + """1D interpolation to display within the preallocated data array""" + x = np.arange(0, zoomed_prealloc) + xp = np.linspace(0, zoomed_prealloc, subdata.shape[0]) + + # interpolate to preallocated size + return np.interp(x, xp, fp=subdata[:, axis]) # use the y-values + + +@selector.add_event_handler("selection") +def update_zoomed_subplots(ev): + """update the zoomed subplots""" + zoomed_data = ev.get_selected_data() + + for i in range(len(zoomed_data)): + # interpolate y-vals + data = interpolate(zoomed_data[i], axis=1) + figure[i + 1, 0]["zoomed"].data[:, 1] = data + figure[i + 1, 0].auto_scale() + + +# set initial selection so zoomed plots update +selector.selection = (0, 4 * np.pi) + +# hide toolbars to reduce clutter +for subplot in figure: + subplot.toolbar = False + +figure.show(maintain_aspect=False) + + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.run() diff --git a/examples/selection_tools/linear_region_selector.py b/examples/selection_tools/linear_region_selector.py new file mode 100644 index 000000000..026da2522 --- /dev/null +++ b/examples/selection_tools/linear_region_selector.py @@ -0,0 +1,114 @@ +""" +LinearRegionSelectors +===================== + +Example showing how to use a `LinearRegionSelector` with lines. We demonstrate two use cases, a horizontal +LinearRegionSelector which selects along the x-axis and a vertical selector which moves along the y-axis. + +In general, a horizontal selector on the x-axis is useful if you are displaying data where y = f(x). +Conversely, a vertical selector that selectors along the y-axis is useful for +displaying data where x = f(y). (ex: vertical histograms) +""" + +# test_example = false +# sphinx_gallery_pygfx_docs = 'screenshot' + +import fastplotlib as fpl +import numpy as np + +# names for out subplots +names = [ + ["y = sine(x)", "x = sine(y), sine(y) > 0 = 0"], + ["zoomed sine(x)", "zoomed sine(y)"] +] + +# 2 rows, 2 columns +figure = fpl.Figure( + (2, 2), + size=(700, 560), + names=names, +) + +# preallocated size for zoomed data +zoomed_prealloc = 1_000 + +# data to plot +xs = np.linspace(0, 10 * np.pi, 1_000) +ys = np.sin(xs) # y = sine(x) + +# make sine along x axis +sine_graphic_x = figure[0, 0].add_line(np.column_stack([xs, ys])) + +# x = sine(y), sine(y) > 0 = 0 +sine_y = ys +sine_y[sine_y > 0] = 0 + +# sine along y axis +sine_graphic_y = figure[0, 1].add_line(np.column_stack([ys, xs])) + +# offset the position of the graphic to demonstrate `get_selected_data()` later +sine_graphic_y.position_x = 50 +sine_graphic_y.position_y = 50 + +# add linear selectors +selector_x = sine_graphic_x.add_linear_region_selector() # default axis is "x" +selector_y = sine_graphic_y.add_linear_region_selector(axis="y") + +# preallocate array for storing zoomed in data +zoomed_init = np.column_stack([np.arange(zoomed_prealloc), np.zeros(zoomed_prealloc)]) + +# make line graphics for displaying zoomed data +zoomed_x = figure[1, 0].add_line(zoomed_init) +zoomed_y = figure[1, 1].add_line(zoomed_init) + + +def interpolate(subdata: np.ndarray, axis: int): + """1D interpolation to display within the preallocated data array""" + x = np.arange(0, zoomed_prealloc) + xp = np.linspace(0, zoomed_prealloc, subdata.shape[0]) + + # interpolate to preallocated size + return np.interp(x, xp, fp=subdata[:, axis]) # use the y-values + + +@selector_x.add_event_handler("selection") +def set_zoom_x(ev): + """sets zoomed x selector data""" + # get the selected data + selected_data = ev.get_selected_data() + if selected_data.size == 0: + # no data selected + zoomed_x.data[:, 1] = 0 + + # interpolate the y-values since y = f(x) + zoomed_x.data[:, 1] = interpolate(selected_data, axis=1) + figure[1, 0].auto_scale() + + +def set_zoom_y(ev): + """sets zoomed x selector data""" + # get the selected data + selected_data = ev.get_selected_data() + if selected_data.size == 0: + # no data selected + zoomed_y.data[:, 1] = 0 + + # interpolate the x values since this x = f(y) + zoomed_y.data[:, 1] = -interpolate(selected_data, axis=0) + figure[1, 1].auto_scale() + + +# you can also add event handlers without a decorator +selector_y.add_event_handler(set_zoom_y, "selection") + +# set initial selection +selector_x.selection = selector_y.selection = (0, 4 * np.pi) + + +figure.show(maintain_aspect=False) + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.run() diff --git a/examples/selection_tools/linear_region_selectors_match_offsets.py b/examples/selection_tools/linear_region_selectors_match_offsets.py new file mode 100644 index 000000000..c5d58c9be --- /dev/null +++ b/examples/selection_tools/linear_region_selectors_match_offsets.py @@ -0,0 +1,109 @@ +""" +LinearRegionSelectors match offsets +=================================== + +Identical to linear region selector but with offsets for testing purposes +""" + +# test_example = true +# sphinx_gallery_pygfx_docs = 'hidden' + +import fastplotlib as fpl +import numpy as np + +# names for out subplots +names = [ + ["y = sine(x)", "x = sine(y), sine(y) > 0 = 0"], + ["zoomed sine(x)", "zoomed sine(y)"] +] + +# 2 rows, 2 columns +figure = fpl.Figure( + (2, 2), + size=(700, 560), + names=names, +) + +# preallocated size for zoomed data +zoomed_prealloc = 1_000 + +# data to plot +xs = np.linspace(0, 10 * np.pi, 1_000) +ys = np.sin(xs) # y = sine(x) + +# make sine along x axis +sine_graphic_x = figure[0, 0].add_line(np.column_stack([xs, ys]), offset=(10, 10, 0)) + +# x = sine(y), sine(y) > 0 = 0 +sine_y = ys +sine_y[sine_y > 0] = 0 + +# sine along y axis +sine_graphic_y = figure[0, 1].add_line(np.column_stack([ys, xs]), offset=(10, 10, 0)) + +# offset the position of the graphic to demonstrate `get_selected_data()` later +sine_graphic_y.position_x = 50 +sine_graphic_y.position_y = 50 + +# add linear selectors +selector_x = sine_graphic_x.add_linear_region_selector() # default axis is "x" +selector_y = sine_graphic_y.add_linear_region_selector(axis="y") + +# preallocate array for storing zoomed in data +zoomed_init = np.column_stack([np.arange(zoomed_prealloc), np.zeros(zoomed_prealloc)]) + +# make line graphics for displaying zoomed data +zoomed_x = figure[1, 0].add_line(zoomed_init) +zoomed_y = figure[1, 1].add_line(zoomed_init) + + +def interpolate(subdata: np.ndarray, axis: int): + """1D interpolation to display within the preallocated data array""" + x = np.arange(0, zoomed_prealloc) + xp = np.linspace(0, zoomed_prealloc, subdata.shape[0]) + + # interpolate to preallocated size + return np.interp(x, xp, fp=subdata[:, axis]) # use the y-values + + +@selector_x.add_event_handler("selection") +def set_zoom_x(ev): + """sets zoomed x selector data""" + # get the selected data + selected_data = ev.get_selected_data() + if selected_data.size == 0: + # no data selected + zoomed_x.data[:, 1] = 0 + + # interpolate the y-values since y = f(x) + zoomed_x.data[:, 1] = interpolate(selected_data, axis=1) + figure[1, 0].auto_scale() + + +def set_zoom_y(ev): + """sets zoomed x selector data""" + # get the selected data + selected_data = ev.get_selected_data() + if selected_data.size == 0: + # no data selected + zoomed_y.data[:, 1] = 0 + + # interpolate the x values since this x = f(y) + zoomed_y.data[:, 1] = -interpolate(selected_data, axis=0) + figure[1, 1].auto_scale() + + +# you can also add event handlers without a decorator +selector_y.add_event_handler(set_zoom_y, "selection") + +# set initial selection +selector_x.selection = selector_y.selection = (0, 4 * np.pi) + + +figure.show(maintain_aspect=False) + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.run() diff --git a/examples/desktop/selectors/linear_selector.py b/examples/selection_tools/linear_selector.py similarity index 100% rename from examples/desktop/selectors/linear_selector.py rename to examples/selection_tools/linear_selector.py diff --git a/examples/desktop/selectors/linear_selector_image.py b/examples/selection_tools/linear_selector_image.py similarity index 100% rename from examples/desktop/selectors/linear_selector_image.py rename to examples/selection_tools/linear_selector_image.py diff --git a/examples/desktop/selectors/rectangle_selector.py b/examples/selection_tools/rectangle_selector.py similarity index 100% rename from examples/desktop/selectors/rectangle_selector.py rename to examples/selection_tools/rectangle_selector.py diff --git a/examples/desktop/selectors/rectangle_selector_zoom.py b/examples/selection_tools/rectangle_selector_zoom.py similarity index 95% rename from examples/desktop/selectors/rectangle_selector_zoom.py rename to examples/selection_tools/rectangle_selector_zoom.py index b5932d820..45633c42b 100644 --- a/examples/desktop/selectors/rectangle_selector_zoom.py +++ b/examples/selection_tools/rectangle_selector_zoom.py @@ -1,6 +1,7 @@ """ -Rectangle Selectors -=================== +Rectangle Selectors Images +========================== + Example showing how to use a `RectangleSelector` with images """ diff --git a/examples/tests/test_examples.py b/examples/tests/test_examples.py index 0143c9bef..55a3fa84d 100644 --- a/examples/tests/test_examples.py +++ b/examples/tests/test_examples.py @@ -2,6 +2,7 @@ Test that examples run without error. """ +import sys import importlib import runpy import pytest @@ -57,20 +58,25 @@ def test_that_we_are_on_lavapipe(): assert is_lavapipe +def import_from_path(module_name, filename): + spec = importlib.util.spec_from_file_location(module_name, filename) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + + # With this approach the module is not added to sys.modules, which + # is great, because that way the gc can simply clean up when we lose + # the reference to the module + assert module.__name__ == module_name + assert module_name not in sys.modules + + return module + + @pytest.mark.parametrize("module", examples_to_test, ids=lambda x: x.stem) def test_example_screenshots(module, force_offscreen): """Make sure that every example marked outputs the expected.""" - # (relative) module name from project root - module_name = ( - module.relative_to(ROOT / "examples") - .with_suffix("") - .as_posix() - .replace("/", ".") - ) - print(pygfx.renderers.wgpu.get_shared().device.limits["max-texture-dimension2d"]) - # import the example module - example = importlib.import_module(module_name) + example = import_from_path(module.stem, module) # there doesn't seem to be a resize event for the manual offscreen canvas example.figure.imgui_renderer._backend.io.display_size = example.figure.canvas.get_logical_size() diff --git a/examples/tests/testutils.py b/examples/tests/testutils.py index 3db6901ef..f72a87123 100644 --- a/examples/tests/testutils.py +++ b/examples/tests/testutils.py @@ -11,7 +11,7 @@ ROOT = Path(__file__).parents[2] # repo root -examples_dir = ROOT / "examples" / "desktop" +examples_dir = ROOT / "examples" screenshots_dir = examples_dir / "screenshots" diffs_dir = examples_dir / "diffs" @@ -25,7 +25,7 @@ "line_collection/*.py", "gridplot/*.py", "misc/*.py", - "selectors/*.py", + "selection_tools/*.py", "guis/*.py", ] diff --git a/fastplotlib/graphics/selectors/_linear.py b/fastplotlib/graphics/selectors/_linear.py index eec4cc910..ae3648f5e 100644 --- a/fastplotlib/graphics/selectors/_linear.py +++ b/fastplotlib/graphics/selectors/_linear.py @@ -160,9 +160,9 @@ def __init__( self._handled_widgets = list() if axis == "x": - offset = (parent.offset[0], center, 0) + offset = (parent.offset[0], center + parent.offset[1], 0) elif axis == "y": - offset = (center, parent.offset[1], 0) + offset = (center + parent.offset[0], parent.offset[1], 0) # init base selector BaseSelector.__init__( diff --git a/fastplotlib/graphics/selectors/_linear_region.py b/fastplotlib/graphics/selectors/_linear_region.py index ecc67b885..f83385d76 100644 --- a/fastplotlib/graphics/selectors/_linear_region.py +++ b/fastplotlib/graphics/selectors/_linear_region.py @@ -211,9 +211,9 @@ def __init__( # TODO: if parent offset changes, we should set the selector offset too # TODO: add check if parent is `None`, will throw error otherwise if axis == "x": - offset = (parent.offset[0], center, 0) + offset = (parent.offset[0], center + parent.offset[1], 0) elif axis == "y": - offset = (center, parent.offset[1], 0) + offset = (center + parent.offset[1], parent.offset[1], 0) # set the initial bounds of the selector # compensate for any offset from the parent graphic diff --git a/setup.py b/setup.py index 46b68fae6..f4b5eb5db 100644 --- a/setup.py +++ b/setup.py @@ -14,7 +14,7 @@ "docs": [ "sphinx", "sphinx-gallery", - "furo", + "pydata-sphinx-theme", "glfw", "jupyter-rfb>=0.4.1", # required so ImageWidget docs show up "ipywidgets>=8.0.0,<9", From 8409523b90222fbe218e79d2e74af910e84ac424 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Tue, 8 Oct 2024 10:56:28 -0400 Subject: [PATCH 114/185] fix limits when rect selector is added from image (#641) * fix limits when rect selector is added from image * fix docstring --- fastplotlib/graphics/image.py | 3 ++- fastplotlib/graphics/selectors/_rectangle.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/fastplotlib/graphics/image.py b/fastplotlib/graphics/image.py index 299c682b3..62477d7ff 100644 --- a/fastplotlib/graphics/image.py +++ b/fastplotlib/graphics/image.py @@ -419,7 +419,8 @@ def add_rectangle_selector( selection = (0, int(diagonal / 4), 0, int(diagonal / 4)) # min/max limits are image shape - limits = (0, self._data.value.shape[0], 0, self._data.value.shape[1]) + # rows are ys, columns are xs + limits = (0, self._data.value.shape[1], 0, self._data.value.shape[0]) selector = RectangleSelector( selection=selection, diff --git a/fastplotlib/graphics/selectors/_rectangle.py b/fastplotlib/graphics/selectors/_rectangle.py index 02e5ec6b3..356b437d7 100644 --- a/fastplotlib/graphics/selectors/_rectangle.py +++ b/fastplotlib/graphics/selectors/_rectangle.py @@ -430,7 +430,7 @@ def get_selected_indices( ------- Union[np.ndarray, List[np.ndarray]] data indicies of the selection - | tuple of [row_indices, y_indices_array] if the graphic is an image + | tuple of [row_indices, col_indices] if the graphic is an image | list of indices along the x-dimension for each line if graphic is a line collection | array of indices along the x-dimension if graphic is a line """ From 964298997ecea1947cb59893f6ec68c744c31c7a Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Wed, 16 Oct 2024 14:36:21 -0400 Subject: [PATCH 115/185] update github workflows, update actions versions and libegl1-mesa is not longer in ubuntu 24.04 (#651) --- .github/workflows/ci.yml | 16 ++++++++-------- .github/workflows/docs-deploy.yml | 2 +- .github/workflows/pypi-publish.yml | 4 ++-- .github/workflows/screenshots.yml | 6 +++--- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ad199d28f..00310cf37 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,15 +33,15 @@ jobs: - name: Install git-lfs run: | sudo apt install --no-install-recommends -y git-lfs - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v3 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.pyversion }} - name: Install llvmpipe and lavapipe for offscreen canvas run: | sudo apt-get update -y -qq - sudo apt-get install --no-install-recommends -y ffmpeg libegl1-mesa libgl1-mesa-dri libxcb-xfixes0-dev mesa-vulkan-drivers git-lfs + sudo apt-get install --no-install-recommends -y ffmpeg libegl1-mesa-dev libgl1-mesa-dri libxcb-xfixes0-dev mesa-vulkan-drivers git-lfs - name: Install dev dependencies run: | python -m pip install --upgrade pip setuptools @@ -63,7 +63,7 @@ jobs: WGPU_FORCE_OFFSCREEN=1 pytest -v tests/ pytest -v examples FASTPLOTLIB_NB_TESTS=1 pytest --nbmake examples/notebooks/ - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 if: ${{ failure() }} with: name: screenshot-diffs @@ -90,15 +90,15 @@ jobs: - name: Install git-lfs run: | sudo apt install --no-install-recommends -y git-lfs - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v3 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.pyversion }} - name: Install llvmpipe and lavapipe for offscreen canvas run: | sudo apt-get update -y -qq - sudo apt-get install --no-install-recommends -y libegl1-mesa libgl1-mesa-dri libxcb-xfixes0-dev mesa-vulkan-drivers git-lfs + sudo apt-get install --no-install-recommends -y libegl1-mesa-dev libgl1-mesa-dri libxcb-xfixes0-dev mesa-vulkan-drivers git-lfs - name: Install dev dependencies run: | python -m pip install --upgrade pip setuptools @@ -119,7 +119,7 @@ jobs: run: | WGPU_FORCE_OFFSCREEN=1 pytest -v tests/ pytest -v examples - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 if: ${{ failure() }} with: name: screenshot-diffs diff --git a/.github/workflows/docs-deploy.yml b/.github/workflows/docs-deploy.yml index b13c60849..713040ae8 100644 --- a/.github/workflows/docs-deploy.yml +++ b/.github/workflows/docs-deploy.yml @@ -35,7 +35,7 @@ jobs: - name: Install llvmpipe and lavapipe for offscreen canvas run: | sudo apt-get update -y -qq - sudo apt-get install --no-install-recommends -y ffmpeg libegl1-mesa libgl1-mesa-dri libxcb-xfixes0-dev mesa-vulkan-drivers git-lfs + sudo apt-get install --no-install-recommends -y ffmpeg libegl1-mesa-dev libgl1-mesa-dri libxcb-xfixes0-dev mesa-vulkan-drivers git-lfs - name: Install dev dependencies run: | python -m pip install --upgrade pip setuptools diff --git a/.github/workflows/pypi-publish.yml b/.github/workflows/pypi-publish.yml index 9ebe52b87..3fc70d7e1 100644 --- a/.github/workflows/pypi-publish.yml +++ b/.github/workflows/pypi-publish.yml @@ -24,13 +24,13 @@ jobs: - name: Install git-lfs run: | sudo apt install --no-install-recommends -y git-lfs - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: fetch git lfs files run: | git lfs fetch --all git lfs pull - name: Set up Python - uses: actions/setup-python@v3 + uses: actions/setup-python@v5 with: python-version: '3.10' - name: Install dependencies diff --git a/.github/workflows/screenshots.yml b/.github/workflows/screenshots.yml index 25a3cf517..eeb992d46 100644 --- a/.github/workflows/screenshots.yml +++ b/.github/workflows/screenshots.yml @@ -20,15 +20,15 @@ jobs: - name: Install git-lfs run: | sudo apt install --no-install-recommends -y git-lfs - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python 3.11 - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: '3.11' - name: Install llvmpipe and lavapipe for offscreen canvas run: | sudo apt-get update -y -qq - sudo apt-get install --no-install-recommends -y ffmpeg libegl1-mesa libgl1-mesa-dri libxcb-xfixes0-dev mesa-vulkan-drivers + sudo apt-get install --no-install-recommends -y ffmpeg libegl1-mesa-dev libgl1-mesa-dri libxcb-xfixes0-dev mesa-vulkan-drivers - name: Install dev dependencies run: | python -m pip install --upgrade pip setuptools From b5dff56d10e20cfa0f4d625a8571b5654d4db512 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Wed, 16 Oct 2024 23:05:36 -0400 Subject: [PATCH 116/185] cleanup readme and gpu guide (#653) --- README.md | 57 ++++++++-------------------------- docs/source/user_guide/gpu.rst | 34 ++++++++++++++++++-- 2 files changed, 44 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index 23aec8ac9..9ae948cbf 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![CI](https://github.com/fastplotlib/fastplotlib/actions/workflows/ci.yml/badge.svg)](https://github.com/fastplotlib/fastplotlib/actions/workflows/ci.yml) [![PyPI version](https://badge.fury.io/py/fastplotlib.svg)](https://badge.fury.io/py/fastplotlib) -[![Documentation Status](https://readthedocs.org/projects/fastplotlib/badge/?version=latest)](https://fastplotlib.readthedocs.io/en/latest/?badge=latest) +[![Deploy docs](https://github.com/fastplotlib/fastplotlib/actions/workflows/docs-deploy.yml/badge.svg)](https://fastplotlib.org/ver/dev/) [![DOI](https://zenodo.org/badge/485481453.svg)](https://zenodo.org/doi/10.5281/zenodo.13365890) [**Installation**](https://github.com/fastplotlib/fastplotlib#installation) | @@ -15,11 +15,11 @@ [**Examples**](https://github.com/kushalkolar/fastplotlib#examples) | [**Contributing**](https://github.com/kushalkolar/fastplotlib#heart-contributing) -Next-gen plotting library built using the [`pygfx`](https://github.com/pygfx/pygfx) rendering engine that can utilize [Vulkan](https://en.wikipedia.org/wiki/Vulkan), [DX12](https://en.wikipedia.org/wiki/DirectX#DirectX_12), or [Metal](https://developer.apple.com/metal/) via WGPU, so it is very fast! `fastplotlib` is an expressive plotting library that enables rapid prototyping for large scale explorative scientific visualization. +Next-gen plotting library built using the [`pygfx`](https://github.com/pygfx/pygfx) rendering engine that can utilize [Vulkan](https://en.wikipedia.org/wiki/Vulkan), [DX12](https://en.wikipedia.org/wiki/DirectX#DirectX_12), or [Metal](https://developer.apple.com/metal/) via WGPU, so it is very fast! `fastplotlib` is an expressive plotting library that enables rapid prototyping for large scale exploratory scientific visualization. > **Note** > -> `fastplotlib` is currently in the **late alpha stage**, but you're welcome to try it out or contribute! See our [Roadmap](https://github.com/kushalkolar/fastplotlib/issues/55). See this for a discussion on API stability: https://github.com/fastplotlib/fastplotlib/issues/121 +> `fastplotlib` is currently in the **late alpha stage**, but you're welcome to use it or contribute! See our [Roadmap](https://github.com/kushalkolar/fastplotlib/issues/55). See this for a discussion on API stability: https://github.com/fastplotlib/fastplotlib/issues/121 # Supported frameworks @@ -31,18 +31,14 @@ Next-gen plotting library built using the [`pygfx`](https://github.com/pygfx/pyg :heavy_check_mark: `wxPython` **Notes:**\ -:heavy_check_mark: Non-blocking interactive Qt/PySide output is supported in ipython and notebooks, see http://fastplotlib.org/ver/dev/user_guide/guide.html#using-fastplotlib-in-an-interactive-shell \ +:heavy_check_mark: Non-blocking interactive Qt/PySide output is supported in ipython and notebooks, see the [interactive shells section in the user guide](http://fastplotlib.org/ver/dev/user_guide/guide.html#using-fastplotlib-in-an-interactive-shell) \ :grey_exclamation: We do not officially support `jupyter notebook` through `jupyter_rfb`, this may change with notebook v7\ -:grey_exclamation: We only officially support jupyterlab for use in notebook. This means we do not support vscode notebooks etc. Jupyterlab is the most reliable way to use `fastplotlib` in notebooks.\ +:grey_exclamation: We only support jupyterlab for use in notebooks. This means that we do not support VSCode notebooks or any other python notebook platform. Jupyterlab is the most reliable way to use widget-based libraries in notebooks.\ :disappointed: [`jupyter_rfb`](https://github.com/vispy/jupyter_rfb) does not work in collab, see https://github.com/vispy/jupyter_rfb/pull/77 -We recommend sticking to jupyter-lab for notebooks. From our experience the usage on other platforms, such as vscode -notebooks, is not optimal. - - # Documentation -http://www.fastplotlib.org/ +http://www.fastplotlib.org/ver/dev Questions, issues, ideas? You are welcome to post an [issue](https://github.com/fastplotlib/fastplotlib/issues) or post on the [discussion forum](https://github.com/fastplotlib/fastplotlib/discussions)! :smiley: @@ -60,7 +56,7 @@ pip install -U fastplotlib # with imgui pip install -U "fastplotlib[imgui]" -# to use in jupyterlab, no imgui +# to use in jupyterlab without imgui pip install -U "fastplotlib[notebook]" ``` @@ -76,7 +72,7 @@ Once you have ``libjpeg-turbo``: pip install simplejpeg ``` -> **Note**[guide.rst](docs%2Fsource%2Fuser_guide%2Fguide.rst) +> **Note** > > `fastplotlib` and `pygfx` are fast evolving projects, the version available through pip might be outdated, you will need to follow the "For developers" instructions below if you want the latest features. You can find the release history here: https://github.com/fastplotlib/fastplotlib/releases @@ -95,7 +91,7 @@ pip install -e ".[notebook,docs,tests]" pip install git+https://github.com/pygfx/pygfx.git@main ``` -Se [Contributing](https://github.com/fastplotlib/fastplotlib?tab=readme-ov-file#heart-contributing) for more details on development +See [Contributing](https://github.com/fastplotlib/fastplotlib?tab=readme-ov-file#heart-contributing) for more details on development # Examples @@ -109,40 +105,13 @@ User guide: http://fastplotlib.org/ver/dev/user_guide/guide.html The `quickstart.ipynb` tutorial notebook is a great way to get familiar with the API: https://github.com/fastplotlib/fastplotlib/tree/main/examples/notebooks/quickstart.ipynb -## Graphics drivers - -You will need a relatively modern GPU, modern integrated graphics are usually fine for many use cases. Generally if your GPU is from 2017 or later it should be fine. - -For more detailed information, such as use on cloud computing infrastructure, see: https://wgpu-py.readthedocs.io/en/stable/start.html#platform-requirements - -Some more information on GPUs is here: http://fastplotlib.org/ver/dev/user_guide/gpu.html - -### Windows: -Vulkan drivers should be installed by default on Windows 11, but you will need to install your GPU manufacturer's driver package (Nvidia or AMD). If you have an integrated GPU within your CPU, you might still need to install a driver package too, check your CPU manufacturer's info. - -### Linux: -You will generally need a linux distro that is from ~2020 or newer (ex. Ubuntu 18.04 won't work), this is due to the `glibc` requirements of the `wgpu-native` binary. +## GPU drivers and requirements -Debian based distros: +Generally if your GPU is from 2017 or later it should be fine. Modern integrated graphics are usually fine for many use cases. The exact requirements will depend on how complex your visualization is and how many objects you need to render. -```bash -sudo apt install mesa-vulkan-drivers -# for better performance with the remote frame buffer install libjpeg-turbo -sudo apt install libjpeg-turbo -``` - -For other distros install the appropriate vulkan driver package, and optionally the corresponding `libjpeg-turbo` package for better remote-frame-buffer performance in jupyter notebooks. - -#### CPU/software rendering (Lavapipe) - -If you do not have a GPU you can perform limited software rendering using lavapipe. This should get you everything you need for that on Debian or Ubuntu based distros: - -```bash -sudo apt install llvm-dev libturbojpeg* libgl1-mesa-dev libgl1-mesa-glx libglapi-mesa libglx-mesa0 mesa-common-dev mesa-vulkan-drivers -``` +More detailed information on GPUs and drivers is here: http://fastplotlib.org/ver/dev/user_guide/gpu.html -### Mac OSX: -WGPU uses Metal instead of Vulkan on Mac. You will need at least Mac OSX 10.13. The OS should come with Metal pre-installed, so you should be good to go! +For more detailed information, such as use on cloud computing infrastructure, see the WGPU docs: https://wgpu-py.readthedocs.io/en/stable/start.html#cloud-compute # :heart: Contributing diff --git a/docs/source/user_guide/gpu.rst b/docs/source/user_guide/gpu.rst index 00653133c..392eb672b 100644 --- a/docs/source/user_guide/gpu.rst +++ b/docs/source/user_guide/gpu.rst @@ -26,11 +26,39 @@ If you aren't able to solve it please post an issue on GitHub. :) Drivers ------- -See the README: https://github.com/fastplotlib/fastplotlib?tab=readme-ov-file#graphics-drivers - If you notice weird graphic artifacts, things not rendering, or other glitches try updating to the latest stable drivers. +More information is also available on the WGPU docs: https://wgpu-py.readthedocs.io/en/stable/start.html#platform-requirements + +Windows +^^^^^^^ + +Vulkan drivers should be installed by default on Windows 11, but you will need to install your GPU manufacturer's driver package (Nvidia or AMD). If you have an integrated GPU within your CPU, you might still need to install a driver package too, check your CPU manufacturer's info. + +Linux +^^^^^ + +You will generally need a linux distro that is from ~2020 or newer (ex. Ubuntu 18.04 won't work), this is due to the `glibc` requirements of the `wgpu-native` binary. + +Install the drivers directly from your GPU manufacturer's website, after that you may still need to install mesa vulkan drivers: + +Debian based distros:: + + sudo apt install mesa-vulkan-drivers + +For other distros install the corresponding vulkan driver package. + +Cloud compute +~~~~~~~~~~~~~ + +See the WGPU docs: https://wgpu-py.readthedocs.io/en/stable/start.html#cloud-compute + +Mac OSX +^^^^^^^ + +WGPU uses Metal instead of Vulkan on Mac. You will need at least Mac OSX 10.13. The OS should come with Metal pre-installed, so you should be good to go! + GPU Info -------- @@ -279,7 +307,7 @@ to ``fpl.select_adapter()``:: Note that using this function reduces the portability of your code, because it's highly specific for your current machine/environment. -The order of the adapters returned by ``wgpu.gpu.enumerate_adapters()`` is +The order of the adapters returned by ``fpl.enumerate_adapters()`` is such that Vulkan adapters go first, then Metal, then D3D12, then OpenGL. Within each category, the order as provided by the particular backend is maintained. Note that the same device may be present via multiple backends From a691be23fc8e18d3b55da3334b6f27f0983c55a4 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Thu, 17 Oct 2024 10:12:24 -0400 Subject: [PATCH 117/185] add example of exploration of a covariance matrix (#652) * add cov matrix example * new file: examples/machine_learning/README.rst * update conf.py with machine_learning examples section --- docs/source/conf.py | 5 +- examples/machine_learning/README.rst | 2 + examples/machine_learning/covariance.py | 94 +++++++++++++++++++++++++ 3 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 examples/machine_learning/README.rst create mode 100644 examples/machine_learning/covariance.py diff --git a/docs/source/conf.py b/docs/source/conf.py index 36d965b83..f65713270 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -56,15 +56,16 @@ "subsection_order": ExplicitOrder( [ "../../examples/image", + "../../examples/heatmap", "../../examples/image_widget", "../../examples/gridplot", "../../examples/line", "../../examples/line_collection", "../../examples/scatter", - "../../examples/heatmap", - "../../examples/misc", "../../examples/selection_tools", + "../../examples/machine_learning", "../../examples/guis", + "../../examples/misc", "../../examples/qt", ] ), diff --git a/examples/machine_learning/README.rst b/examples/machine_learning/README.rst new file mode 100644 index 000000000..f6830887e --- /dev/null +++ b/examples/machine_learning/README.rst @@ -0,0 +1,2 @@ +Machine Learning Examples +========================= diff --git a/examples/machine_learning/covariance.py b/examples/machine_learning/covariance.py new file mode 100644 index 000000000..111a7bd22 --- /dev/null +++ b/examples/machine_learning/covariance.py @@ -0,0 +1,94 @@ +""" +Explore Covariance Matrix +========================= + +Example showing how you can explore a covariance matrix with a selector tool. +""" + +# test_example = false +# sphinx_gallery_pygfx_docs = 'animate 10s' + + +import fastplotlib as fpl +from sklearn import datasets +from sklearn.preprocessing import StandardScaler + +# load faces dataset +faces = datasets.fetch_olivetti_faces() +data = faces["data"] + +# sort the data so it's easier to understand the covariance matrix +targets = faces["target"] +sort_indices = targets.argsort() +targets_sorted = targets[sort_indices] + +X = data[sort_indices] + +# scale the data w.r.t. mean and standard deviation +X = StandardScaler().fit_transform(X) + +# compute covariance matrix +X = X.T +cov = X @ X.T / X.shape[1] + +# reshaped image for each sample wil be 64 x 64 pixels +img = cov[0].reshape(64, 64) + +# figure kwargs for image widget +# controller_ids = [[0, 1]] so we get independent controllers for each supblot +# the covariance matrix is 4096 x 4096 and the reshaped image ix 64 x 64 +figure_kwargs = {"size": (700, 400), "controller_ids": [[0, 1]]} + +# create image widget +iw = fpl.ImageWidget( + data=[cov, img], # display the covariance matrix and reshaped image of a row + cmap="bwr", # diverging colormap + names=["covariance", "row image"], + figure_kwargs=figure_kwargs, +) + +# graphic that corresponds to image widget data array 0 +# 0 is the covariance matrix, 1 is the reshaped image of a row from the covariance matrix + +# add a linear selector to select y axis values so we can select rows of the cov matrix +selector_cov = iw.managed_graphics[0].add_linear_selector(axis="y") + +# if you are exploring other types of matrices which are not-symmetric +# you can also add a column selector by setting axis="x" + +# set vmin vmax +for g in iw.managed_graphics: + g.vmin, g.vmax = -1, 1 + + +# event handler when the covariance matrix row changes +@selector_cov.add_event_handler("selection") +def update_img(ev): + # get the row index + ix = ev.get_selected_index() + + # get the image the corresponds to this row + img = cov[ix].reshape(64, 64) + + # change the reshaped image graphic data + iw.managed_graphics[1].data = img + + +figure = iw.figure # not required, just for the docs gallery to pick it up + + +# move the selector programmatically, this is mainly for the docs gallery +# for real use you can interact with the selector with your mouse +def animate(): + selector_cov.selection += 1 + + +iw.figure.add_animations(animate) + +iw.show() + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.run() From eb4b3f8fc42b2676abceacc844c43830e46ddafd Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Fri, 18 Oct 2024 08:24:44 -0400 Subject: [PATCH 118/185] remove ipywidget code from selectors and cleanup docstrings (#644) * remove ipywidget code from linear selector and cleanup docstrings * remove ipywidget code from LinearRegionSelector, cleanup docstrings * remove a method * update API docs --- .../api/selectors/LinearRegionSelector.rst | 2 - docs/source/api/selectors/LinearSelector.rst | 2 - fastplotlib/graphics/selectors/_linear.py | 195 +++--------------- .../graphics/selectors/_linear_region.py | 180 +++------------- 4 files changed, 51 insertions(+), 328 deletions(-) diff --git a/docs/source/api/selectors/LinearRegionSelector.rst b/docs/source/api/selectors/LinearRegionSelector.rst index bb406b7e2..9637dd8e1 100644 --- a/docs/source/api/selectors/LinearRegionSelector.rst +++ b/docs/source/api/selectors/LinearRegionSelector.rst @@ -43,12 +43,10 @@ Methods LinearRegionSelector.add_axes LinearRegionSelector.add_event_handler - LinearRegionSelector.add_ipywidget_handler LinearRegionSelector.clear_event_handlers LinearRegionSelector.get_selected_data LinearRegionSelector.get_selected_index LinearRegionSelector.get_selected_indices - LinearRegionSelector.make_ipywidget_slider LinearRegionSelector.remove_event_handler LinearRegionSelector.rotate LinearRegionSelector.share_property diff --git a/docs/source/api/selectors/LinearSelector.rst b/docs/source/api/selectors/LinearSelector.rst index d434ef82f..c514f982c 100644 --- a/docs/source/api/selectors/LinearSelector.rst +++ b/docs/source/api/selectors/LinearSelector.rst @@ -43,12 +43,10 @@ Methods LinearSelector.add_axes LinearSelector.add_event_handler - LinearSelector.add_ipywidget_handler LinearSelector.clear_event_handlers LinearSelector.get_selected_data LinearSelector.get_selected_index LinearSelector.get_selected_indices - LinearSelector.make_ipywidget_slider LinearSelector.remove_event_handler LinearSelector.rotate LinearSelector.share_property diff --git a/fastplotlib/graphics/selectors/_linear.py b/fastplotlib/graphics/selectors/_linear.py index ae3648f5e..dfe7aadab 100644 --- a/fastplotlib/graphics/selectors/_linear.py +++ b/fastplotlib/graphics/selectors/_linear.py @@ -1,22 +1,16 @@ -from typing import * import math from numbers import Real +from typing import Sequence import numpy as np import pygfx -from ...utils.gui import IS_JUPYTER from .._base import Graphic from .._collection_base import GraphicCollection from .._features._selection_features import LinearSelectionFeature from ._base_selector import BaseSelector -if IS_JUPYTER: - # If using the jupyter backend, user has jupyter_rfb, and thus also ipywidgets - import ipywidgets - - class LinearSelector(BaseSelector): @property def parent(self) -> Graphic: @@ -39,11 +33,11 @@ def selection(self, value: int): self._selection.set_value(self, value) @property - def limits(self) -> Tuple[float, float]: + def limits(self) -> tuple[float, float]: return self._limits @limits.setter - def limits(self, values: Tuple[float, float]): + def limits(self, values: tuple[float, float]): # check that `values` is an iterable of two real numbers # using `Real` here allows it to work with builtin `int` and `float` types, and numpy scaler types if len(values) != 2 or not all(map(lambda v: isinstance(v, Real), values)): @@ -62,46 +56,50 @@ def __init__( center: float, axis: str = "x", parent: Graphic = None, - color: str | tuple = "w", + color: str | Sequence[float] | np.ndarray = "w", thickness: float = 2.5, arrow_keys_modifier: str = "Shift", name: str = None, ): """ - Create a horizontal or vertical line slider that is synced to an ipywidget IntSlider + Create a horizontal or vertical line that can be used to select a value along an axis. Parameters ---------- selection: int - initial x or y selected position for the slider, in world space + initial x or y selected position for the selector, in data space limits: (int, int) - (min, max) limits along the x or y axis for the selector, in world space + (min, max) limits along the x or y-axis for the selector, in data space - axis: str, default "x" - "x" | "y", the axis which the slider can move along + size: float + size of the selector, usually the range of the data center: float - center offset of the selector on the orthogonal axis, by default the data mean + center offset of the selector on the orthogonal axis, usually the data mean + + axis: str, default "x" + "x" | "y", the axis along which the selector can move parent: Graphic - parent graphic for this LineSelector + parent graphic for this LinearSelector arrow_keys_modifier: str modifier key that must be pressed to initiate movement using arrow keys, must be one of: - "Control", "Shift", "Alt" or ``None``. Double click on the selector first to enable the + "Control", "Shift", "Alt" or ``None``. Double-click the selector first to enable the arrow key movements, or set the attribute ``arrow_key_events_enabled = True`` thickness: float, default 2.5 - thickness of the slider + thickness of the selector - color: Any, default "w" - selection to set the color of the slider + color: str | tuple | np.ndarray, default "w" + color of the selector name: str, optional - name of line slider + name of linear selector """ + if len(limits) != 2: raise ValueError("limits must be a tuple of 2 integers, i.e. (int, int)") @@ -155,10 +153,6 @@ def __init__( self._move_info: dict = None - self._block_ipywidget_call = False - - self._handled_widgets = list() - if axis == "x": offset = (parent.offset[0], center + parent.offset[1], 0) elif axis == "y": @@ -187,149 +181,22 @@ def __init__( else: self._selection.set_value(self, selection) - # update any ipywidgets - self.add_event_handler(self._update_ipywidgets, "selection") - - def _setup_ipywidget_slider(self, widget): - # setup an ipywidget slider with bidirectional callbacks to this LinearSelector - value = self.selection - - if isinstance(widget, ipywidgets.IntSlider): - value = int(value) - - widget.value = value - - # user changes widget -> linear selection changes - widget.observe(self._ipywidget_callback, "value") - - self._handled_widgets.append(widget) - - def _update_ipywidgets(self, ev): - # update the ipywidget sliders when LinearSelector value changes - self._block_ipywidget_call = True # prevent infinite recursion - - value = ev.info["value"] - # update all the handled slider widgets - for widget in self._handled_widgets: - if isinstance(widget, ipywidgets.IntSlider): - widget.value = int(value) - else: - widget.value = value - - self._block_ipywidget_call = False - - def _ipywidget_callback(self, change): - # update the LinearSelector when the ipywidget value changes - if self._block_ipywidget_call or self._moving: - return - - self.selection = change["new"] - - def _fpl_add_plot_area_hook(self, plot_area): - super()._fpl_add_plot_area_hook(plot_area=plot_area) - - # resize the slider widgets when the canvas is resized - self._plot_area.renderer.add_event_handler(self._set_slider_layout, "resize") - - def _set_slider_layout(self, *args): - w, h = self._plot_area.renderer.logical_size - - for widget in self._handled_widgets: - widget.layout = ipywidgets.Layout(width=f"{w}px") - - def make_ipywidget_slider(self, kind: str = "IntSlider", **kwargs): + def get_selected_index(self, graphic: Graphic = None) -> int | list[int]: """ - Makes and returns an ipywidget slider that is associated to this LinearSelector + Data index the selector is currently at w.r.t. the Graphic data. - Parameters - ---------- - kind: str - "IntSlider", "FloatSlider" or "FloatLogSlider" - - kwargs - passed to the ipywidget slider constructor - - Returns - ------- - ipywidgets.Intslider or ipywidgets.FloatSlider - - """ - - if not IS_JUPYTER: - raise ImportError( - "Must installed `ipywidgets` to use `make_ipywidget_slider()`" - ) - - if kind not in ["IntSlider", "FloatSlider", "FloatLogSlider"]: - raise TypeError( - f"`kind` must be one of: 'IntSlider', 'FloatSlider' or 'FloatLogSlider'\n" - f"You have passed: '{kind}'" - ) - - cls = getattr(ipywidgets, kind) - - value = self.selection - if "Int" in kind: - value = int(self.selection) - - slider = cls( - min=self.limits[0], - max=self.limits[1], - value=value, - **kwargs, - ) - self.add_ipywidget_handler(slider) - - return slider - - def add_ipywidget_handler(self, widget, step: Union[int, float] = None): - """ - Bidirectionally connect events with a ipywidget slider - - Parameters - ---------- - widget: ipywidgets.IntSlider, ipywidgets.FloatSlider, or ipywidgets.FloatLogSlider - ipywidget slider to connect to - - step: int or float, default ``None`` - step size, if ``None`` 100 steps are created - - """ - - if not isinstance( - widget, - (ipywidgets.IntSlider, ipywidgets.FloatSlider, ipywidgets.FloatLogSlider), - ): - raise TypeError( - f"`widget` must be one of: ipywidgets.IntSlider, ipywidgets.FloatSlider, or ipywidgets.FloatLogSlider\n" - f"You have passed a: <{type(widget)}" - ) - - if step is None: - step = (self.limits[1] - self.limits[0]) / 100 - - if isinstance(widget, ipywidgets.IntSlider): - step = int(step) - - widget.step = step - - self._setup_ipywidget_slider(widget) - - def get_selected_index(self, graphic: Graphic = None) -> Union[int, List[int]]: - """ - Data index the slider is currently at w.r.t. the Graphic data. With LineGraphic data, the geometry x or y - position is not always the data position, for example if plotting data using np.linspace. Use this to get - the data index of the slider. + With LineGraphic data, the geometry x or y position is not always the data position, for example if plotting + data using np.linspace. Use this to get the data index of the selector. Parameters ---------- graphic: Graphic, optional - Graphic to get the selected data index from. Default is the parent graphic associated to the slider. + Graphic to get the selected data index from. Default is the parent graphic associated to the selector. Returns ------- int or List[int] - data index the slider is currently at, list of ``int`` if a Collection + data index the selector is currently at, list of ``int`` if a Collection """ source = self._get_source(graphic) @@ -354,10 +221,10 @@ def _get_selected_index(self, graphic): "Line" in graphic.__class__.__name__ or "Scatter" in graphic.__class__.__name__ ): - # we want to find the index of the data closest to the slider position + # we want to find the index of the data closest to the selector position find_value = self.selection - # get closest data index to the world space position of the slider + # get closest data index to the world space position of the selector idx = np.searchsorted(data, find_value, side="left") if idx > 0 and ( @@ -398,9 +265,3 @@ def _move_graphic(self, delta: np.ndarray): self.selection = self.selection + delta[0] else: self.selection = self.selection + delta[1] - - def _fpl_prepare_del(self): - for widget in self._handled_widgets: - widget.unobserve(self._ipywidget_callback, "value") - - super()._fpl_prepare_del() diff --git a/fastplotlib/graphics/selectors/_linear_region.py b/fastplotlib/graphics/selectors/_linear_region.py index f83385d76..db7788d00 100644 --- a/fastplotlib/graphics/selectors/_linear_region.py +++ b/fastplotlib/graphics/selectors/_linear_region.py @@ -1,21 +1,15 @@ -from typing import * from numbers import Real +from typing import Sequence import numpy as np import pygfx -from ...utils.gui import IS_JUPYTER from .._base import Graphic from .._collection_base import GraphicCollection from .._features._selection_features import LinearRegionSelectionFeature from ._base_selector import BaseSelector -if IS_JUPYTER: - # If using the jupyter backend, user has jupyter_rfb, and thus also ipywidgets - import ipywidgets - - class LinearRegionSelector(BaseSelector): @property def parent(self) -> Graphic | None: @@ -23,35 +17,31 @@ def parent(self) -> Graphic | None: return self._parent @property - def selection(self) -> Sequence[float] | List[Sequence[float]]: + def selection(self) -> np.ndarray[float]: """ - (min, max) of data value along selector's axis + (min, max) of selector along selector's axis """ # TODO: This probably does not account for rotation since world.position # does not account for rotation, we can do this later return self._selection.value.copy() - # TODO: if no parent graphic is set, this just returns world positions + # TODO: if no parent graphic is set, this just returns values in world space # but should we change it? - # return self._selection.value @selection.setter def selection(self, selection: Sequence[float]): - # set (xmin, xmax), or (ymin, ymax) of the selector in data space + # set (min, max) of the selector in data space graphic = self._parent - if isinstance(graphic, GraphicCollection): - pass - self._selection.set_value(self, selection) @property - def limits(self) -> Tuple[float, float]: + def limits(self) -> tuple[float, float]: return self._limits @limits.setter - def limits(self, values: Tuple[float, float]): + def limits(self, values: tuple[float, float]): # check that `values` is an iterable of two real numbers # using `Real` here allows it to work with builtin `int` and `float` types, and numpy scaler types if len(values) != 2 or not all(map(lambda v: isinstance(v, Real), values)): @@ -70,8 +60,8 @@ def __init__( axis: str = "x", parent: Graphic = None, resizable: bool = True, - fill_color=(0, 0, 0.35), - edge_color=(0.8, 0.6, 0), + fill_color: str | Sequence[float] = (0, 0, 0.35), + edge_color: str | Sequence[float] = (0.8, 0.6, 0), edge_thickness: float = 8, arrow_keys_modifier: str = "Shift", name: str = None, @@ -82,7 +72,7 @@ def __init__( Assumes that the data under the selector is a function of the axis on which the selector moves along. Example: if the selector is along the x-axis, then there must be only one y-value for each - x-value, otherwise functions such as ``get_selected_data()`` do not make sense. + x-value, otherwise methods such as ``get_selected_data()`` do not make sense. Parameters ---------- @@ -93,19 +83,19 @@ def __init__( (min limit, max limit) within which the selector can move size: int - height or width of the selector + usually the data range, height or width of the selector center: float - center offset of the selector on the orthogonal axis, by default the data mean + usually the data mean, center offset of the selector on the orthogonal axis axis: str, default "x" - "x" | "y", axis the selected can move on + "x" | "y", axis along which the selector can move - parent: Graphic, default ``None`` - associate this selector with a parent Graphic from which to fetch data or indices + parent: ``Graphic`` instance, default ``None`` + associate this selector with a parent ``Graphic`` from which to fetch data or indices resizable: bool - if ``True``, the edges can be dragged to resize the width of the linear selection + if ``True``, the edges can be dragged to change the range of the selection fill_color: str, array, or tuple fill color for the selector, passed to pygfx.Color @@ -120,8 +110,8 @@ def __init__( modifier key that must be pressed to initiate movement using arrow keys, must be one of: "Control", "Shift", "Alt" or ``None`` - name: str - name for this selector graphic + name: str, optional + name of this selector graphic """ @@ -201,14 +191,14 @@ def __init__( ), ) - self.edges: Tuple[pygfx.Line, pygfx.Line] = (line0, line1) + self.edges: tuple[pygfx.Line, pygfx.Line] = (line0, line1) # add the edge lines for edge in self.edges: edge.world.z = -0.5 group.add(edge) - # TODO: if parent offset changes, we should set the selector offset too + # TODO: if parent offset changes, we should set the selector offset too, use offset evented property # TODO: add check if parent is `None`, will throw error otherwise if axis == "x": offset = (parent.offset[0], center + parent.offset[1], 0) @@ -222,8 +212,6 @@ def __init__( selection, axis=axis, limits=self._limits ) - self._handled_widgets = list() - self._block_ipywidget_call = False self._pygfx_event = None BaseSelector.__init__( @@ -244,7 +232,7 @@ def __init__( def get_selected_data( self, graphic: Graphic = None - ) -> Union[np.ndarray, List[np.ndarray]]: + ) -> np.ndarray | list[np.ndarray]: """ Get the ``Graphic`` data bounded by the current selection. Returns a view of the data array. @@ -277,7 +265,7 @@ def get_selected_data( # this will return a list of views of the arrays, therefore no copy operations occur # it's fine and fast even as a list of views because there is no re-allocating of memory # this is fast even for slicing a 10,000 x 5,000 LineStack - data_selections: List[np.ndarray] = list() + data_selections: list[np.ndarray] = list() for i, g in enumerate(source.graphics): if ixs[i].size == 0: @@ -317,7 +305,7 @@ def get_selected_data( def get_selected_indices( self, graphic: Graphic = None - ) -> Union[np.ndarray, List[np.ndarray]]: + ) -> np.ndarray | list[np.ndarray]: """ Returns the indices of the ``Graphic`` data bounded by the current selection. @@ -371,128 +359,6 @@ def get_selected_indices( # indices map directly to grid geometry for image data buffer return np.arange(*bounds, dtype=int) - def make_ipywidget_slider(self, kind: str = "IntRangeSlider", **kwargs): - """ - Makes and returns an ipywidget slider that is associated to this LinearSelector - - Parameters - ---------- - kind: str - "IntRangeSlider" or "FloatRangeSlider" - - kwargs - passed to the ipywidget slider constructor - - Returns - ------- - ipywidgets.Intslider or ipywidgets.FloatSlider - - """ - - if not IS_JUPYTER: - raise ImportError( - "Must installed `ipywidgets` to use `make_ipywidget_slider()`" - ) - - if kind not in ["IntRangeSlider", "FloatRangeSlider"]: - raise TypeError( - f"`kind` must be one of: 'IntRangeSlider', or 'FloatRangeSlider'\n" - f"You have passed: '{kind}'" - ) - - cls = getattr(ipywidgets, kind) - - value = self.selection - if "Int" in kind: - value = tuple(map(int, self.selection)) - - slider = cls( - min=self.limits[0], - max=self.limits[1], - value=value, - **kwargs, - ) - self.add_ipywidget_handler(slider) - - return slider - - def add_ipywidget_handler(self, widget, step: Union[int, float] = None): - """ - Bidirectionally connect events with a ipywidget slider - - Parameters - ---------- - widget: ipywidgets.IntRangeSlider or ipywidgets.FloatRangeSlider - ipywidget slider to connect to - - step: int or float, default ``None`` - step size, if ``None`` 100 steps are created - - """ - if not isinstance( - widget, (ipywidgets.IntRangeSlider, ipywidgets.FloatRangeSlider) - ): - raise TypeError( - f"`widget` must be one of: ipywidgets.IntRangeSlider or ipywidgets.FloatRangeSlider\n" - f"You have passed a: <{type(widget)}" - ) - - if step is None: - step = (self.limits[1] - self.limits[0]) / 100 - - if isinstance(widget, ipywidgets.IntSlider): - step = int(step) - - widget.step = step - - self._setup_ipywidget_slider(widget) - - def _setup_ipywidget_slider(self, widget): - # setup an ipywidget slider with bidirectional callbacks to this LinearSelector - value = self.selection - - if isinstance(widget, ipywidgets.IntSlider): - value = tuple(map(int, value)) - - widget.value = value - - # user changes widget -> linear selection changes - widget.observe(self._ipywidget_callback, "value") - - # user changes linear selection -> widget changes - self.add_event_handler(self._update_ipywidgets, "selection") - - self._plot_area.renderer.add_event_handler(self._set_slider_layout, "resize") - - self._handled_widgets.append(widget) - - def _update_ipywidgets(self, ev): - # update the ipywidget sliders when LinearSelector value changes - self._block_ipywidget_call = True # prevent infinite recursion - - value = ev.pick_info["new_data"] - # update all the handled slider widgets - for widget in self._handled_widgets: - if isinstance(widget, ipywidgets.IntSlider): - widget.value = tuple(map(int, value)) - else: - widget.value = value - - self._block_ipywidget_call = False - - def _ipywidget_callback(self, change): - # update the LinearSelector if the ipywidget value changes - if self._block_ipywidget_call or self._moving: - return - - self.selection = change["new"] - - def _set_slider_layout(self, *args): - w, h = self._plot_area.renderer.logical_size - - for widget in self._handled_widgets: - widget.layout = ipywidgets.Layout(width=f"{w}px") - def _move_graphic(self, delta: np.ndarray): # add delta to current min, max to get new positions if self.axis == "x": From 54d330c76ba85b9eaaa0411a21e11365c1d61eac Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Fri, 18 Oct 2024 08:26:27 -0400 Subject: [PATCH 119/185] better event docs, better docstring too (#654) --- docs/source/user_guide/guide.rst | 141 ++++++++++++++++++++++++------- fastplotlib/graphics/_base.py | 42 +++++++-- 2 files changed, 144 insertions(+), 39 deletions(-) diff --git a/docs/source/user_guide/guide.rst b/docs/source/user_guide/guide.rst index 0496054a7..d544c42a3 100644 --- a/docs/source/user_guide/guide.rst +++ b/docs/source/user_guide/guide.rst @@ -249,13 +249,11 @@ Let's take a look at how we can define events to link ``Graphics`` and their pro Events ------ -Events can be a multitude of things: traditional events such as mouse or keyboard events, but they can also be -You can use renderer events, such as mouse or keyboard events, or events related to ``Graphic`` properties. - +Events can be a multitude of things: traditional events such as mouse or keyboard events, or events related to ``Graphic`` properties. There are two ways to add events in ``fastplotlib``. -1) Use the method :: +1) Use the method `add_event_handler()` :: def event_handler(ev): pass @@ -275,29 +273,16 @@ There are two ways to add events in ``fastplotlib``. The ``event_handler`` is a user-defined function that accepts an event instance as the first and only positional argument. -Information about the structure of event instances are described below. The `"event_type"` +Information about the structure of event instances are described below. The ``"event_type"`` is a string that identifies the type of event; this can be either a ``pygfx.Event`` or a ``Graphic`` property event. -See the above graphic-specific properties that can be used for events and below for the available ``pygfx`` events. - -Rendering engine (``pygfx``) events: - - "key_down" - - "key_up" - - "pointer_down" - - "pointer_move" - - "pointer_up" - - "pointer_enter" - - "pointer_leave" - - "click" - - "double_click" - - "wheel" - - "close" - - "resize" - -When an event occurs, the user-defined event handler will receive and event object. Depending on the type of event, the + +``graphic.supported_events`` will return a tuple of all ``event_type`` strings that this graphic supports. + +When an event occurs, the user-defined event handler will receive an event object. Depending on the type of event, the event object will have relevant information that can be used in the callback. See below for event tables. -Event Attributes -^^^^^^^^^^^^^^^^ +Graphic property events +^^^^^^^^^^^^^^^^^^^^^^^ All ``Graphic`` events have the following attributes: @@ -427,13 +412,107 @@ event_type: "selection" | value | np.ndarray | new [min, max] of selection | +----------+------------+-----------------------------+ -Renderer Events -^^^^^^^^^^^^^^^ +Rendering engine events from a Graphic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Rendering engine event handlers can be added to a graphic or to a Figure (see next section). +Here is a description of all rendering engine events and their attributes. + +* **pointer_down**: emitted when the user interacts with mouse, + touch or other pointer devices, by pressing it down. + + * *x*: horizontal position of the pointer within the widget. + * *y*: vertical position of the pointer within the widget. + * *button*: the button to which this event applies. See "Mouse buttons" section below for details. + * *buttons*: a tuple of buttons being pressed down. + * *modifiers*: a tuple of modifier keys being pressed down. See section below for details. + * *ntouches*: the number of simultaneous pointers being down. + * *touches*: a dict with int keys (pointer id's), and values that are dicts + that contain "x", "y", and "pressure". + * *time_stamp*: a timestamp in seconds. + +* **pointer_up**: emitted when the user releases a pointer. + This event has the same keys as the pointer down event. + +* **pointer_move**: emitted when the user moves a pointer. + This event has the same keys as the pointer down event. + This event is throttled. + +* **double_click**: emitted on a double-click. + This event looks like a pointer event, but without the touches. + +* **wheel**: emitted when the mouse-wheel is used (scrolling), + or when scrolling/pinching on the touchpad/touchscreen. + + Similar to the JS wheel event, the values of the deltas depend on the + platform and whether the mouse-wheel, trackpad or a touch-gesture is + used. Also, scrolling can be linear or have inertia. As a rule of + thumb, one "wheel action" results in a cumulative ``dy`` of around + 100. Positive values of ``dy`` are associated with scrolling down and + zooming out. Positive values of ``dx`` are associated with scrolling + to the right. (A note for Qt users: the sign of the deltas is (usually) + reversed compared to the QWheelEvent.) + + On MacOS, using the mouse-wheel while holding shift results in horizontal + scrolling. In applications where the scroll dimension does not matter, + it is therefore recommended to use `delta = event['dy'] or event['dx']`. + + * *dx*: the horizontal scroll delta (positive means scroll right). + * *dy*: the vertical scroll delta (positive means scroll down or zoom out). + * *x*: the mouse horizontal position during the scroll. + * *y*: the mouse vertical position during the scroll. + * *buttons*: a tuple of buttons being pressed down. + * *modifiers*: a tuple of modifier keys being pressed down. + * *time_stamp*: a timestamp in seconds. + +* **key_down**: emitted when a key is pressed down. + + * *key*: the key being pressed as a string. See section below for details. + * *modifiers*: a tuple of modifier keys being pressed down. + * *time_stamp*: a timestamp in seconds. -You can also add events to a ``Figure`` object's renderer. This is useful for defining click events where -you want to map your click position to the nearest graphic object for example. +* **key_up**: emitted when a key is released. + This event has the same keys as the key down event. -Renderer events can be added using either method mentioned above (i.e. using the method or via a decorator). + +Time stamps +~~~~~~~~~~~ + +Since the time origin of ``time_stamp`` values is undefined, +time stamp values only make sense in relation to other time stamps. + + +Mouse buttons +~~~~~~~~~~~~~ + +* 0: No button. +* 1: Left button. +* 2: Right button. +* 3: Middle button +* 4-9: etc. + + +Keys +~~~~ + +The key names follow the `browser spec `_. + +* Keys that represent a character are simply denoted as such. For these the case matters: + "a", "A", "z", "Z" "3", "7", "&", " " (space), etc. +* The modifier keys are: + "Shift", "Control", "Alt", "Meta". +* Some example keys that do not represent a character: + "ArrowDown", "ArrowUp", "ArrowLeft", "ArrowRight", "F1", "Backspace", etc. + + +Add renderer event handlers to a Figure +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +You can add event handlers to a ``Figure`` object's renderer. For example, this is useful for defining click events +where you want to map click positions to the nearest graphic object. See the previous section for a description +of all the renderer events. + +Renderer event handlers can be added using a method or a decorator. For example: :: @@ -484,7 +563,9 @@ ImageWidget Often times, developing UIs for interacting with multi-dimension image data can be tedious and repetitive. In order to aid with common image and video visualization requirements the ``ImageWidget`` automatically generates sliders -to easily navigate through different dimensions of your data. Let's look at an example: :: +to easily navigate through different dimensions of your data. The image widget supports 2D, 3D and 4D arrays. + +Let's look at an example: :: import fastplotlib as fpl import imageio.v3 as iio diff --git a/fastplotlib/graphics/_base.py b/fastplotlib/graphics/_base.py index c3fc665e7..a25bc7176 100644 --- a/fastplotlib/graphics/_base.py +++ b/fastplotlib/graphics/_base.py @@ -220,22 +220,18 @@ def event_handlers(self) -> list[tuple[str, callable, ...]]: def add_event_handler(self, *args): """ - Register an event handler. + Register an event handler. Can also be used as a decorator. Parameters ---------- callback: callable, the first argument Event handler, must accept a single event argument - *types: list of strings - A list of event types, ex: "click", "data", "colors", "pointer_down" - For the available renderer event types, see - https://jupyter-rfb.readthedocs.io/en/stable/events.html + *types: strings + event types, ex: "click", "data", "colors", "pointer_down" - All feature support events, i.e. ``graphic.features`` will give a set of - all features that are evented - - Can also be used as a decorator. + ``supported_events`` will return a tuple of all event type strings that this graphic supports. + See the user guide in the documentation for more information on events. Example ------- @@ -316,6 +312,34 @@ def _handle_event(self, callback, event: pygfx.Event): callback(event) def remove_event_handler(self, callback, *types): + """ + remove a registered event handler + + Parameters + ---------- + callback: callable + event handler that has been added + + *types: strings + event types that were registered with the given callback + + Example + ------- + + .. code-block:: py + + # define event handler + def my_handler(event): + print(event) + + # add event handler + graphic.add_event_handler(my_handler, "pointer_up", "pointer_down") + + # remove event handler + graphic.remove_event_handler(my_handler, "pointer_up", "pointer_down") + + """ + # remove from our record first for t in types: for wrapper_map in self._event_handler_wrappers[t]: From 4a1b983e93306930be36b22539128b7e4aaf1a63 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Fri, 18 Oct 2024 08:27:06 -0400 Subject: [PATCH 120/185] deleted: .readthedocs.yaml (#655) --- .readthedocs.yaml | 41 ----------------------------------------- 1 file changed, 41 deletions(-) delete mode 100644 .readthedocs.yaml diff --git a/.readthedocs.yaml b/.readthedocs.yaml deleted file mode 100644 index ce6b214e4..000000000 --- a/.readthedocs.yaml +++ /dev/null @@ -1,41 +0,0 @@ -version: 2 - -build: - os: ubuntu-22.04 - tools: - python: "3.11" - apt_packages: - - libegl1-mesa - - libgl1-mesa-dri - - libxcb-xfixes0-dev - - mesa-vulkan-drivers - - libglfw3 - - pandoc # installs older version of pandoc which nbsphinx complains about, but works for now - jobs: - post_checkout: - # Download and uncompress the binary - # https://git-lfs.github.com/ - - wget https://github.com/git-lfs/git-lfs/releases/download/v3.1.4/git-lfs-linux-amd64-v3.1.4.tar.gz - - tar xvfz git-lfs-linux-amd64-v3.1.4.tar.gz - # Modify LFS config paths to point where git-lfs binary was downloaded - - git config filter.lfs.process "`pwd`/git-lfs filter-process" - - git config filter.lfs.smudge "`pwd`/git-lfs smudge -- %f" - - git config filter.lfs.clean "`pwd`/git-lfs clean -- %f" - # Make LFS available in current repository - - ./git-lfs install - # Download content from remote - - ./git-lfs fetch - # Make local files to have the real content on them - - ./git-lfs checkout - pre_install: - - pip install git+https://github.com/pygfx/pygfx.git@main - -sphinx: - configuration: docs/source/conf.py - -python: - install: - - method: pip - path: . - extra_requirements: - - docs From ec666e224e8140423eae380c17c8207b793f6724 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Fri, 18 Oct 2024 08:28:02 -0400 Subject: [PATCH 121/185] PlotArea.center_graphic() default zoom 1.35 -> 1.0 (#656) --- fastplotlib/layouts/_plot_area.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/fastplotlib/layouts/_plot_area.py b/fastplotlib/layouts/_plot_area.py index 5dc415ad7..76a956876 100644 --- a/fastplotlib/layouts/_plot_area.py +++ b/fastplotlib/layouts/_plot_area.py @@ -543,7 +543,7 @@ def _check_graphic_name_exists(self, name): f"All graphics within a subplot or plot area must have a unique name." ) - def center_graphic(self, graphic: Graphic, zoom: float = 1.35): + def center_graphic(self, graphic: Graphic, zoom: float = 1.0): """ Center the camera w.r.t. the passed graphic @@ -552,7 +552,7 @@ def center_graphic(self, graphic: Graphic, zoom: float = 1.35): graphic: Graphic The graphic instance to center on - zoom: float, default 1.3 + zoom: float zoom the camera after centering """ @@ -569,10 +569,11 @@ def center_scene(self, *, zoom: float = 1.0): Parameters ---------- - zoom: float, default 1.3 + zoom: float apply a zoom after centering the scene """ + if not len(self._fpl_graphics_scene.children) > 0: return @@ -600,10 +601,11 @@ def auto_scale( Maintain the camera aspect ratio for all dimensions. If ``None``, the aspect is left unchanged. if ``False`` the camera is scaled to the bounding box of the current scene. - zoom: float, default 0.8 - zoom value for the camera after auto-scaling, if zoom = 1.0 then the graphics - in the scene will fill the entire canvas. + zoom: float + zoom value for the camera after auto-scaling + """ + if not len(self._fpl_graphics_scene.children) > 0: return From 3575417cde836a680c9c0e0c738b4fc3fa96fffb Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Fri, 18 Oct 2024 08:50:42 -0400 Subject: [PATCH 122/185] cleanup CI (#661) * cleanup CI, add py3.13 * remove python3.13 --- .github/workflows/ci.yml | 16 +++++++--------- .github/workflows/docs-deploy.yml | 2 +- .github/workflows/pypi-publish.yml | 11 +++-------- .github/workflows/screenshots.yml | 9 ++------- 4 files changed, 13 insertions(+), 25 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 00310cf37..06272f0f4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,10 +30,9 @@ jobs: - name: Test py312 pyversion: '3.12' steps: - - name: Install git-lfs - run: | - sudo apt install --no-install-recommends -y git-lfs - uses: actions/checkout@v4 + with: + lfs: true - name: Set up Python uses: actions/setup-python@v5 with: @@ -41,7 +40,7 @@ jobs: - name: Install llvmpipe and lavapipe for offscreen canvas run: | sudo apt-get update -y -qq - sudo apt-get install --no-install-recommends -y ffmpeg libegl1-mesa-dev libgl1-mesa-dri libxcb-xfixes0-dev mesa-vulkan-drivers git-lfs + sudo apt-get install --no-install-recommends -y ffmpeg libegl1-mesa-dev libgl1-mesa-dri libxcb-xfixes0-dev mesa-vulkan-drivers - name: Install dev dependencies run: | python -m pip install --upgrade pip setuptools @@ -71,7 +70,7 @@ jobs: examples/diffs examples/notebooks/diffs - test-build-desktop: + test-build-offscreen: name: Test Linux, only offscreen runs-on: ubuntu-latest timeout-minutes: 10 @@ -87,10 +86,9 @@ jobs: - name: Test py312 pyversion: '3.12' steps: - - name: Install git-lfs - run: | - sudo apt install --no-install-recommends -y git-lfs - uses: actions/checkout@v4 + with: + lfs: true - name: Set up Python uses: actions/setup-python@v5 with: @@ -98,7 +96,7 @@ jobs: - name: Install llvmpipe and lavapipe for offscreen canvas run: | sudo apt-get update -y -qq - sudo apt-get install --no-install-recommends -y libegl1-mesa-dev libgl1-mesa-dri libxcb-xfixes0-dev mesa-vulkan-drivers git-lfs + sudo apt-get install --no-install-recommends -y libegl1-mesa-dev libgl1-mesa-dri libxcb-xfixes0-dev mesa-vulkan-drivers - name: Install dev dependencies run: | python -m pip install --upgrade pip setuptools diff --git a/.github/workflows/docs-deploy.yml b/.github/workflows/docs-deploy.yml index 713040ae8..63b0f81d7 100644 --- a/.github/workflows/docs-deploy.yml +++ b/.github/workflows/docs-deploy.yml @@ -35,7 +35,7 @@ jobs: - name: Install llvmpipe and lavapipe for offscreen canvas run: | sudo apt-get update -y -qq - sudo apt-get install --no-install-recommends -y ffmpeg libegl1-mesa-dev libgl1-mesa-dri libxcb-xfixes0-dev mesa-vulkan-drivers git-lfs + sudo apt-get install --no-install-recommends -y ffmpeg libegl1-mesa-dev libgl1-mesa-dri libxcb-xfixes0-dev mesa-vulkan-drivers - name: Install dev dependencies run: | python -m pip install --upgrade pip setuptools diff --git a/.github/workflows/pypi-publish.yml b/.github/workflows/pypi-publish.yml index 3fc70d7e1..9c5f3da59 100644 --- a/.github/workflows/pypi-publish.yml +++ b/.github/workflows/pypi-publish.yml @@ -21,14 +21,9 @@ jobs: runs-on: ubuntu-latest steps: - - name: Install git-lfs - run: | - sudo apt install --no-install-recommends -y git-lfs - uses: actions/checkout@v4 - - name: fetch git lfs files - run: | - git lfs fetch --all - git lfs pull + with: + lfs: true - name: Set up Python uses: actions/setup-python@v5 with: @@ -40,7 +35,7 @@ jobs: - name: Build package run: python -m build - name: Publish package - uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29 + uses: pypa/gh-action-pypi-publish@release/v1 with: user: __token__ password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.github/workflows/screenshots.yml b/.github/workflows/screenshots.yml index eeb992d46..87d8ca05a 100644 --- a/.github/workflows/screenshots.yml +++ b/.github/workflows/screenshots.yml @@ -17,10 +17,9 @@ jobs: timeout-minutes: 10 if: ${{ !github.event.pull_request.draft }} steps: - - name: Install git-lfs - run: | - sudo apt install --no-install-recommends -y git-lfs - uses: actions/checkout@v4 + with: + lfs: true - name: Set up Python 3.11 uses: actions/setup-python@v5 with: @@ -39,10 +38,6 @@ jobs: - name: Show wgpu backend run: python -c "from examples.tests.testutils import wgpu_backend; print(wgpu_backend)" - - name: fetch git lfs files - run: | - git lfs fetch --all - git lfs pull - name: Test examples env: PYGFX_EXPECT_LAVAPIPE: true From ef7b644a9a456e821c378ce29c12ec13bf205b50 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Fri, 18 Oct 2024 08:51:02 -0400 Subject: [PATCH 123/185] display adapters in HTML table upon import in notebooks (#659) * display adapters in HTML table upon import in notebooks * black --- fastplotlib/utils/gui.py | 57 ++++++++++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 14 deletions(-) diff --git a/fastplotlib/utils/gui.py b/fastplotlib/utils/gui.py index 1f13c1406..c9b2ad011 100644 --- a/fastplotlib/utils/gui.py +++ b/fastplotlib/utils/gui.py @@ -45,7 +45,7 @@ def _notebook_print_banner(): from ipywidgets import Image - from IPython.display import display + from IPython.display import display, HTML logo_path = Path(__file__).parent.parent.joinpath( "assets", "fastplotlib_face_logo.png" @@ -54,40 +54,69 @@ def _notebook_print_banner(): with open(logo_path, "rb") as f: logo_data = f.read() + # get small logo image image = Image(value=logo_data, format="png", width=300, height=55) - display(image) - - # print logo and adapter info + # get adapters and info adapters = [a for a in wgpu.gpu.enumerate_adapters()] adapters_info = [a.info for a in adapters] default_adapter_info = wgpu.gpu.request_adapter().info default_ix = adapters_info.index(default_adapter_info) - if len(adapters) > 0: - print("Available devices:") + if len(adapters) < 1: + return + + # start HTML table + table_str = ( + "Available devices:" + "" + "" + "" + "" + "" + "" + "" + "" + ) + # parse each adapter that WGPU found for ix, adapter in enumerate(adapters_info): atype = adapter["adapter_type"] backend = adapter["backend_type"] driver = adapter["description"] device = adapter["device"] - if atype == "DiscreteGPU" and backend != "OpenGL": - charactor = chr(0x2705) - elif atype == "IntegratedGPU" and backend != "OpenGL": - charactor = chr(0x0001FBC4) + if atype in ("DiscreteGPU", "IntegratedGPU") and backend != "OpenGL": + charactor = chr(0x2705) # green checkmark + tooltip = "This adapter can be used with fastplotlib" + elif backend == "OpenGL": + charactor = chr(0x0000274C) # red x + tooltip = "This adapter cannot be used with fastplotlib" + elif device.startswith("llvmpipe") or atype == "CPU": + charactor = f"{chr(0x00002757)} limited" # red ! + tooltip = "CPU rendering support is limited and mainly for testing purposes" else: - charactor = chr(0x2757) + charactor = f"{chr(0x00002757)} unknown" # red ! + tooltip = "Unknown adapter type and backend" if ix == default_ix: default = " (default) " else: - default = " " + default = "" - output_str = f"{charactor}{default}| {device} | {atype} | {backend} | {driver}" - print(output_str) + # add row to HTML table + table_str += f'' + # add each element to this row + for s in [f"{charactor}{default}", device, atype, backend, driver]: + table_str += f"" + table_str += "" + + table_str += "
ValidDeviceTypeBackendDriver
{s}
" + + # display logo and adapters table + display(image) + display(HTML(table_str)) if GUI_BACKEND == "jupyter": From bd69578746bad8624299fd64e3ad3c837512f545 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Thu, 24 Oct 2024 08:44:05 -0400 Subject: [PATCH 124/185] Update LICENSE (#665) * Update LICENSE * Update LICENSE year --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index eb132f363..256b23cdb 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2023 Kushal Kolar + Copyright 2024 Kushal Kolar, Caitlin Lewis Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From f7a9ccad7778ed5b63f5868a3accde48c889385e Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Thu, 24 Oct 2024 08:44:53 -0400 Subject: [PATCH 125/185] Delete apt.txt (#666) --- apt.txt | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 apt.txt diff --git a/apt.txt b/apt.txt deleted file mode 100644 index 42b836e1a..000000000 --- a/apt.txt +++ /dev/null @@ -1,3 +0,0 @@ -libvulkan1 -mesa-vulkan-drivers -neofetch From 8590b8d3b32fc434a2b5e18008a422049e933a0e Mon Sep 17 00:00:00 2001 From: Caitlin Lewis <69729525+clewis7@users.noreply.github.com> Date: Thu, 24 Oct 2024 10:15:09 -0400 Subject: [PATCH 126/185] add vertex, edge, and fill color properties (#664) * add vertex, edge, and fill color properties * update api docs * lint * requested changes * add doc strings, fix linear selector --- .../api/selectors/LinearRegionSelector.rst | 3 + docs/source/api/selectors/LinearSelector.rst | 3 + .../api/selectors/RectangleSelector.rst | 3 + .../graphics/selectors/_base_selector.py | 65 +++++++++++++++++++ fastplotlib/graphics/selectors/_linear.py | 30 ++++++++- .../graphics/selectors/_linear_region.py | 15 +++-- fastplotlib/graphics/selectors/_rectangle.py | 32 +++++---- 7 files changed, 131 insertions(+), 20 deletions(-) diff --git a/docs/source/api/selectors/LinearRegionSelector.rst b/docs/source/api/selectors/LinearRegionSelector.rst index 9637dd8e1..6c8d2eefc 100644 --- a/docs/source/api/selectors/LinearRegionSelector.rst +++ b/docs/source/api/selectors/LinearRegionSelector.rst @@ -24,7 +24,9 @@ Properties LinearRegionSelector.axis LinearRegionSelector.block_events LinearRegionSelector.deleted + LinearRegionSelector.edge_color LinearRegionSelector.event_handlers + LinearRegionSelector.fill_color LinearRegionSelector.limits LinearRegionSelector.name LinearRegionSelector.offset @@ -33,6 +35,7 @@ Properties LinearRegionSelector.rotation LinearRegionSelector.selection LinearRegionSelector.supported_events + LinearRegionSelector.vertex_color LinearRegionSelector.visible LinearRegionSelector.world_object diff --git a/docs/source/api/selectors/LinearSelector.rst b/docs/source/api/selectors/LinearSelector.rst index c514f982c..b82e3c1df 100644 --- a/docs/source/api/selectors/LinearSelector.rst +++ b/docs/source/api/selectors/LinearSelector.rst @@ -24,7 +24,9 @@ Properties LinearSelector.axis LinearSelector.block_events LinearSelector.deleted + LinearSelector.edge_color LinearSelector.event_handlers + LinearSelector.fill_color LinearSelector.limits LinearSelector.name LinearSelector.offset @@ -33,6 +35,7 @@ Properties LinearSelector.rotation LinearSelector.selection LinearSelector.supported_events + LinearSelector.vertex_color LinearSelector.visible LinearSelector.world_object diff --git a/docs/source/api/selectors/RectangleSelector.rst b/docs/source/api/selectors/RectangleSelector.rst index 930e12c67..81c9afd66 100644 --- a/docs/source/api/selectors/RectangleSelector.rst +++ b/docs/source/api/selectors/RectangleSelector.rst @@ -24,7 +24,9 @@ Properties RectangleSelector.axis RectangleSelector.block_events RectangleSelector.deleted + RectangleSelector.edge_color RectangleSelector.event_handlers + RectangleSelector.fill_color RectangleSelector.limits RectangleSelector.name RectangleSelector.offset @@ -33,6 +35,7 @@ Properties RectangleSelector.rotation RectangleSelector.selection RectangleSelector.supported_events + RectangleSelector.vertex_color RectangleSelector.visible RectangleSelector.world_object diff --git a/fastplotlib/graphics/selectors/_base_selector.py b/fastplotlib/graphics/selectors/_base_selector.py index 30643bbe4..5158a9239 100644 --- a/fastplotlib/graphics/selectors/_base_selector.py +++ b/fastplotlib/graphics/selectors/_base_selector.py @@ -3,6 +3,7 @@ from functools import partial import numpy as np +import pygfx from pygfx import WorldObject, Line, Mesh, Points @@ -40,6 +41,70 @@ class BaseSelector(Graphic): def axis(self) -> str: return self._axis + @property + def fill_color(self) -> pygfx.Color: + """Returns the fill color of the selector, ``None`` if selector has no fill.""" + return self._fill_color + + @fill_color.setter + def fill_color(self, color: str | Sequence[float]): + """ + Set the fill color of the selector. + + Parameters + ---------- + color : str | Sequence[float] + String or sequence of floats that gets converted into a ``pygfx.Color`` object. + """ + color = pygfx.Color(color) + for fill in self._fill: + fill.material.color = color + self._original_colors[fill] = color + self._fill_color = color + + @property + def vertex_color(self) -> pygfx.Color: + """Returns the vertex color of the selector, ``None`` if selector has no vertices.""" + return self._vertex_color + + @vertex_color.setter + def vertex_color(self, color: str | Sequence[float]): + """ + Set the vertex color of the selector. + + Parameters + ---------- + color : str | Sequence[float] + String or sequence of floats that gets converted into a ``pygfx.Color`` object. + """ + color = pygfx.Color(color) + for vertex in self._vertices: + vertex.material.color = color + vertex.material.edge_color = color + self._original_colors[vertex] = color + self._vertex_color = color + + @property + def edge_color(self) -> pygfx.Color: + """Returns the edge color of the selector""" + return self._edge_color + + @edge_color.setter + def edge_color(self, color: str | Sequence[float]): + """ + Set the edge color of the selector. + + Parameters + ---------- + color : str | Sequence[float] + String or sequence of floats that gets converted into a ``pygfx.Color`` object. + """ + color = pygfx.Color(color) + for edge in self._edges: + edge.material.color = color + self._original_colors[edge] = color + self._edge_color = color + def __init__( self, edges: Tuple[Line, ...] = None, diff --git a/fastplotlib/graphics/selectors/_linear.py b/fastplotlib/graphics/selectors/_linear.py index dfe7aadab..fe57036a3 100644 --- a/fastplotlib/graphics/selectors/_linear.py +++ b/fastplotlib/graphics/selectors/_linear.py @@ -47,6 +47,27 @@ def limits(self, values: tuple[float, float]): ) # if values are close to zero things get weird so round them self.selection._limits = self._limits + @property + def edge_color(self) -> pygfx.Color: + """Returns the color of the linear selector.""" + return self._edge_color + + @edge_color.setter + def edge_color(self, color: str | Sequence[float]): + """ + Set the color of the linear selector. + + Parameters + ---------- + color : str | Sequence[float] + String or sequence of floats that gets converted into a ``pygfx.Color`` object. + """ + color = pygfx.Color(color) + # only want to change inner line color + self._edges[0].material.color = color + self._original_colors[self._edges[0]] = color + self._edge_color = color + # TODO: make `selection` arg in graphics data space not world space def __init__( self, @@ -56,7 +77,7 @@ def __init__( center: float, axis: str = "x", parent: Graphic = None, - color: str | Sequence[float] | np.ndarray = "w", + edge_color: str | Sequence[float] | np.ndarray = "w", thickness: float = 2.5, arrow_keys_modifier: str = "Shift", name: str = None, @@ -92,13 +113,16 @@ def __init__( thickness: float, default 2.5 thickness of the selector - color: str | tuple | np.ndarray, default "w" + edge_color: str | tuple | np.ndarray, default "w" color of the selector name: str, optional name of linear selector """ + self._fill_color = None + self._edge_color = pygfx.Color(edge_color) + self._vertex_color = None if len(limits) != 2: raise ValueError("limits must be a tuple of 2 integers, i.e. (int, int)") @@ -134,7 +158,7 @@ def __init__( line_inner = pygfx.Line( # self.data.feature_data because data is a Buffer geometry=pygfx.Geometry(positions=line_data), - material=material(thickness=thickness, color=color, pick_write=True), + material=material(thickness=thickness, color=edge_color, pick_write=True), ) self.line_outer = pygfx.Line( diff --git a/fastplotlib/graphics/selectors/_linear_region.py b/fastplotlib/graphics/selectors/_linear_region.py index db7788d00..c1e6095f8 100644 --- a/fastplotlib/graphics/selectors/_linear_region.py +++ b/fastplotlib/graphics/selectors/_linear_region.py @@ -114,6 +114,9 @@ def __init__( name of this selector graphic """ + self._edge_color = pygfx.Color(edge_color) + self._fill_color = pygfx.Color(fill_color) + self._vertex_color = None # lots of very close to zero values etc. so round them, otherwise things get weird if not len(selection) == 2: @@ -134,13 +137,17 @@ def __init__( if axis == "x": mesh = pygfx.Mesh( pygfx.box_geometry(1, size, 1), - pygfx.MeshBasicMaterial(color=pygfx.Color(fill_color), pick_write=True), + pygfx.MeshBasicMaterial( + color=pygfx.Color(self.fill_color), pick_write=True + ), ) elif axis == "y": mesh = pygfx.Mesh( pygfx.box_geometry(size, 1, 1), - pygfx.MeshBasicMaterial(color=pygfx.Color(fill_color), pick_write=True), + pygfx.MeshBasicMaterial( + color=pygfx.Color(self.fill_color), pick_write=True + ), ) else: raise ValueError("`axis` must be one of 'x' or 'y'") @@ -179,7 +186,7 @@ def __init__( positions=init_line_data.copy() ), # copy so the line buffer is isolated pygfx.LineMaterial( - thickness=edge_thickness, color=edge_color, pick_write=True + thickness=edge_thickness, color=self.edge_color, pick_write=True ), ) line1 = pygfx.Line( @@ -187,7 +194,7 @@ def __init__( positions=init_line_data.copy() ), # copy so the line buffer is isolated pygfx.LineMaterial( - thickness=edge_thickness, color=edge_color, pick_write=True + thickness=edge_thickness, color=self.edge_color, pick_write=True ), ) diff --git a/fastplotlib/graphics/selectors/_rectangle.py b/fastplotlib/graphics/selectors/_rectangle.py index 356b437d7..51c3209b1 100644 --- a/fastplotlib/graphics/selectors/_rectangle.py +++ b/fastplotlib/graphics/selectors/_rectangle.py @@ -117,6 +117,10 @@ def __init__( xmin, xmax, ymin, ymax = selection + self._fill_color = pygfx.Color(fill_color) + self._edge_color = pygfx.Color(edge_color) + self._vertex_color = pygfx.Color(vertex_color) + width = xmax - xmin height = ymax - ymin @@ -125,7 +129,9 @@ def __init__( self.fill = pygfx.Mesh( pygfx.box_geometry(width, height, 1), - pygfx.MeshBasicMaterial(color=pygfx.Color(fill_color), pick_write=True), + pygfx.MeshBasicMaterial( + color=pygfx.Color(self.fill_color), pick_write=True + ), ) self.fill.world.position = (0, 0, -2) @@ -142,7 +148,7 @@ def __init__( left_line = pygfx.Line( pygfx.Geometry(positions=left_line_data.copy()), - pygfx.LineMaterial(thickness=edge_thickness, color=edge_color), + pygfx.LineMaterial(thickness=edge_thickness, color=self.edge_color), ) # position data for the right edge line @@ -155,7 +161,7 @@ def __init__( right_line = pygfx.Line( pygfx.Geometry(positions=right_line_data.copy()), - pygfx.LineMaterial(thickness=edge_thickness, color=edge_color), + pygfx.LineMaterial(thickness=edge_thickness, color=self.edge_color), ) # position data for the left edge line @@ -168,7 +174,7 @@ def __init__( bottom_line = pygfx.Line( pygfx.Geometry(positions=bottom_line_data.copy()), - pygfx.LineMaterial(thickness=edge_thickness, color=edge_color), + pygfx.LineMaterial(thickness=edge_thickness, color=self.edge_color), ) # position data for the right edge line @@ -181,7 +187,7 @@ def __init__( top_line = pygfx.Line( pygfx.Geometry(positions=top_line_data.copy()), - pygfx.LineMaterial(thickness=edge_thickness, color=edge_color), + pygfx.LineMaterial(thickness=edge_thickness, color=self.edge_color), ) self.edges: Tuple[pygfx.Line, pygfx.Line, pygfx.Line, pygfx.Line] = ( @@ -207,9 +213,9 @@ def __init__( pygfx.PointsMarkerMaterial( marker="square", size=vertex_thickness, - color=vertex_color, + color=self.vertex_color, size_mode="vertex", - edge_color=vertex_color, + edge_color=self.vertex_color, ), ) @@ -218,9 +224,9 @@ def __init__( pygfx.PointsMarkerMaterial( marker="square", size=vertex_thickness, - color=vertex_color, + color=self.vertex_color, size_mode="vertex", - edge_color=vertex_color, + edge_color=self.vertex_color, ), ) @@ -231,9 +237,9 @@ def __init__( pygfx.PointsMarkerMaterial( marker="square", size=vertex_thickness, - color=vertex_color, + color=self.vertex_color, size_mode="vertex", - edge_color=vertex_color, + edge_color=self.vertex_color, ), ) @@ -244,9 +250,9 @@ def __init__( pygfx.PointsMarkerMaterial( marker="square", size=vertex_thickness, - color=vertex_color, + color=self.vertex_color, size_mode="vertex", - edge_color=vertex_color, + edge_color=self.vertex_color, ), ) From 7a0e40f63ab923fbc15e5274900ac61a6bf41d70 Mon Sep 17 00:00:00 2001 From: Caitlin Lewis <69729525+clewis7@users.noreply.github.com> Date: Fri, 25 Oct 2024 17:28:40 -0400 Subject: [PATCH 127/185] update contrib guide, add developer notes (#667) * new pr for contrib guide * fix warning * fix formatting issues * reformat * Update docs/source/developer_notes/graphics.rst * Apply suggestions from code review --- CONTRIBUTING.md | 356 ++++++++++++++--------- docs/source/developer_notes/graphics.rst | 76 +++++ docs/source/developer_notes/index.rst | 39 +++ docs/source/developer_notes/layouts.rst | 84 ++++++ docs/source/index.rst | 1 + 5 files changed, 411 insertions(+), 145 deletions(-) create mode 100644 docs/source/developer_notes/graphics.rst create mode 100644 docs/source/developer_notes/index.rst create mode 100644 docs/source/developer_notes/layouts.rst diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0786596b4..6e1a5396c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,221 +1,299 @@ -# Contribution guide +# Contributing Guide -Contributions are welcome! :smile: +`fastplotlib` is a next-generation plotting library built on top of the `pygfx` rendering engine that leverages modern +GPU hardware and new graphics APIs to build large-scale scientific visualizations. We welcome and encourage contributions +from everyone! :smile: -## Installation +This guide explains how to contribute: if you have questions about the process, please +reach out on [GitHub Discussions](https://github.com/fastplotlib/fastplotlib/discussions). + +> **_NOTE:_** If you are already familiar with contributing to open-source software packages, +> please check out the [quick guide](#contributing-quick-guide)! + +## General Guidelines + +Developers are encouraged to contribute to various areas of development. This could include the addition of new features (e.g. +graphics or selector tools), bug fixes, or the addition of new examples to the [examples gallery](https://www.fastplotlib.org/ver/dev/_gallery/index.html). +Enhancements to documentation and the overall readability of the code are also greatly appreciated. + +Feel free to work on any section of the code that you believe you can improve. More importantly, remember to thoroughly test all +your classes and functions, and to provide clear, detailed comments within your code. This not only aids others in using the library, +but also facilitates future maintenance and further development. + +For more detailed information about `fastplotlib` modules, including design choices and implementation details, visit the +[`For Develeopers`](https://www.fastplotlib.org/ver/dev/developer_notes/index.html) section of the package documentation. + +## Contributing to the code + +### Contribution workflow cycle + +In order to contribute, you will need to do the following: + +1) Create your own branch +2) Make sure that tests pass +3) Open a Pull Request + +The `fastplotlib` package follows the [Git feature branch](https://www.atlassian.com/git/tutorials/comparing-workflows/feature-branch-workflow) workflow. In essence, `main` is the primary branch to which no one is allowed to +push directly. All development happens in separate feature branches that are then merged into `main` once we have determined they are ready. When enough changes have accumulated, a new release is +generated. This process includes adding a new tag to increment the version number and uploading the new release to PyPI. + +### Creating a development environment + +You will need a local installation of `fastplotlib` which keeps up-to-date with any changes you make. To do so, you will need to fork and clone `fastplotlib` before checking out a new branch. 1. Fork the repo to your own GitHub account, click the "Fork" button at the top: ![image](https://github.com/kushalkolar/fastplotlib/assets/9403332/82612021-37b2-48dd-b7e4-01a919535c17) -2. Clone the repo and install according to the development instructions. Replace the `YOUR_ACCOUNT` in the repo URL to the fork on your account. We use [git-lfs](https://git-lfs.com) for storing large files, such as ground-truths for tests, so you will need to [install it](https://github.com/git-lfs/git-lfs#installing) before cloning the repo. +2. We use [git-lfs](https://git-lfs.com) for storing large files, such as ground-truths for tests, so you will need +to [install it](https://github.com/git-lfs/git-lfs#installing) before cloning the repo. If you already have `git-lfs` +installed, ignore this step. + +3. Clone the repo. Replace the `YOUR_ACCOUNT` in the repo URL to the fork on your account. ```bash git clone https://github.com/YOUR_ACCOUNT/fastplotlib.git cd fastplotlib +``` +> **_NOTE:_** If you cloned the repo before installing `git-lfs`, you can run `git lfs pull` at any +> time to download the files stored on LFS + +4. Install `fastplotlib` in editable mode with developer dependencies + +```bash # install all extras in place pip install -e ".[notebook,docs,tests]" ``` -> If you cloned the repo before installing `git-lfs`, you can run `git lfs pull` at any -> time to download the files stored on LFS +5. Add the upstream remote branch: -3. Checkout the `main` branch, and then checkout your feature or bug fix branch, and run tests: +```bash +git remote add upstream https://github.com/fastplotlib/fastplotlib +``` -If your contributions modify how visualizations look, see the "Tests in detail" section at the very bottom. +At this point you have two remotes: `origin` (your fork) and `upstream` (the official fastplotlib org version). You won't have permission to push to upstream (only `origin`), but +this makes it easy to keep your `fastplotlib` up-to-date with the official fastplotlib org version by pulling from upstream: `git pull upstream`. -```bash -cd fastplotlib +### Creating a new branch +As mentioned previously, each feature in `fastplotlib` is worked on in a separate branch. This allows multiple people to develop multiple features simultaneously, without interfering with each other's work. To create +your own branch, run the following from within your `fastplotlib` directory: + +```bash +# switch to the main branch on your local copy git checkout main -# checkout your new branch from main -git checkout -b my-new-feature-branch +# update your local copy from your fork +git pull origin main -# make some changes, lint with black -black . +# sync your local copy with upstream main +git pull upstream main + +# update your fork's main branch with any changes from upstream +git push origin main + +# create and switch to a new branch, where you'll work on your new feature +git checkout -b my_feature_branch +``` + +After you have made changes on this branch, add and commit them when you are ready: + +```bash +# lint your code +black . # run tests from the repo root dir +WGPU_FORCE_OFFSCREEN=1 pytest tests/ + +# desktop examples pytest -v examples + +# notebook examples FASTPLOTLIB_NB_TESTS=1 pytest --nbmake examples/notebooks/ -# add your changed files, do not add any changes from screenshot diff dirs +# add your changed files, do not add any changes from the screenshot diff directory git add my_changed_files -# commit changes -git commit -m "my new feature" +# commit your changes +git commit -m "A one-line message explaining the changes made" -# push changes to your fork -git push origin my-new-feature-branch +# push to the remote origin +git push origin my_feature_branch ``` +> **_NOTE:_** If your contributions modify how visualizations _look_, see the [Testing details](#testing-details) section at the very bottom. -4. Finally make a **draft** PR against the `main` branch. When you think the PR is ready, mark it for review to trigger tests using our CI pipeline. If you need to make changes, please set the PR to a draft when pushing further commits until it's ready for review scion. We will get back to your with any further suggestions! +> **_NOTE:_** If your contributions modify the API, you must regenerate the API docs before making a PR, see +> the [Documenation](#documentation) section below. -## How fastplotlib works +### Contributing your changes back to `fastplotlib` -Fastplotlib uses the [`pygfx`](https://github.com/pygfx/pygfx) rendering engine to give users a high-level scientific -plotting library. Some degree of familiarity with [`pygfx`](https://github.com/pygfx/pygfx) or rendering engines may -be useful depending on the type of contribution you're working on. +You can make any number of changes on your branch. Once you are happy with your changes, add tests to check that they run correctly and add +documentation to properly note your changes. +See below for details on how to [add tests](#adding-tests) and properly [document](#adding-documentation) your code. -There are currently 2 major subpackages within `fastplotlib`, `layouts` and `graphics`. The user-facing public -class within `layouts` is `Figure`. A user is intended to create a `Figure`, and -then add *Graphics* to subplots within that `Figure`. +Now you are ready to make a Pull Request. You can open a pull request by clicking on the big `Compare & pull request` button that appears at the top of the `fastplotlib` repo +after pushing to your branch (see [here](https://intersect-training.org/collaborative-git/03-pr/index.html) for a tutorial). -### Graphics +> **_NOTE:_** Please make sure that you initially make your PR as a **draft** PR against the `main` branch. When you think the PR is ready, mark +> it for review to trigger tests using our CI pipeline. If you need to make changes, please set the PR back to a draft when pushing further +> commits until it is ready for review again. -A `Graphic` is something that can be added to a `PlotArea` (described in detail in a later section). All the various -fastplotlib graphics, such as `ImageGraphic`, `ScatterGraphic`, etc. inherit from the `Graphic` base class in -`fastplotlib/graphics/_base.py`. It has a few properties that mostly wrap `pygfx` `WorldObject` properties and transforms. -These might change in the future (ex. `Graphic.position_x` etc.). +Your pull request should include the following: +- A summary including information on what you changed and why +- References to relevant issues or discussions +- Special notice to any portion of your changes where you have lingering questions (e.g., "was this the right way to implement this?") or +want reviewers to pay special attention to -All graphics can be given a string name for the user's convenience. This allows graphics to be easily accessed from -plots, ex: `subplot["some_image"]`. +Next, we will be notified of the pull request and will read it over. We will try to give an initial response quickly, and then do a longer in-depth +review, at which point you will probably need to respond to our comments, making changes as appropriate. We will then respond again, and proceed +in an iterative fashion until everyone is happy with the proposed changes. -All graphics contain a `world_object` property which is just the `pygfx.WorldObject` that this graphic uses. Fastplotlib -keeps a *private* global dictionary of all `WorldObject` instances and users are only given a weakref proxy to this world object. -This is due to garbage collection. This may be quite complicated for beginners, for more details see this PR: https://github.com/fastplotlib/fastplotlib/pull/160 . -If you are curious or have more questions on garbage collection in fastplotlib you're welcome to post an issue :D. +Once your changes are integrated, you will be added as a GitHub contributor. Thank you for being +a part of `fastplotlib`! -#### Graphic properties +### Style Guide -Graphic properties are all evented, and internally we called these "graphic features". They are the various -aspects of a graphic that the user can change. -The "graphic features" subpackage can be found at `fastplotlib/graphics/_features`. As we can see this -is a private subpackage and never meant to be accessible to users.. +As far as code style, please adhere to the following guidelines: -##### LineGraphic +- Longer, descriptive names are preferred (e.g., `x` is not an appropriate name for a variable), especially for anything user-facing, +such as methods, attributes, or arguments +- Any public method, property, or attribute must have complete type-annotated docstrings (see below for details). Private methods or +attributes do not need to have a complete docstring, but they probably should. -For example let's look at `LineGraphic` in `fastplotlib/graphics/line.py`. Every graphic has a class variable called -`_features` which is a set of all graphic properties that are evented. It has the following evented properties: -`"data", "colors", "cmap", "thickness"` in addition to properties common to all graphics, such as `"name", "offset", "rotation", and "visible"` +### Releases -Now look at the constructor for the `LineGraphic` base class `PositionsGraphic`, it first creates an instance of `VertexPositions`. -This is a class that manages vertex positions buffer. It defines the line, and provides additional useful functionality. -For example, every time that the `data` is changed, the new data will be marked for upload to the GPU before the next draw. -In addition, event handlers will be called if any event handlers are registered. +We create releases on GitHub and distribute via [pypi](https://pypi.org/), and try to follow [semantic versioning](https://semver.org/): -`VertexColors`behaves similarly, but it can perform additional parsing that can create the colors buffer from different -forms of user input. For example if a user runs: `line_graphic.colors = "blue"`, then `VertexColors.__setitem__()` will -create a buffer that corresponds to what `pygfx.Color` thinks is "blue". Users can also take advantage of fancy indexing, -ex: `line_graphics.colors[bool_array] = "red"` :smile: +> Given a version number MAJOR.MINOR.PATCH, increment the: +> 1. MAJOR version when you make incompatible API changes +> 2. MINOR version when you add functionality in a backward compatible manner +> 3. PATCH version when you make backward compatible bug fixes -`LineGraphic` also has a `VertexCmap`, this manages the line `VertexColors` instance to parse colormaps, for example: -`line_graphic.cmap = "jet"` or even `line_graphic.cmap[50:] = "viridis"`. +To release a new version, we [create a GitHub release](https://docs.github.com/en/repositories/releasing-projects-on-github/managing-releases-in-a-repository) with a new tag incrementing the version as described above. +Creating the GitHub release will trigger the deployment to pypi, via our `deploy` action (found in `.github/workflows/pypi-publish.yml`). +The built version will grab the version tag from the GitHub release, using [setuptools_scm](https://github.com/pypa/setuptools_scm). -`LineGraphic` also has a `thickness` property which is pretty simple, and `DeletedFeature` which is useful if you need -callbacks to indicate that the graphic has been deleted (for example, removing references to a graphic from a legend). +### Testing -Other graphics have properties that are relevant to them, for example `ImageGraphic` has `cmap`, `vmin`, `vmax`, -properties unique to images. +#### Testing Details -#### Selectors +As a plotting library we require two layers of testing. 1) We use a backend test suite that verifies the basic functionality of buffer managers, +graphics, layouts, etc., and 2) another test suite which verifies that the library renders plots that are visually correct. -Selectors are a fairly new subpackage at `fastplotlib/graphics/selectors` which is likely to change significantly -after https://github.com/pygfx/pygfx/pull/665 . This subpackage contains selection tools, such as line selectors -(horizontal or vertical lines that can be moved), linear region selectors, and a primitive polygon drawing selection tool. -All selector tools inherit from `BaseSelector` in `graphics/selectors/_base_selector.py` but this is likely to change -after the aforementioned `Input` class PR in `pygfx` and after https://github.com/fastplotlib/fastplotlib/pull/413 . +In order to do this, each example within the `examples` directory is run and an image of the canvas is taken and compared +with a ground-truth screenshot that we have manually inspected. Ground-truth images are stored using `git-lfs`. -### Layouts +The ground-truth images are located in: -#### PlotArea +``` +examples/desktop/screenshots +examples/notebooks/screenshots +``` + +The tests will produce slightly different imperceptible (to a human) results on different hardware when compared to the +ground-truth. A small RMSE tolerance has been chosen, `0.025` for most examples. If the output image and +ground-truth image are within that tolerance the test will pass. -This is the main base class within layouts. Subplots within a `Figure` and `Dock` areas within a `Subplot`, -inherit from `PlotArea`. +Some feature development may require the ground-truth screenshots to be updated. In the event that your changes require +this, please do the following: -`PlotArea` has the following key properties that allow it to be a "plot area" that can be used to view graphical objects: +1. Download the regenerated screenshots from the [`fastplotlib` GitHub Actions page](https://github.com/fastplotlib/fastplotlib/actions/workflows/screenshots.yml) for your specific PR -* scene - instance of `pygfx.Scene` -* canvas - instance of `WgpuCanvas` -* renderer - instance of `pygfx.WgpuRenderer` -* viewport - instance of `pygfx.Viewport` -* camera - instance of `pygfx.PerspectiveCamera`, we always just use `PerspectiveCamera` and just set `camera.fov = 0` for orthographic projections -* controller - instance of `pygfx.Controller` +2. Replace only the screenshots that your PR changes in your local `fastplotlib` screenshots directories with those downloaded -Abstract method that must be implemented in subclasses: +``` +examples/desktop/screenshots +examples/notebooks/screenshots +``` -* get_rect - musut return [x, y, width, height] that defines the viewport rect for this `PlotArea` +3. Commit your new screenshots and push them to your branch to get picked up by `git-lfs` -Properties specifically used by subplots in a Figure: +```bash +# add changes +git add examples/desktop/screenshots/ +git add examples/notebooks/screenshots/ -* parent - A parent if relevant, used by individual `Subplots` in `Figure`, and by `Dock` which are "docked" subplots at the edges of a subplot. -* position - if a subplot within a Figure, it is the position of this subplot within the `Figure` +# commit changes +git commit -m "update screenshots" -Other important properties: +# push changes +git push origin my_feature_branch +``` -* graphics - a tuple of weakref proxies to all `Graphics` within this `PlotArea`, users are only given weakref proxies to `Graphic` objects, all `Graphic` objects are stored in a private global dict. -* selectors - a tuple of weakref proxies to all selectors within this `PlotArea` -* legend - a tuple of weakref proxies to all legend graphics within this `PlotArea` -* name - plot areas are allowed to have names that the user can use for their convenience +#### Adding tests -Important methods: +Depending on the type of contribution you are making, new tests might need to be added to the repository. Unit tests for testing underlying functionality such as buffer managers, figure instantiation, and +more can be found in the `/tests` directory. However, we also test all of our desktop examples as well. -* add_graphic - add a `Graphic` to the `PlotArea`, append to the end of the `PlotArea._graphics` list -* insert_graphic - insert a `Graphic` to the `PlotArea`, insert to a specific position of the `PlotArea._graphics` list -* remove_graphic - remove a graphic from the `Scene`, **does not delete it** -* delete_graphic - delete a graphic from the `PlotArea`, performs garbage collection -* clear - deletes all graphics from the `PlotArea` -* center_graphic - center camera w.r.t. a `Graphic` -* center_scene - center camera w.r.t. entire `Scene` -* auto_scale - Auto-scale the camera w.r.t to the `Scene` +If you are adding a new example to the library, you will need to add the following comments to the top of your `.py` file in order to make sure it is both tested and added to the gallery. -In addition, `PlotArea` supports `__getitem__`, so you can do: `plot_area["graphic_name"]` to retrieve a `Graphic` by -name :smile: +```python +# test_example = true +# sphinx_gallery_pygfx_docs = 'screenshot' +``` -You can also check if a `PlotArea` has certain graphics, ex: `"some_image_name" in plot_area`, or `graphic_instance in plot_area` +### Documentation -#### Subplot +Documentation is a crucial part of open-source software and greatly influences the ability to use a codebase. As such, it is imperative that any new changes are +properly documented as outlined below. -This class inherits from `PlotArea` and `GraphicMethodsMixin`. +We use [`sphinx`](https://www.sphinx-doc.org/en/master/) for generating our documentation. In addition to this, we also use the [`sphinx-gallery`](https://sphinx-gallery.github.io/stable/index.html) +extension to build our examples gallery. -`GraphicMethodsMixin` is a simple class that just has all the `add_` methods. It is autogenerated by a utility script like this: +If you would like to build the documentation locally: ```bash -python scripts/generate_add_methods.py -``` +cd docs +# regenerate the api guide +python source/generate_api.py -Each `add_` method basically creates an instance of `Graphic`, adds it to the `Subplot`, and returns a weakref -proxy to the `Graphic`. +# build locally +make html +``` -Subplot has one property that is not in `PlotArea`: +#### Adding documentation -* docks: a `dict` of `PlotAreas` which are located at the "top", "right", "left", and "bottom" edges of a `Subplot`. By default their size is `0`. They are useful for putting things like histogram LUT tools. +All public-facing functions and classes should have complete docstrings, which start with a one-line short summary of the function, +a medium-length description of the function / class and what it does, and a complete description of all arguments and return values. +Docstrings should be comprehensive, providing the information necessary for a user to use the method or property without going through the code. -The key method in `Subplot` is an implementation of `get_rect` that returns the viewport rect for this subplot. +Private functions and classes should have sufficient explanation that other developers know what the function / class does and how to use it, +but do not need to be as extensive. -#### Figure +We follow the [numpydoc](https://numpydoc.readthedocs.io/en/latest/) conventions for docstring structure. -Now that we have understood `PlotArea` and `Subplot` we need a way for the user to create them! +### Contributing Quick Guide -A `Figure` contains a grid of subplot and has methods such as `show()` to output the figure. -`Figure.__init__` basically does a lot of parsing of user arguments to determine how to create -the subplots. All subplots within a `Figure` share the same canvas and use different viewports to create the subplots. +This section is a brief introduction to how to contribute to `fastplotlib`. It is intended for individuals who have prior experience with contributing +to open source software packages. -## Tests in detail +> **_NOTE:_** +> We use [git-lfs](https://git-lfs.com) for storing large files, such as ground-truths for tests, so you will need +> to [install it](https://github.com/git-lfs/git-lfs#installing) before cloning the repo. -Backend tests are in `tests/`, in addition as a plotting library CI pipeline produces things that -"look visually correct". Each example within the `examples` dir is run and an image of the canvas -is taken and compared with a ground-truth screenshot that we have manually inspected. -Ground-truth image are stored using `git-lfs`. +1) Fork and clone the repo -The ground-truth images are in: +2) Install locally with developer dependencies +```bash +# after cloning +cd fastplotlib +# install dev dependencies +pip install -e ".[tests, docs, notebook]" ``` -examples/desktop/screenshots -examples/notebooks/screenshots -``` - -The tests will produce slightly different imperceptible (to a human) results on different hardware when compared to the -ground-truth. A small RMSE tolerance has been chosen, `0.025` for most examples. If the output image and -ground-truth image are within that tolerance the test will pass. +3) Check out a feature branch from `main` -To run tests: +4) Lint codebase and make sure tests pass ```bash -# tests basic backend functionality -WGPU_FORCE_OFFSCREEN=1 pytest -v -s tests/ +# lint codebase +black . + +# run tests +# backend tests +WGPU_FORCE_OFFSCREEN=1 pytest tests/ # desktop examples pytest -v examples @@ -224,19 +302,7 @@ pytest -v examples FASTPLOTLIB_NB_TESTS=1 pytest --nbmake examples/notebooks/ ``` -If your contribution modifies a ground-truth test screenshot then replace the ground-truth image along with your PR and -also notify us of this in the PR. Likewise, if your contribution requires a new test or new ground-truth then include -this new image in your PR. - -You can create/regenerate ground-truths for the examples like this: - -```bash -# desktop examples -REGENERATE_SCREENSHOTS=1 pytest -v examples/ +5) Update screenshots if necessary ([see testing](#testing-details)) -# notebook examples -FASTPLOTLIB_NB_TESTS=1 REGENERATE_SCREENSHOTS=1 pytest --nbmake examples/notebooks/image_widget_test.ipynb -``` +6) Push and open a draft PR against `main` -**Please only commit ground-truth images that correspond to your PR** since this will generate ground-truth images for -the entire test suite. diff --git a/docs/source/developer_notes/graphics.rst b/docs/source/developer_notes/graphics.rst new file mode 100644 index 000000000..71d99854a --- /dev/null +++ b/docs/source/developer_notes/graphics.rst @@ -0,0 +1,76 @@ +Graphics +======== + + +A ``Graphic`` is something that can be added to a ``PlotArea`` (described in detail in the layouts section). All the various +fastplotlib graphics, such as ``ImageGraphic``, ``ScatterGraphic``, etc. inherit from the ``Graphic`` base class in +``fastplotlib/graphics/_base.py``. It has a few properties that mostly wrap ``pygfx`` ``WorldObject`` properties and transforms. + +Inheritance Diagram +------------------- + +.. code-block:: rst + + Graphic + │ + ├─ ImageGraphic + │ + ├─ TextGraphic + │ + ├─ PositionsGraphic + │ │ + │ ├─ LineGraphic + │ │ + │ └─ ScatterGraphic + │ + └─ GraphicCollection + │ + └─ LineCollection + │ + └─ LineStack + +.. + +All graphics can be given a string name for the user's convenience. This allows graphics to be easily accessed from +plots, ex: ``subplot["some_image"]``. + +All graphics contain a ``world_object`` property which is just the ``pygfx.WorldObject`` that this graphic uses. Fastplotlib +keeps a *private* global dictionary of all ``WorldObject`` instances and users are only given a weakref proxy to this world object. +This is due to garbage collection. This may be quite complicated for beginners, for more details see this PR: https://github.com/fastplotlib/fastplotlib/pull/160 . +Furthermore, garbage collection is even more complicated in ipython and jupyter (see https://github.com/fastplotlib/fastplotlib/pull/546 ). +If you are curious or have more questions on garbage collection in ``fastplotlib`` you're welcome to post an issue :D. + +Graphic collections are collections of graphics. For now, we have a ``LineCollection`` which is a collection of ``LineGraphic`` objects. We also have a ``LineStack`` which +inherits from ``LineCollection`` and gives some fixed offset between ``LineGraphic`` objects in the collection. A graphic collection behaves like an array of graphics. + +Graphic Properties +------------------ + +Graphic properties are all evented, and internally we call these "graphic features". They are the various +aspects of a graphic that the user can change. +The "graphic features" subpackage can be found at ``fastplotlib/graphics/_features``. As we can see this +is a private subpackage and never meant to be accessible to users. + +For example let's look at ``LineGraphic`` in ``fastplotlib/graphics/line.py``. Every graphic has a class variable called +``_features`` which is a set of all graphic properties that are evented. It has the following evented properties: +``"data", "colors", "cmap", "thickness"`` in addition to properties common to all graphics, such as ``"name", "offset", "rotation", and "visible"`` + +Now look at the constructor for the ``LineGraphic`` base class ``PositionsGraphic``, it first creates an instance of ``VertexPositions``. +This is a class that manages vertex positions buffer. For the user, it defines the line data, and provides additional useful functionality. +It defines the line, and provides additional useful functionality. +For example, every time that the ``data`` is changed, the new data will be marked for upload to the GPU before the next draw. +In addition, event handlers will be called if any event handlers are registered. + +``VertexColors`` behaves similarly, but it can perform additional parsing that can create the colors buffer from different +forms of user input. For example if a user runs: ``line_graphic.colors = "blue"``, then ``VertexColors.__setitem__()`` will +create a buffer that corresponds to what ``pygfx.Color`` thinks is "blue". Users can also take advantage of fancy indexing, +ex: ``line_graphics.colors[bool_array] = "red"`` 😊 + +``LineGraphic`` also has a ``VertexCmap``, this manages the line ``VertexColors`` instance to parse colormaps, for example: +``line_graphic.cmap = "jet"`` or even ``line_graphic.cmap[50:] = "viridis"``. + +``LineGraphic`` also has a ``thickness`` property which is pretty simple, and ``DeletedFeature`` which is useful if you need +callbacks to indicate that the graphic has been deleted (for example, removing references to a graphic from a legend). + +Other graphics have properties that are relevant to them, for example ``ImageGraphic`` has ``cmap``, ``vmin``, ``vmax``, +properties unique to images. \ No newline at end of file diff --git a/docs/source/developer_notes/index.rst b/docs/source/developer_notes/index.rst new file mode 100644 index 000000000..b0a448981 --- /dev/null +++ b/docs/source/developer_notes/index.rst @@ -0,0 +1,39 @@ +Developer Notes +*************** + +Welcome to the Developer Notes for `fastplotlib`. These notes aim to provide detailed and technical information +about the various modules, classes, and functions that make up this library, as well as guidelines on how to write +code that integrates nicely with our package. They are intended to help current and future developers understand +the design decisions, and functioning of the library. + +**Intended Audience** + +These notes are primarily intended for the following groups: + +- **Current Developers**: The Developer Notes can serve as a comprehensive guide to understanding the library, making it easier to debug, modify and maintain the code. + +- **Future Developers**: These notes can help onboard new developers to the project, providing them with detailed explanations of the codebase and its underlying architecture. + +- **Contributors**: If you wish to contribute to the `fastplotlib` project, the Developer Notes can provide a solid foundation of understanding, helping to ensure that your contributions align with the existing structure and design principles of the library. + +- **Advanced Users**: While the primary focus of these notes is on development, they might also be of interest to advanced users who want a deeper understanding of the library's functionality. + +Please note that these notes assume a certain level of programming knowledge. Familiarity with Python, object-oriented programming, and the NumPy and pygfx libraries would be beneficial when reading these notes. + +**Sections** + +.. toctree:: + :maxdepth: 2 + + graphics + layouts + +**Interact with us** + +If you're considering contributing to the library, first of all, welcome aboard! As a first step, we recommend that you read the `CONTRIBUTING.md `_ guidelines. +These will help you understand how to interact with other contributors and how to submit your changes. + +If you have any questions or need further clarification on any of the topics covered in these notes, please don't hesitate to reach out to us. You can do so via the `discussion `_ forum on GitHub. + +We're looking forward to your contributions and to answering any questions you might have! + diff --git a/docs/source/developer_notes/layouts.rst b/docs/source/developer_notes/layouts.rst new file mode 100644 index 000000000..4aacd38da --- /dev/null +++ b/docs/source/developer_notes/layouts.rst @@ -0,0 +1,84 @@ +Layouts +======= + +PlotArea +-------- + +This is the main base class within layouts. A ``Figure`` and ``Dock`` are areas within a ``Subplot`` that +inherit from ``PlotArea``. + +``PlotArea`` has the following key properties that allow it to be a "plot area" that can be used to view graphical objects: + +* scene - instance of ``pygfx.Scene`` +* canvas - instance of ``WgpuCanvas`` +* renderer - instance of ``pygfx.WgpuRenderer`` +* viewport - instance of ``pygfx.Viewport`` +* camera - instance of ``pygfx.PerspectiveCamera``, we always just use ``PerspectiveCamera`` and just set ``camera.fov = 0`` for orthographic projections +* controller - instance of ``pygfx.Controller`` + +If these concepts are unfamiliar to you we recommend learning about how rendering engines work, the pygfx guide +is a great place to start! https://docs.pygfx.org/stable/guide.html + +Here is also a short video that goes through the basic concepts: https://www.youtube.com/watch?v=cvcAjgMUPUA + +Abstract method that must be implemented in subclasses: + +* get_rect - must return [x, y, width, height] that defines the viewport rect for this ``PlotArea`` + +Properties specifically used by subplots in a Figure: + +* parent - A parent if relevant, used by individual ``Subplots`` in ``Figure``, and by ``Dock`` which are "docked" subplots at the edges of a subplot. +* position - if a subplot within a ``Figure``, it is the position of this subplot within the ``Figure`` + +Other important properties: + +* graphics - a tuple of weakref proxies to all ``Graphics`` within this ``PlotArea``, users are only given weakref proxies to ``Graphic`` objects, all ``Graphic`` objects are stored in a private global dict. +* selectors - a tuple of weakref proxies to all selectors within this ``PlotArea`` +* legend - a tuple of weakref proxies to all legend graphics within this ``PlotArea`` +* name - plot areas are allowed to have names that the user can use for their convenience + +Important methods: + +* add_graphic - add a ``Graphic`` to the ``PlotArea``, append to the end of the ``PlotArea._graphics`` list +* insert_graphic - insert a ``Graphic`` to the ``PlotArea``, insert to a specific position of the ``PlotArea._graphics`` list +* remove_graphic - remove a graphic from the ``Scene``, **does not delete it** +* delete_graphic - delete a graphic from the ``PlotArea``, performs garbage collection +* clear - deletes all graphics from the ``PlotArea`` +* center_graphic - center camera w.r.t. a ``Graphic`` +* center_scene - center camera w.r.t. entire ``Scene`` +* auto_scale - Auto-scale the camera w.r.t to the ``Scene`` + +In addition, ``PlotArea`` supports ``__getitem__``, so you can do: ``plot_area["graphic_name"]`` to retrieve a ``Graphic`` by +name. + +You can also check if a ``PlotArea`` has certain graphics, ex: ``"some_image_name" in plot_area``, or ``graphic_instance in plot_area`` + +Subplot +------- + +This class inherits from ``PlotArea`` and ``GraphicMethodsMixin``. + +``GraphicMethodsMixin`` is a simple class that just has all the ``add_`` methods. It is autogenerated by a utility script like this: + +.. code-block:: bash + + python scripts/generate_add_methods.py + +Each ``add_`` method basically creates an instance of ``Graphic``, adds it to the ``Subplot``, and returns the ``Graphic``. + +``Subplot`` has one property that is not in ``PlotArea``: + +* docks: a ``dict`` of ``PlotAreas`` which are located at the "top", "right", "left", and "bottom" edges of a ``Subplot``. + +By default their size is ``0``. They are useful for putting things like histogram LUT tools. + +The key method in ``Subplot`` is an implementation of ``get_rect`` that returns the viewport rect for this subplot. + +Figure +------ + +Now that we have understood ``PlotArea`` and ``Subplot`` we need a way for the user to create them! + +A ``Figure`` contains a grid of subplot and has methods such as ``show()`` to output the figure. +``Figure.__init__`` basically does a lot of parsing of user arguments to determine how to create +the subplots. All subplots within a ``Figure`` share the same canvas and use different viewports to create the subplots. \ No newline at end of file diff --git a/docs/source/index.rst b/docs/source/index.rst index 2794bc17e..c44f4e3a8 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -6,6 +6,7 @@ Welcome to fastplotlib's documentation! :maxdepth: 2 user_guide/index + developer_notes/index .. toctree:: :maxdepth: 2 From f0f80c6a4deabb0941154584c854f2004b0cc316 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Mon, 28 Oct 2024 16:44:51 -0400 Subject: [PATCH 128/185] update comments for image data shapes (#668) --- fastplotlib/graphics/_features/_image.py | 2 +- fastplotlib/graphics/image.py | 2 +- fastplotlib/widgets/image_widget/_widget.py | 7 ++++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/fastplotlib/graphics/_features/_image.py b/fastplotlib/graphics/_features/_image.py index 513677e15..53bcc5bc4 100644 --- a/fastplotlib/graphics/_features/_image.py +++ b/fastplotlib/graphics/_features/_image.py @@ -94,7 +94,7 @@ def _fix_data(self, data): if data.ndim not in (2, 3): raise ValueError( "image data must be 2D with or without an RGB(A) dimension, i.e. " - "it must be of shape [x, y], [x, y, 3] or [x, y, 4]" + "it must be of shape [rows, cols], [rows, cols, 3] or [rows, cols, 4]" ) # let's just cast to float32 always diff --git a/fastplotlib/graphics/image.py b/fastplotlib/graphics/image.py index 62477d7ff..25a4741e0 100644 --- a/fastplotlib/graphics/image.py +++ b/fastplotlib/graphics/image.py @@ -91,7 +91,7 @@ def __init__( ---------- data: array-like array-like, usually numpy.ndarray, must support ``memoryview()`` - | shape must be ``[x_dim, y_dim]`` + | shape must be ``[n_rows, n_cols]``, ``[n_rows, n_cols, 3]`` for RGB or ``[n_rows, n_cols, 4]`` for RGBA vmin: int, optional minimum value for color scaling, calculated from data if not provided diff --git a/fastplotlib/widgets/image_widget/_widget.py b/fastplotlib/widgets/image_widget/_widget.py index 21c6add44..413038c15 100644 --- a/fastplotlib/widgets/image_widget/_widget.py +++ b/fastplotlib/widgets/image_widget/_widget.py @@ -12,8 +12,9 @@ from ._sliders import ImageWidgetSliders -# Number of dimensions that represent one image/one frame. For grayscale shape will be [x, y], i.e. 2 dims, for RGB(A) -# shape will be [x, y, c] where c is of size 3 (RGB) or 4 (RGBA) +# Number of dimensions that represent one image/one frame +# For grayscale shape will be [n_rows, n_cols], i.e. 2 dims +# For RGB(A) shape will be [n_rows, n_cols, c] where c is of size 3 (RGB) or 4 (RGBA) IMAGE_DIM_COUNTS = {"gray": 2, "rgb": 3} # Map boolean (indicating whether we use RGB or grayscale) to the string. Used to index RGB_DIM_MAP @@ -162,7 +163,7 @@ def ndim(self) -> int: def n_scrollable_dims(self) -> list[int]: """ list indicating the number of dimenensions that are scrollable for each data array - All other dimensions are frame/image data, i.e. [x, y] or [x, y, c] + All other dimensions are frame/image data, i.e. [rows, cols] or [rows, cols, rgb(a)] """ return self._n_scrollable_dims From 07053e73264b6f72b4d44b10dacd2143ba60f69d Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Mon, 6 Jan 2025 09:05:57 -0500 Subject: [PATCH 129/185] updates related to upstream changes (#687) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * use imageio[ffmpeg] instead of pyav (#686) * fix typo in the requested limit name of the wgpu device (#679) * fix typo in the requested limit name of the wgpu device. * fix test config. * fix examples/test and docs * fix max-texture-dimension-2d limit name and replace underscore by hyphen for the other limits. * fix missing dependency (libxrandr) to compile GLFW during building wheel for imgui-bundle. * update CI timeout * update timeout in non-nb test job --------- Co-authored-by: Kushal Kolar * update to use pygfx.TextureMap * forgot to set imageio[ffmpeg] for all extras * migrate to RenderCanvas, remove event_filter stuff for imgui since that was fixed in pygfx * remove very old irrelevant example * fix docstring * update examples to use fpl.loop.run() * update API docs * black * import ImageWidget only if imgui is installed * enumerate_adapters() -> enumerate_adapters_sync() * remove annoying warnings from cmap * remove more cmap warnings * black --------- Co-authored-by: Jérémie Fache --- .github/workflows/ci.yml | 8 +- docs/source/api/fastplotlib.rst | 4 +- docs/source/api/ui/Popup.rst | 2 - docs/source/conf.py | 2 +- docs/source/generate_api.py | 4 +- docs/source/user_guide/gpu.rst | 64 ++++++------- examples/gridplot/gridplot.py | 2 +- examples/gridplot/gridplot_non_square.py | 2 +- examples/gridplot/multigraphic_gridplot.py | 2 +- examples/guis/image_widget_imgui.py | 2 +- examples/guis/imgui_basic.py | 2 +- examples/heatmap/heatmap.py | 2 +- examples/image/image_cmap.py | 2 +- examples/image/image_rgb.py | 2 +- examples/image/image_rgbvminvmax.py | 2 +- examples/image/image_simple.py | 2 +- examples/image/image_small.py | 2 +- examples/image/image_vminvmax.py | 2 +- examples/image_widget/image_widget.py | 4 +- examples/image_widget/image_widget_grid.py | 2 +- .../image_widget/image_widget_single_video.py | 2 +- examples/image_widget/image_widget_videos.py | 2 +- examples/line/line.py | 2 +- examples/line/line_cmap.py | 2 +- examples/line/line_cmap_more.py | 2 +- examples/line/line_colorslice.py | 2 +- examples/line/line_dataslice.py | 2 +- examples/line_collection/line_collection.py | 2 +- .../line_collection_cmap_values.py | 2 +- ...line_collection_cmap_values_qualitative.py | 2 +- .../line_collection/line_collection_colors.py | 2 +- .../line_collection_slicing.py | 2 +- examples/line_collection/line_stack.py | 2 +- examples/line_collection/line_stack_3d.py | 2 +- examples/machine_learning/covariance.py | 2 +- examples/misc-dev/large_img.py | 35 -------- examples/misc/cycle_animation.py | 2 +- examples/misc/em_wave_animation.py | 2 +- examples/misc/image_animation.py | 2 +- examples/misc/line3d_animation.py | 2 +- examples/misc/line_animation.py | 2 +- examples/misc/lorenz_animation.py | 2 +- examples/misc/multiplot_animation.py | 2 +- examples/misc/scatter_animation.py | 2 +- examples/misc/scatter_sizes_animation.py | 2 +- examples/misc/simple_event.py | 2 +- examples/qt/embed.py | 4 +- examples/qt/imagewidget.py | 4 +- examples/qt/minimal.py | 2 +- examples/scatter/scatter.py | 4 +- examples/scatter/scatter_cmap.py | 6 +- examples/scatter/scatter_cmap_iris.py | 2 +- examples/scatter/scatter_colorslice.py | 4 +- examples/scatter/scatter_colorslice_iris.py | 2 +- examples/scatter/scatter_dataslice.py | 2 +- examples/scatter/scatter_dataslice_iris.py | 2 +- examples/scatter/scatter_iris.py | 2 +- examples/scatter/scatter_size.py | 2 +- .../linear_region_line_collection.py | 2 +- .../selection_tools/linear_region_selector.py | 2 +- .../linear_region_selectors_match_offsets.py | 2 +- examples/selection_tools/linear_selector.py | 2 +- .../selection_tools/linear_selector_image.py | 2 +- .../selection_tools/rectangle_selector.py | 2 +- .../rectangle_selector_zoom.py | 2 +- examples/tests/test_examples.py | 2 +- fastplotlib/__init__.py | 4 +- fastplotlib/graphics/_features/_image.py | 9 +- fastplotlib/graphics/image.py | 21 ++--- fastplotlib/layouts/_figure.py | 34 +++---- fastplotlib/layouts/_imgui_figure.py | 6 +- fastplotlib/layouts/_plot_area.py | 9 +- fastplotlib/layouts/_subplot.py | 10 ++- fastplotlib/layouts/_utils.py | 51 +++-------- fastplotlib/ui/_base.py | 25 ------ .../ui/right_click_menus/_colormap_picker.py | 5 -- .../ui/right_click_menus/_standard_menu.py | 5 -- fastplotlib/utils/__init__.py | 2 +- fastplotlib/utils/functions.py | 90 ++++++++++++++++++- fastplotlib/utils/gpu.py | 4 +- fastplotlib/utils/gui.py | 10 +-- fastplotlib/widgets/image_widget/_widget.py | 22 ++--- setup.py | 6 +- tests/conftest.py | 2 +- tests/test_image_graphic.py | 11 ++- 85 files changed, 281 insertions(+), 296 deletions(-) delete mode 100644 examples/misc-dev/large_img.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 06272f0f4..1fe4a128b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: test-build-full: name: Test Linux, notebook + offscreen runs-on: ubuntu-latest - timeout-minutes: 10 + timeout-minutes: 30 if: ${{ !github.event.pull_request.draft }} strategy: fail-fast: false @@ -40,7 +40,7 @@ jobs: - name: Install llvmpipe and lavapipe for offscreen canvas run: | sudo apt-get update -y -qq - sudo apt-get install --no-install-recommends -y ffmpeg libegl1-mesa-dev libgl1-mesa-dri libxcb-xfixes0-dev mesa-vulkan-drivers + sudo apt-get install --no-install-recommends -y ffmpeg libegl1-mesa-dev libgl1-mesa-dri libxcb-xfixes0-dev mesa-vulkan-drivers xorg-dev - name: Install dev dependencies run: | python -m pip install --upgrade pip setuptools @@ -73,7 +73,7 @@ jobs: test-build-offscreen: name: Test Linux, only offscreen runs-on: ubuntu-latest - timeout-minutes: 10 + timeout-minutes: 30 if: ${{ !github.event.pull_request.draft }} strategy: fail-fast: false @@ -96,7 +96,7 @@ jobs: - name: Install llvmpipe and lavapipe for offscreen canvas run: | sudo apt-get update -y -qq - sudo apt-get install --no-install-recommends -y libegl1-mesa-dev libgl1-mesa-dri libxcb-xfixes0-dev mesa-vulkan-drivers + sudo apt-get install --no-install-recommends -y libegl1-mesa-dev libgl1-mesa-dri libxcb-xfixes0-dev mesa-vulkan-drivers xorg-dev - name: Install dev dependencies run: | python -m pip install --upgrade pip setuptools diff --git a/docs/source/api/fastplotlib.rst b/docs/source/api/fastplotlib.rst index 74349156f..34dc89049 100644 --- a/docs/source/api/fastplotlib.rst +++ b/docs/source/api/fastplotlib.rst @@ -11,4 +11,6 @@ fastplotlib .. autofunction:: fastplotlib.print_wgpu_report -.. autofunction:: fastplotlib.run +fastplotlib.loop +------------------ +See the rendercanvas docs: https://rendercanvas.readthedocs.io/stable/api.html#rendercanvas.BaseLoop \ No newline at end of file diff --git a/docs/source/api/ui/Popup.rst b/docs/source/api/ui/Popup.rst index a154e9ce9..5e924db94 100644 --- a/docs/source/api/ui/Popup.rst +++ b/docs/source/api/ui/Popup.rst @@ -26,8 +26,6 @@ Methods .. autosummary:: :toctree: Popup_api - Popup.clear_event_filters Popup.open - Popup.set_event_filter Popup.update diff --git a/docs/source/conf.py b/docs/source/conf.py index f65713270..9f8e34ab1 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -18,7 +18,7 @@ import imageio.v3 as iio MAX_TEXTURE_SIZE = 2048 -pygfx.renderers.wgpu.set_wgpu_limits(**{"max-texture-dimension2d": MAX_TEXTURE_SIZE}) +pygfx.renderers.wgpu.set_wgpu_limits(**{"max-texture-dimension-2d": MAX_TEXTURE_SIZE}) ROOT_DIR = Path(__file__).parents[1].parents[0] # repo root EXAMPLES_DIR = Path.joinpath(ROOT_DIR, "examples") diff --git a/docs/source/generate_api.py b/docs/source/generate_api.py index 488b28466..6887566cb 100644 --- a/docs/source/generate_api.py +++ b/docs/source/generate_api.py @@ -51,7 +51,9 @@ ".. autofunction:: fastplotlib.print_wgpu_report\n\n" - ".. autofunction:: fastplotlib.run\n" + "fastplotlib.loop\n" + "------------------\n" + "See the rendercanvas docs: https://rendercanvas.readthedocs.io/stable/api.html#rendercanvas.BaseLoop " ) with open(API_DIR.joinpath("utils.rst"), "w") as f: diff --git a/docs/source/user_guide/gpu.rst b/docs/source/user_guide/gpu.rst index 392eb672b..428c8600b 100644 --- a/docs/source/user_guide/gpu.rst +++ b/docs/source/user_guide/gpu.rst @@ -235,38 +235,38 @@ Example output:: adapter device - max_bind_groups: 8 8 - max_bind_groups_plus_vertex_buffers: 0 0 - max_bindings_per_bind_group: 1.00K 1.00K - max_buffer_size: 2.14G 2.14G - max_color_attachment_bytes_per_sample: 0 0 - max_color_attachments: 0 0 - max_compute_invocations_per_workgroup: 1.02K 1.02K - max_compute_workgroup_size_x: 1.02K 1.02K - max_compute_workgroup_size_y: 1.02K 1.02K - max_compute_workgroup_size_z: 1.02K 1.02K - max_compute_workgroup_storage_size: 32.7K 32.7K - max_compute_workgroups_per_dimension: 65.5K 65.5K - max_dynamic_storage_buffers_per_pipeline_layout: 8 8 - max_dynamic_uniform_buffers_per_pipeline_layout: 16 16 - max_inter_stage_shader_components: 128 128 - max_inter_stage_shader_variables: 0 0 - max_sampled_textures_per_shader_stage: 8.38M 8.38M - max_samplers_per_shader_stage: 8.38M 8.38M - max_storage_buffer_binding_size: 2.14G 2.14G - max_storage_buffers_per_shader_stage: 8.38M 8.38M - max_storage_textures_per_shader_stage: 8.38M 8.38M - max_texture_array_layers: 2.04K 2.04K - max_texture_dimension1d: 16.3K 16.3K - max_texture_dimension2d: 16.3K 16.3K - max_texture_dimension3d: 2.04K 2.04K - max_uniform_buffer_binding_size: 2.14G 2.14G - max_uniform_buffers_per_shader_stage: 8.38M 8.38M - max_vertex_attributes: 32 32 - max_vertex_buffer_array_stride: 2.04K 2.04K - max_vertex_buffers: 16 16 - min_storage_buffer_offset_alignment: 32 32 - min_uniform_buffer_offset_alignment: 32 32 + max-bind-groups: 8 8 + max-bind-groups-plus-vertex-buffers: 0 0 + max-bindings-per-bind-group: 1.00K 1.00K + max-buffer-size: 2.14G 2.14G + max-color-attachment-bytes-per-sample: 0 0 + max-color-attachments: 0 0 + max-compute-invocations-per-workgroup: 1.02K 1.02K + max-compute-workgroup-size-x: 1.02K 1.02K + max-compute-workgroup-size-y: 1.02K 1.02K + max-compute-workgroup-size-z: 1.02K 1.02K + max-compute-workgroup-storage-size: 32.7K 32.7K + max-compute-workgroups-per-dimension: 65.5K 65.5K + max-dynamic-storage-buffers-per-pipeline-layout: 8 8 + max-dynamic-uniform-buffers-per-pipeline-layout: 16 16 + max-inter-stage-shader-components: 128 128 + max-inter-stage-shader-variables: 0 0 + max-sampled-textures-per-shader-stage: 8.38M 8.38M + max-samplers-per-shader-stage: 8.38M 8.38M + max-storage-buffer-binding-size: 2.14G 2.14G + max-storage-buffers-per-shader-stage: 8.38M 8.38M + max-storage-textures-per-shader-stage: 8.38M 8.38M + max-texture-array-layers: 2.04K 2.04K + max-texture-dimension-1d: 16.3K 16.3K + max-texture-dimension-2d: 16.3K 16.3K + max-texture-dimension-3d: 2.04K 2.04K + max-uniform-buffer-binding-size: 2.14G 2.14G + max-uniform-buffers-per-shader-stage: 8.38M 8.38M + max-vertex-attributes: 32 32 + max-vertex-buffer-array-stride: 2.04K 2.04K + max-vertex-buffers: 16 16 + min-storage-buffer-offset-alignment: 32 32 + min-uniform-buffer-offset-alignment: 32 32 ██ pygfx_caches: diff --git a/examples/gridplot/gridplot.py b/examples/gridplot/gridplot.py index a77cb7872..5c38d6d43 100644 --- a/examples/gridplot/gridplot.py +++ b/examples/gridplot/gridplot.py @@ -30,4 +30,4 @@ # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/gridplot/gridplot_non_square.py b/examples/gridplot/gridplot_non_square.py index a7874319e..0277bcccd 100644 --- a/examples/gridplot/gridplot_non_square.py +++ b/examples/gridplot/gridplot_non_square.py @@ -28,4 +28,4 @@ # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/gridplot/multigraphic_gridplot.py b/examples/gridplot/multigraphic_gridplot.py index eec0d06fa..1bed60b31 100644 --- a/examples/gridplot/multigraphic_gridplot.py +++ b/examples/gridplot/multigraphic_gridplot.py @@ -114,5 +114,5 @@ def make_circle(center, radius: float, n_points: int = 75) -> np.ndarray: # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/guis/image_widget_imgui.py b/examples/guis/image_widget_imgui.py index 38a5c72e1..13d41af20 100644 --- a/examples/guis/image_widget_imgui.py +++ b/examples/guis/image_widget_imgui.py @@ -79,4 +79,4 @@ def process_image(self): # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/guis/imgui_basic.py b/examples/guis/imgui_basic.py index 456375950..eac39121c 100644 --- a/examples/guis/imgui_basic.py +++ b/examples/guis/imgui_basic.py @@ -120,4 +120,4 @@ def _set_data(self): # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/heatmap/heatmap.py b/examples/heatmap/heatmap.py index 11d5559c4..39d76ae4e 100644 --- a/examples/heatmap/heatmap.py +++ b/examples/heatmap/heatmap.py @@ -30,4 +30,4 @@ # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/image/image_cmap.py b/examples/image/image_cmap.py index 4aad934b2..99a3c1969 100644 --- a/examples/image/image_cmap.py +++ b/examples/image/image_cmap.py @@ -26,4 +26,4 @@ # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/image/image_rgb.py b/examples/image/image_rgb.py index e89f3d192..5af8cee0d 100644 --- a/examples/image/image_rgb.py +++ b/examples/image/image_rgb.py @@ -25,4 +25,4 @@ # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/image/image_rgbvminvmax.py b/examples/image/image_rgbvminvmax.py index 2263f1307..08c01a36e 100644 --- a/examples/image/image_rgbvminvmax.py +++ b/examples/image/image_rgbvminvmax.py @@ -27,4 +27,4 @@ # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/image/image_simple.py b/examples/image/image_simple.py index cec8e3313..31803f2f8 100644 --- a/examples/image/image_simple.py +++ b/examples/image/image_simple.py @@ -25,4 +25,4 @@ # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/image/image_small.py b/examples/image/image_small.py index 937411ab1..eebc49797 100644 --- a/examples/image/image_small.py +++ b/examples/image/image_small.py @@ -27,4 +27,4 @@ # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/image/image_vminvmax.py b/examples/image/image_vminvmax.py index 0503c5ff2..6cf13834d 100644 --- a/examples/image/image_vminvmax.py +++ b/examples/image/image_vminvmax.py @@ -27,4 +27,4 @@ # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/image_widget/image_widget.py b/examples/image_widget/image_widget.py index 78b54b8ef..b500720d5 100644 --- a/examples/image_widget/image_widget.py +++ b/examples/image_widget/image_widget.py @@ -10,7 +10,7 @@ # test_example = true # sphinx_gallery_pygfx_docs = 'screenshot' - +import glfw import fastplotlib as fpl import imageio.v3 as iio # not a fastplotlib dependency, only used for examples @@ -31,4 +31,4 @@ # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/image_widget/image_widget_grid.py b/examples/image_widget/image_widget_grid.py index 48b31caa7..f52f38bc5 100644 --- a/examples/image_widget/image_widget_grid.py +++ b/examples/image_widget/image_widget_grid.py @@ -38,4 +38,4 @@ # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/image_widget/image_widget_single_video.py b/examples/image_widget/image_widget_single_video.py index 30073a935..3a0e94fca 100644 --- a/examples/image_widget/image_widget_single_video.py +++ b/examples/image_widget/image_widget_single_video.py @@ -44,4 +44,4 @@ # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/image_widget/image_widget_videos.py b/examples/image_widget/image_widget_videos.py index 6e5c35c50..1e367f0ad 100644 --- a/examples/image_widget/image_widget_videos.py +++ b/examples/image_widget/image_widget_videos.py @@ -40,4 +40,4 @@ # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/line/line.py b/examples/line/line.py index eb1afbe60..c460c84ac 100644 --- a/examples/line/line.py +++ b/examples/line/line.py @@ -44,4 +44,4 @@ # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/line/line_cmap.py b/examples/line/line_cmap.py index 81895e17b..b2fb39779 100644 --- a/examples/line/line_cmap.py +++ b/examples/line/line_cmap.py @@ -46,4 +46,4 @@ # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/line/line_cmap_more.py b/examples/line/line_cmap_more.py index c05f36797..37fd68cdb 100644 --- a/examples/line/line_cmap_more.py +++ b/examples/line/line_cmap_more.py @@ -53,4 +53,4 @@ # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/line/line_colorslice.py b/examples/line/line_colorslice.py index 0b71efc3d..788aa342d 100644 --- a/examples/line/line_colorslice.py +++ b/examples/line/line_colorslice.py @@ -87,4 +87,4 @@ # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/line/line_dataslice.py b/examples/line/line_dataslice.py index 83a9ae34a..92f33a109 100644 --- a/examples/line/line_dataslice.py +++ b/examples/line/line_dataslice.py @@ -51,4 +51,4 @@ # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/line_collection/line_collection.py b/examples/line_collection/line_collection.py index 67f3834d3..75b56e61e 100644 --- a/examples/line_collection/line_collection.py +++ b/examples/line_collection/line_collection.py @@ -43,4 +43,4 @@ def make_circle(center, radius: float, n_points: int = 75) -> np.ndarray: # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/line_collection/line_collection_cmap_values.py b/examples/line_collection/line_collection_cmap_values.py index e0b6f2507..c577609f9 100644 --- a/examples/line_collection/line_collection_cmap_values.py +++ b/examples/line_collection/line_collection_cmap_values.py @@ -50,4 +50,4 @@ def make_circle(center, radius: float, n_points: int = 75) -> np.ndarray: # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/line_collection/line_collection_cmap_values_qualitative.py b/examples/line_collection/line_collection_cmap_values_qualitative.py index bbb463c2f..7b1c0a419 100644 --- a/examples/line_collection/line_collection_cmap_values_qualitative.py +++ b/examples/line_collection/line_collection_cmap_values_qualitative.py @@ -60,4 +60,4 @@ def make_circle(center, radius: float, n_points: int = 75) -> np.ndarray: # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/line_collection/line_collection_colors.py b/examples/line_collection/line_collection_colors.py index 23ca25b25..1d9eff45d 100644 --- a/examples/line_collection/line_collection_colors.py +++ b/examples/line_collection/line_collection_colors.py @@ -47,4 +47,4 @@ def make_circle(center, radius: float, n_points: int = 75) -> np.ndarray: # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/line_collection/line_collection_slicing.py b/examples/line_collection/line_collection_slicing.py index fbeab53c2..f829a53c6 100644 --- a/examples/line_collection/line_collection_slicing.py +++ b/examples/line_collection/line_collection_slicing.py @@ -75,4 +75,4 @@ if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/line_collection/line_stack.py b/examples/line_collection/line_stack.py index 9ca2a937e..95b681b76 100644 --- a/examples/line_collection/line_stack.py +++ b/examples/line_collection/line_stack.py @@ -35,4 +35,4 @@ # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/line_collection/line_stack_3d.py b/examples/line_collection/line_stack_3d.py index 46a24ef75..35fe48ca9 100644 --- a/examples/line_collection/line_stack_3d.py +++ b/examples/line_collection/line_stack_3d.py @@ -105,4 +105,4 @@ def animate_colors(subplot): # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/machine_learning/covariance.py b/examples/machine_learning/covariance.py index 111a7bd22..84c5bf531 100644 --- a/examples/machine_learning/covariance.py +++ b/examples/machine_learning/covariance.py @@ -91,4 +91,4 @@ def animate(): # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/misc-dev/large_img.py b/examples/misc-dev/large_img.py deleted file mode 100644 index 021bbd6f6..000000000 --- a/examples/misc-dev/large_img.py +++ /dev/null @@ -1,35 +0,0 @@ -from fastplotlib import Plot, run -import numpy as np - -temporal = np.load("./array_10-000x108-000.npy") - -from PIL import Image - -Image.MAX_IMAGE_PIXELS = None - -img = Image.open("/home/kushal/Downloads/gigahour_stitched_0042_bbs.png") - -a = np.array(img) - -r = np.random.randint(0, 50, a.size, dtype=np.uint8).reshape(a.shape) - -plot = Plot(renderer_kwargs={"show_fps": True}) -plot.add_heatmap(r) -# plot.camera.scale.y = 0.2 -plot.show() - -r = np.random.randint(0, 50, a.size, dtype=np.uint8).reshape(a.shape) -r2 = np.random.randint(0, 50, a.size, dtype=np.uint8).reshape(a.shape) -r3 = np.random.randint(0, 50, a.size, dtype=np.uint8).reshape(a.shape) - -rs = [r, r2, r3] -i = 0 - -def update_frame(p): - global i - p.graphics[0].data[:] = rs[i % 3] - i +=1 - -plot.add_animations(update_frame) - -run() diff --git a/examples/misc/cycle_animation.py b/examples/misc/cycle_animation.py index f866434a1..e369b957c 100644 --- a/examples/misc/cycle_animation.py +++ b/examples/misc/cycle_animation.py @@ -58,4 +58,4 @@ def cycle_colors(subplot): # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/misc/em_wave_animation.py b/examples/misc/em_wave_animation.py index bfccedf5f..06c60ccaf 100644 --- a/examples/misc/em_wave_animation.py +++ b/examples/misc/em_wave_animation.py @@ -112,4 +112,4 @@ def tick(subplot): # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/misc/image_animation.py b/examples/misc/image_animation.py index 8c323f464..bc5f83957 100644 --- a/examples/misc/image_animation.py +++ b/examples/misc/image_animation.py @@ -34,4 +34,4 @@ def update_data(figure_instance): # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/misc/line3d_animation.py b/examples/misc/line3d_animation.py index 4f2f089e6..b26bfd3a0 100644 --- a/examples/misc/line3d_animation.py +++ b/examples/misc/line3d_animation.py @@ -58,4 +58,4 @@ def move_marker(): # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/misc/line_animation.py b/examples/misc/line_animation.py index a602a6e7d..07c6a7d94 100644 --- a/examples/misc/line_animation.py +++ b/examples/misc/line_animation.py @@ -47,4 +47,4 @@ def update_line(subplot): # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/misc/lorenz_animation.py b/examples/misc/lorenz_animation.py index af577d5a2..4d4c5129f 100644 --- a/examples/misc/lorenz_animation.py +++ b/examples/misc/lorenz_animation.py @@ -90,4 +90,4 @@ def animate(subplot): # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/misc/multiplot_animation.py b/examples/misc/multiplot_animation.py index b0a942d0a..18512add1 100644 --- a/examples/misc/multiplot_animation.py +++ b/examples/misc/multiplot_animation.py @@ -45,4 +45,4 @@ def update_data(f): # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/misc/scatter_animation.py b/examples/misc/scatter_animation.py index de57292a5..d85a33e6a 100644 --- a/examples/misc/scatter_animation.py +++ b/examples/misc/scatter_animation.py @@ -55,4 +55,4 @@ def update_points(subplot): # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/misc/scatter_sizes_animation.py b/examples/misc/scatter_sizes_animation.py index aeb0466b0..45782564d 100644 --- a/examples/misc/scatter_sizes_animation.py +++ b/examples/misc/scatter_sizes_animation.py @@ -44,4 +44,4 @@ def update_sizes(subplot): # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/misc/simple_event.py b/examples/misc/simple_event.py index 574b8ea5e..e382f04b5 100644 --- a/examples/misc/simple_event.py +++ b/examples/misc/simple_event.py @@ -51,4 +51,4 @@ def click_event(event_data): # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/qt/embed.py b/examples/qt/embed.py index 535c08e5f..ba20c5084 100644 --- a/examples/qt/embed.py +++ b/examples/qt/embed.py @@ -60,7 +60,7 @@ def update_frame(ix): main_window.show() # execute Qt app -fpl.run() +fpl.loop.run() # You can also use Qt interactively/in a non-blocking manner in notebooks and ipython -# by using %gui qt and NOT calling `fpl.run()`, see the user guide for more details +# by using %gui qt and NOT calling `fpl.loop.run()`, see the user guide for more details diff --git a/examples/qt/imagewidget.py b/examples/qt/imagewidget.py index d18ae9b15..8a5b8937c 100644 --- a/examples/qt/imagewidget.py +++ b/examples/qt/imagewidget.py @@ -36,7 +36,7 @@ iw_rgb = fpl.ImageWidget(rgb_video, rgb=[True], figure_kwargs={"size": (800, 500)}) iw_rgb.show() -fpl.run() +fpl.loop.run() # You can also use Qt interactively/in a non-blocking manner in notebooks and ipython -# by using %gui qt and NOT calling `fpl.run()`, see the user guide for more details +# by using %gui qt and NOT calling `fpl.loop.run()`, see the user guide for more details diff --git a/examples/qt/minimal.py b/examples/qt/minimal.py index 01b5de352..0424df403 100644 --- a/examples/qt/minimal.py +++ b/examples/qt/minimal.py @@ -30,7 +30,7 @@ # execute Qt app # if this is part of a larger Qt QApplication, you can also call app.exec() where app is the QApplication instance -fpl.run() +fpl.loop.run() # You can also use Qt interactively/in a non-blocking manner in notebooks and ipython # by using %gui qt and NOT calling `fpl.run()`, see the user guide for more details diff --git a/examples/scatter/scatter.py b/examples/scatter/scatter.py index fe1f6ce6d..afb0a0b81 100644 --- a/examples/scatter/scatter.py +++ b/examples/scatter/scatter.py @@ -36,7 +36,7 @@ colors = ["yellow"] * n_points + ["cyan"] * n_points + ["magenta"] * n_points # use an alpha value since this will be a lot of points -figure[0,0].add_scatter(data=cloud, sizes=3, colors=colors, alpha=0.6) +figure[0, 0].add_scatter(data=cloud, sizes=3, colors=colors, alpha=0.6) figure.show() @@ -45,4 +45,4 @@ # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/scatter/scatter_cmap.py b/examples/scatter/scatter_cmap.py index 42ff572d8..8810e3d7b 100644 --- a/examples/scatter/scatter_cmap.py +++ b/examples/scatter/scatter_cmap.py @@ -36,15 +36,15 @@ colors = ["yellow"] * n_points + ["cyan"] * n_points + ["magenta"] * n_points # use an alpha value since this will be a lot of points -figure[0,0].add_scatter(data=cloud, sizes=3, colors=colors, alpha=0.6) +figure[0, 0].add_scatter(data=cloud, sizes=3, colors=colors, alpha=0.6) figure.show() -figure[0,0].graphics[0].cmap = "viridis" +figure[0, 0].graphics[0].cmap = "viridis" # NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/scatter/scatter_cmap_iris.py b/examples/scatter/scatter_cmap_iris.py index b25369c60..139554dae 100644 --- a/examples/scatter/scatter_cmap_iris.py +++ b/examples/scatter/scatter_cmap_iris.py @@ -35,4 +35,4 @@ if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/scatter/scatter_colorslice.py b/examples/scatter/scatter_colorslice.py index 839df3826..cf7472361 100644 --- a/examples/scatter/scatter_colorslice.py +++ b/examples/scatter/scatter_colorslice.py @@ -36,7 +36,7 @@ colors = ["yellow"] * n_points + ["cyan"] * n_points + ["magenta"] * n_points # use an alpha value since this will be a lot of points -figure[0,0].add_scatter(data=cloud, sizes=3, colors=colors, alpha=0.6) +figure[0, 0].add_scatter(data=cloud, sizes=3, colors=colors, alpha=0.6) figure.show() @@ -50,4 +50,4 @@ # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/scatter/scatter_colorslice_iris.py b/examples/scatter/scatter_colorslice_iris.py index 92df1f66c..725374ef7 100644 --- a/examples/scatter/scatter_colorslice_iris.py +++ b/examples/scatter/scatter_colorslice_iris.py @@ -35,4 +35,4 @@ if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/scatter/scatter_dataslice.py b/examples/scatter/scatter_dataslice.py index 715959e06..840553237 100644 --- a/examples/scatter/scatter_dataslice.py +++ b/examples/scatter/scatter_dataslice.py @@ -36,4 +36,4 @@ # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/scatter/scatter_dataslice_iris.py b/examples/scatter/scatter_dataslice_iris.py index 04ac4b85f..cc688eeb4 100644 --- a/examples/scatter/scatter_dataslice_iris.py +++ b/examples/scatter/scatter_dataslice_iris.py @@ -34,4 +34,4 @@ if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/scatter/scatter_iris.py b/examples/scatter/scatter_iris.py index 6937ffe4b..e000d5a0a 100644 --- a/examples/scatter/scatter_iris.py +++ b/examples/scatter/scatter_iris.py @@ -32,4 +32,4 @@ # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/scatter/scatter_size.py b/examples/scatter/scatter_size.py index 0cecb6dad..73be31f62 100644 --- a/examples/scatter/scatter_size.py +++ b/examples/scatter/scatter_size.py @@ -44,4 +44,4 @@ # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/selection_tools/linear_region_line_collection.py b/examples/selection_tools/linear_region_line_collection.py index 493062026..76739d784 100644 --- a/examples/selection_tools/linear_region_line_collection.py +++ b/examples/selection_tools/linear_region_line_collection.py @@ -78,4 +78,4 @@ def update_zoomed_subplots(ev): # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/selection_tools/linear_region_selector.py b/examples/selection_tools/linear_region_selector.py index 026da2522..6fa17db38 100644 --- a/examples/selection_tools/linear_region_selector.py +++ b/examples/selection_tools/linear_region_selector.py @@ -111,4 +111,4 @@ def set_zoom_y(ev): # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/selection_tools/linear_region_selectors_match_offsets.py b/examples/selection_tools/linear_region_selectors_match_offsets.py index c5d58c9be..b48e30f28 100644 --- a/examples/selection_tools/linear_region_selectors_match_offsets.py +++ b/examples/selection_tools/linear_region_selectors_match_offsets.py @@ -106,4 +106,4 @@ def set_zoom_y(ev): # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/selection_tools/linear_selector.py b/examples/selection_tools/linear_selector.py index d724ccf5d..1edf6345c 100644 --- a/examples/selection_tools/linear_selector.py +++ b/examples/selection_tools/linear_selector.py @@ -119,4 +119,4 @@ def line_stack_selector_changed(ev): # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/selection_tools/linear_selector_image.py b/examples/selection_tools/linear_selector_image.py index 540c7645a..00484aba7 100644 --- a/examples/selection_tools/linear_selector_image.py +++ b/examples/selection_tools/linear_selector_image.py @@ -70,4 +70,4 @@ def image_col_selector_changed(ev): # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/selection_tools/rectangle_selector.py b/examples/selection_tools/rectangle_selector.py index 48e8647ac..850937f7a 100644 --- a/examples/selection_tools/rectangle_selector.py +++ b/examples/selection_tools/rectangle_selector.py @@ -63,4 +63,4 @@ def color_indices(ev): # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/selection_tools/rectangle_selector_zoom.py b/examples/selection_tools/rectangle_selector_zoom.py index 45633c42b..33ba2ae2a 100644 --- a/examples/selection_tools/rectangle_selector_zoom.py +++ b/examples/selection_tools/rectangle_selector_zoom.py @@ -50,4 +50,4 @@ def update_data(ev): # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": print(__doc__) - fpl.run() + fpl.loop.run() diff --git a/examples/tests/test_examples.py b/examples/tests/test_examples.py index 55a3fa84d..caa8e9dca 100644 --- a/examples/tests/test_examples.py +++ b/examples/tests/test_examples.py @@ -12,7 +12,7 @@ import pygfx MAX_TEXTURE_SIZE = 2048 -pygfx.renderers.wgpu.set_wgpu_limits(**{"max-texture-dimension2d": MAX_TEXTURE_SIZE}) +pygfx.renderers.wgpu.set_wgpu_limits(**{"max-texture-dimension-2d": MAX_TEXTURE_SIZE}) from .testutils import ( ROOT, diff --git a/fastplotlib/__init__.py b/fastplotlib/__init__.py index 3cee71b28..2f3c35a1a 100644 --- a/fastplotlib/__init__.py +++ b/fastplotlib/__init__.py @@ -1,7 +1,7 @@ from pathlib import Path # this must be the first import for auto-canvas detection -from .utils import run # noqa +from .utils import loop # noqa from .graphics import * from .graphics.selectors import * from .graphics.utils import pause_events @@ -13,10 +13,10 @@ if IMGUI: # default to imgui figure if imgui_bundle is installed from .layouts import ImguiFigure as Figure + from .widgets import ImageWidget else: from .layouts import Figure -from .widgets import ImageWidget from .utils import config, enumerate_adapters, select_adapter, print_wgpu_report diff --git a/fastplotlib/graphics/_features/_image.py b/fastplotlib/graphics/_features/_image.py index 53bcc5bc4..b67bf1cd4 100644 --- a/fastplotlib/graphics/_features/_image.py +++ b/fastplotlib/graphics/_features/_image.py @@ -21,7 +21,7 @@ def __init__(self, data, isolated_buffer: bool = True): data = self._fix_data(data) shared = pygfx.renderers.wgpu.get_shared() - self._texture_limit_2d = shared.device.limits["max-texture-dimension2d"] + self._texture_limit_2d = shared.device.limits["max-texture-dimension-2d"] if isolated_buffer: # useful if data is read-only, example: memmaps @@ -202,8 +202,8 @@ def value(self) -> str: def set_value(self, graphic, value: str): new_colors = make_colors(256, value) - graphic._material.map.data[:] = new_colors - graphic._material.map.update_range((0, 0, 0), size=(256, 1, 1)) + graphic._material.map.texture.data[:] = new_colors + graphic._material.map.texture.update_range((0, 0, 0), size=(256, 1, 1)) self._value = value event = FeatureEvent(type="cmap", info={"value": value}) @@ -258,7 +258,8 @@ def set_value(self, graphic, value: str): self._validate(value) # common material for all image tiles - graphic._material.map_interpolation = value + graphic._material.map.min_filter = value + graphic._material.map.mag_filter = value self._value = value event = FeatureEvent(type="cmap_interpolation", info={"value": value}) diff --git a/fastplotlib/graphics/image.py b/fastplotlib/graphics/image.py index 25a4741e0..8b937023b 100644 --- a/fastplotlib/graphics/image.py +++ b/fastplotlib/graphics/image.py @@ -132,27 +132,28 @@ def __init__( self._vmin = ImageVmin(vmin) self._vmax = ImageVmax(vmax) - # set cmap to None for RGB images + self._interpolation = ImageInterpolation(interpolation) + + # set map to None for RGB images if self._data.value.ndim > 2: self._cmap = None + _map = None else: + # use TextureMap for grayscale images self._cmap = ImageCmap(cmap) + self._cmap_interpolation = ImageCmapInterpolation(cmap_interpolation) - self._interpolation = ImageInterpolation(interpolation) - self._cmap_interpolation = ImageCmapInterpolation(cmap_interpolation) - - # use cmap if not RGB - if self._data.value.ndim == 2: - _map = self._cmap.texture - else: - _map = None + _map = pygfx.TextureMap( + self._cmap.texture, + filter=self._cmap_interpolation.value, + wrap="clamp-to-edge", + ) # one common material is used for every Texture chunk self._material = pygfx.ImageBasicMaterial( clim=(vmin, vmax), map=_map, interpolation=self._interpolation.value, - map_interpolation=self._cmap_interpolation.value, pick_write=True, ) diff --git a/fastplotlib/layouts/_figure.py b/fastplotlib/layouts/_figure.py index bba5d4aab..775d72dbf 100644 --- a/fastplotlib/layouts/_figure.py +++ b/fastplotlib/layouts/_figure.py @@ -11,7 +11,7 @@ import pygfx -from wgpu.gui import WgpuCanvasBase +from rendercanvas import BaseRenderCanvas from ._video_writer import VideoWriterAV from ._utils import make_canvas_and_renderer, create_controller, create_camera @@ -41,7 +41,7 @@ def __init__( | Iterable[Iterable[str]] ) = None, controllers: pygfx.Controller | Iterable[Iterable[pygfx.Controller]] = None, - canvas: str | WgpuCanvasBase | pygfx.Texture = None, + canvas: str | BaseRenderCanvas | pygfx.Texture = None, renderer: pygfx.WgpuRenderer = None, size: tuple[int, int] = (500, 300), names: list | np.ndarray = None, @@ -84,8 +84,8 @@ def __init__( plot/subplot. Other controller kwargs, i.e. ``controller_types`` and ``controller_ids`` are ignored if ``controllers`` are provided. - canvas: WgpuCanvas, optional - Canvas for drawing + canvas: str, BaseRenderCanvas, pygfx.Texture + Canvas to draw the figure onto, usually auto-selected based on running environment. renderer: pygfx.Renderer, optional pygfx renderer instance @@ -334,13 +334,13 @@ def shape(self) -> tuple[int, int]: return self._shape @property - def canvas(self) -> WgpuCanvasBase: - """The canvas associated to this Figure""" + def canvas(self) -> BaseRenderCanvas: + """The canvas this Figure is drawn onto""" return self._canvas @property def renderer(self) -> pygfx.WgpuRenderer: - """The renderer associated to this Figure""" + """The renderer that renders this Figure""" return self._renderer @property @@ -422,7 +422,7 @@ def show( Returns ------- - WgpuCanvasBase + BaseRenderCanvas In Qt or GLFW, the canvas window containing the Figure will be shown. In jupyter, it will display the plot in the output cell or sidecar. """ @@ -452,7 +452,7 @@ def show( subplot.auto_scale(maintain_aspect=maintain_aspect) # parse based on canvas type - if self.canvas.__class__.__name__ == "JupyterWgpuCanvas": + if self.canvas.__class__.__name__ == "JupyterRenderCanvas": if sidecar: from sidecar import Sidecar from IPython.display import display @@ -464,12 +464,12 @@ def show( self._output = self.canvas return self._output - elif self.canvas.__class__.__name__ == "QWgpuCanvas": + elif self.canvas.__class__.__name__ == "QRenderCanvas": self._output = self.canvas self._output.show() return self.canvas - elif self.canvas.__class__.__name__ == "WgpuManualOffscreenCanvas": + elif self.canvas.__class__.__name__ == "OffscreenRenderCanvas": # for test and docs gallery screenshots for subplot in self: subplot.set_viewport_rect() @@ -494,18 +494,6 @@ def close(self): if self._sidecar: self._sidecar.close() - def get_pygfx_render_area(self, *args) -> tuple[int, int, int, int]: - """ - Get rect for the portion of the canvas that the pygfx renderer draws to - - Returns - ------- - tuple[int, int, int, int] - x_pos, y_pos, width, height - - """ - return 0, 0, *self.canvas.get_logical_size() - def _call_animate_functions(self, funcs: list[callable]): for fn in funcs: try: diff --git a/fastplotlib/layouts/_imgui_figure.py b/fastplotlib/layouts/_imgui_figure.py index 3396c3d27..8621f4464 100644 --- a/fastplotlib/layouts/_imgui_figure.py +++ b/fastplotlib/layouts/_imgui_figure.py @@ -7,7 +7,7 @@ from imgui_bundle import imgui, icons_fontawesome_6 as fa from wgpu.utils.imgui import ImguiRenderer -from wgpu.gui import WgpuCanvasBase +from rendercanvas import BaseRenderCanvas import pygfx @@ -38,7 +38,7 @@ def __init__( | Iterable[Iterable[str]] ) = None, controllers: pygfx.Controller | Iterable[Iterable[pygfx.Controller]] = None, - canvas: str | WgpuCanvasBase | pygfx.Texture = None, + canvas: str | BaseRenderCanvas | pygfx.Texture = None, renderer: pygfx.WgpuRenderer = None, size: tuple[int, int] = (500, 300), names: list | np.ndarray = None, @@ -198,7 +198,7 @@ def get_pygfx_render_area(self, *args) -> tuple[int, int, int, int]: else: ypos = 0 - return xpos, ypos, width, height + return xpos, ypos, max(1, width), max(1, height) def _reset_viewports(self): # TODO: think about moving this to Figure later, diff --git a/fastplotlib/layouts/_plot_area.py b/fastplotlib/layouts/_plot_area.py index 76a956876..e096a7f21 100644 --- a/fastplotlib/layouts/_plot_area.py +++ b/fastplotlib/layouts/_plot_area.py @@ -6,7 +6,7 @@ import pygfx from pylinalg import vec_transform, vec_unproject -from wgpu.gui import WgpuCanvasBase +from rendercanvas import BaseRenderCanvas from ._utils import create_controller from ..graphics._base import Graphic @@ -32,9 +32,8 @@ def __init__( camera: pygfx.PerspectiveCamera, controller: pygfx.Controller, scene: pygfx.Scene, - canvas: WgpuCanvasBase, + canvas: BaseRenderCanvas, renderer: pygfx.WgpuRenderer, - extra_renderers: dict = None, name: str = None, ): """ @@ -59,7 +58,7 @@ def __init__( scene: pygfx.Scene represents the root of a scene graph, will be viewed by the given ``camera`` - canvas: WgpuCanvas + canvas: BaseRenderCanvas provides surface on which a scene will be rendered renderer: pygfx.WgpuRenderer @@ -153,7 +152,7 @@ def scene(self) -> pygfx.Scene: return self._scene @property - def canvas(self) -> WgpuCanvasBase: + def canvas(self) -> BaseRenderCanvas: """Canvas associated to the plot area""" return self._canvas diff --git a/fastplotlib/layouts/_subplot.py b/fastplotlib/layouts/_subplot.py index 9c3b174a9..7d52ebab2 100644 --- a/fastplotlib/layouts/_subplot.py +++ b/fastplotlib/layouts/_subplot.py @@ -4,7 +4,7 @@ import pygfx -from wgpu.gui import WgpuCanvasBase +from rendercanvas import BaseRenderCanvas from ..graphics import TextGraphic from ._utils import create_camera, create_controller @@ -25,7 +25,7 @@ def __init__( parent_dims: tuple[int, int], camera: Literal["2d", "3d"] | pygfx.PerspectiveCamera, controller: pygfx.Controller, - canvas: WgpuCanvasBase | pygfx.Texture, + canvas: BaseRenderCanvas | pygfx.Texture, renderer: pygfx.WgpuRenderer = None, name: str = None, ): @@ -56,7 +56,7 @@ def __init__( | if ``str``, must be one of: `"panzoom", "fly", "trackball", or "orbit"`. | also accepts a pygfx.Controller instance - canvas: WgpuCanvas, or a pygfx.Texture + canvas: BaseRenderCanvas, or a pygfx.Texture Provides surface on which a scene will be rendered. renderer: WgpuRenderer @@ -219,7 +219,9 @@ def get_rect(self) -> np.ndarray: # leave space for imgui toolbar height_subplot -= IMGUI_TOOLBAR_HEIGHT - rect = np.array([x_pos, y_pos, width_subplot, height_subplot]) + # clip so that min values are always 1, otherwise JupyterRenderCanvas causes issues because it + # initializes with a width of (0, 0) + rect = np.array([x_pos, y_pos, width_subplot, height_subplot]).clip(1) for dv in self.docks.values(): rect = rect + dv.get_parent_rect_adjust() diff --git a/fastplotlib/layouts/_utils.py b/fastplotlib/layouts/_utils.py index ea44f6950..b42971570 100644 --- a/fastplotlib/layouts/_utils.py +++ b/fastplotlib/layouts/_utils.py @@ -2,43 +2,12 @@ import pygfx from pygfx import WgpuRenderer, Texture, Renderer -from pygfx.renderers.wgpu.engine.renderer import ( - EVENT_TYPE_MAP, - PointerEvent, - WheelEvent, -) -from wgpu.gui import WgpuCanvasBase - -from ..utils import gui - - -# temporary until https://github.com/pygfx/pygfx/issues/495 -class WgpuRendererWithEventFilters(WgpuRenderer): - def __init__(self, target, *args, **kwargs): - super().__init__(target, *args, **kwargs) - self._event_filters = {} - - def convert_event(self, event: dict): - event_type = event["event_type"] - - if EVENT_TYPE_MAP[event_type] in [PointerEvent, WheelEvent]: - for filt in self.event_filters.values(): - if ( - filt[0, 0] < event["x"] < filt[1, 0] - and filt[0, 1] < event["y"] < filt[1, 1] - ): - return - - super().convert_event(event) - - @property - def event_filters(self) -> dict: - return self._event_filters +from ..utils.gui import BaseRenderCanvas, RenderCanvas def make_canvas_and_renderer( - canvas: str | WgpuCanvasBase | Texture | None, + canvas: str | BaseRenderCanvas | Texture | None, renderer: Renderer | None, canvas_kwargs: dict, ): @@ -48,18 +17,20 @@ def make_canvas_and_renderer( """ if canvas is None: - canvas = gui.WgpuCanvas(max_fps=60, **canvas_kwargs) + canvas = RenderCanvas(max_fps=60, **canvas_kwargs) elif isinstance(canvas, str): - m = importlib.import_module("wgpu.gui." + canvas) - canvas = m.WgpuCanvas(max_fps=60, **canvas_kwargs) - elif not isinstance(canvas, (WgpuCanvasBase, Texture)): + import rendercanvas + + m = importlib.import_module("rendercanvas." + canvas) + canvas = m.RenderCanvas(max_fps=60, **canvas_kwargs) + elif not isinstance(canvas, (BaseRenderCanvas, Texture)): raise TypeError( - f"canvas option must either be a valid WgpuCanvas implementation, a pygfx Texture" - f" or a str with the wgpu gui backend name." + f"canvas option must either be a valid BaseRenderCanvas implementation, a pygfx Texture" + f" or a str with the gui backend name, valid str are: 'qt', 'glfw', 'jupyter', 'wx', and 'offscreen'" ) if renderer is None: - renderer = WgpuRendererWithEventFilters(canvas) + renderer = WgpuRenderer(canvas) elif not isinstance(renderer, Renderer): raise TypeError( f"renderer option must be a pygfx.Renderer instance such as pygfx.WgpuRenderer" diff --git a/fastplotlib/ui/_base.py b/fastplotlib/ui/_base.py index 4ca9fbeca..0abc81e88 100644 --- a/fastplotlib/ui/_base.py +++ b/fastplotlib/ui/_base.py @@ -252,31 +252,6 @@ def __init__(self, figure: Figure, *args, **kwargs): self.is_open = False - def set_event_filter(self, name: str): - """Filter out events under the popup from being handled by pygfx renderer""" - # get popup window position & size - x1, y1 = imgui.get_window_pos() - width, height = imgui.get_window_size() - x2, y2 = x1 + width, y1 + height - - # add or modify event filter - if name not in self._figure.renderer.event_filters.keys(): - self._figure.renderer.event_filters[name] = np.array( - [[x1 - 1, y1 - 1], [x2 + 4, y2 + 4]] - ) - else: - self._figure.renderer.event_filters[name][:] = [x1 - 1, y1 - 1], [ - x2 + 4, - y2 + 4, - ] - - self._event_filter_names.add(name) - - def clear_event_filters(self): - """clear event filters when the popup is not shown""" - for name in self._event_filter_names: - self._figure.renderer.event_filters[name][:] = [-1, -1], [-1, -1] - def open(self, pos: tuple[int, int], *args, **kwargs): """implement in subclass""" raise NotImplementedError diff --git a/fastplotlib/ui/right_click_menus/_colormap_picker.py b/fastplotlib/ui/right_click_menus/_colormap_picker.py index 5a14705c7..03176f673 100644 --- a/fastplotlib/ui/right_click_menus/_colormap_picker.py +++ b/fastplotlib/ui/right_click_menus/_colormap_picker.py @@ -120,8 +120,6 @@ def close(self): self.is_open = False - self.clear_event_filters() - def _add_cmap_menu_item(self, cmap_name: str): texture_id = self._texture_ids[cmap_name] imgui.image( @@ -149,9 +147,6 @@ def update(self): if imgui.begin_popup("cmap-picker"): self.is_open = True - # event filter so click events in the menu aren't propagated down to pygfx - self.set_event_filter("cmap-picker-filter") - # make the cmap image height the same as the text height self._texture_height = ( self.imgui_renderer.backend.io.font_global_scale diff --git a/fastplotlib/ui/right_click_menus/_standard_menu.py b/fastplotlib/ui/right_click_menus/_standard_menu.py index 71e8df632..cb1763d6d 100644 --- a/fastplotlib/ui/right_click_menus/_standard_menu.py +++ b/fastplotlib/ui/right_click_menus/_standard_menu.py @@ -42,7 +42,6 @@ def get_subplot(self) -> PlotArea | bool: def cleanup(self): """called when the popup disappears""" - self.clear_event_filters() self.is_open = False def update(self): @@ -65,9 +64,6 @@ def update(self): self.cleanup() if imgui.begin_popup(f"right-click-menu"): - # set event filter so event in the popup region are not handled by pygfx.WgpuRenderer - self.set_event_filter("right-click-menu") - if not self.get_subplot(): # for some reason it will still trigger at certain locations # despite open_popup() only being called when an actual @@ -143,7 +139,6 @@ def update(self): # controller options if imgui.begin_menu("Controller"): - self.set_event_filter("controller-menu") _, enabled = imgui.menu_item( "Enabled", None, self.get_subplot().controller.enabled ) diff --git a/fastplotlib/utils/__init__.py b/fastplotlib/utils/__init__.py index 276397e88..dce4d96f9 100644 --- a/fastplotlib/utils/__init__.py +++ b/fastplotlib/utils/__init__.py @@ -1,7 +1,7 @@ from dataclasses import dataclass # this MUST be imported as early as possible in fpl.__init__ before any other wgpu stuff -from .gui import run +from .gui import loop from .functions import * from .gpu import enumerate_adapters, select_adapter, print_wgpu_report from ._plot_helpers import * diff --git a/fastplotlib/utils/functions.py b/fastplotlib/utils/functions.py index d93f09da3..02dcd0572 100644 --- a/fastplotlib/utils/functions.py +++ b/fastplotlib/utils/functions.py @@ -9,7 +9,95 @@ cmap_catalog = cmap_lib.Catalog() -COLORMAPS = cmap_catalog.short_keys() +COLORMAPS = sorted( + [ + "viridis", + "plasma", + "inferno", + "magma", + "cividis", + "Greys", + "Purples", + "Blues", + "Greens", + "Oranges", + "Reds", + "tol:YlOrBr", + "YlOrRd", + "OrRd", + "PuRd", + "RdPu", + "BuPu", + "GnBu", + "PuBu", + "YlGnBu", + "PuBuGn", + "BuGn", + "YlGn", + "binary", + "gist_yarg", + "gist_gray", + "gray", + "bone", + "pink", + "spring", + "summer", + "autumn", + "winter", + "cool", + "Wistia", + "hot", + "afmhot", + "gist_heat", + "matlab:copper", + "PiYG", + "tol:PRGn", + "BrBG", + "PuOr", + "RdGy", + "vispy:RdBu", + "RdYlBu", + "RdYlGn", + "Spectral", + "coolwarm", + "bwr", + "seismic", + "berlin", + "vanimo", + "twilight", + "twilight_shifted", + "hsv", + "Pastel1", + "Pastel2", + "Paired", + "Accent", + "Dark2", + "Set1", + "Set2", + "Set3", + "tab10", + "tab20", + "tab20b", + "tab20c", + "flag", + "prism", + "gnuplot:ocean", + "gist_earth", + "terrain", + "gist_stern", + "gnuplot", + "gnuplot2", + "CMRmap", + "cubehelix", + "brg", + "gist_rainbow", + "yorick:rainbow", + "jet", + "turbo", + "nipy_spectral", + "gist_ncar", + ] +) SEQUENTIAL_CMAPS = list() QUALITATIVE_CMAPS = list() diff --git a/fastplotlib/utils/gpu.py b/fastplotlib/utils/gpu.py index 72d303d23..912cf0935 100644 --- a/fastplotlib/utils/gpu.py +++ b/fastplotlib/utils/gpu.py @@ -4,10 +4,10 @@ def enumerate_adapters() -> list[wgpu.GPUAdapter]: - return wgpu.gpu.enumerate_adapters() + return wgpu.gpu.enumerate_adapters_sync() -enumerate_adapters.__doc__ = wgpu.gpu.enumerate_adapters.__doc__ +enumerate_adapters.__doc__ = wgpu.gpu.enumerate_adapters_async.__doc__ def select_adapter(adapter: wgpu.GPUAdapter): diff --git a/fastplotlib/utils/gui.py b/fastplotlib/utils/gui.py index c9b2ad011..a8a79920f 100644 --- a/fastplotlib/utils/gui.py +++ b/fastplotlib/utils/gui.py @@ -33,10 +33,11 @@ # Let wgpu do the auto gui selection -from wgpu.gui.auto import WgpuCanvas, run +from rendercanvas import BaseRenderCanvas +from rendercanvas.auto import RenderCanvas, loop # Get the name of the backend ('qt', 'glfw', 'jupyter') -GUI_BACKEND = WgpuCanvas.__module__.split(".")[-1] +GUI_BACKEND = RenderCanvas.__module__.split(".")[-1] IS_JUPYTER = GUI_BACKEND == "jupyter" @@ -123,10 +124,7 @@ def _notebook_print_banner(): _notebook_print_banner() elif GUI_BACKEND == "qt": - from wgpu.gui.qt import get_app, libname - - # create and store ref to qt app - _qt_app = get_app() + from rendercanvas.qt import libname # Import submodules of PySide6/PyQt6/PySid2/PyQt5 # For the way that fpl uses Qt, the supported Qt libs seems compatible enough. diff --git a/fastplotlib/widgets/image_widget/_widget.py b/fastplotlib/widgets/image_widget/_widget.py index 413038c15..c52f026db 100644 --- a/fastplotlib/widgets/image_widget/_widget.py +++ b/fastplotlib/widgets/image_widget/_widget.py @@ -3,7 +3,7 @@ import numpy as np -from wgpu.gui import WgpuCanvasBase +from rendercanvas import BaseRenderCanvas from ... import Figure from ...graphics import ImageGraphic @@ -939,23 +939,25 @@ def set_data( # force graphics to update self.current_index = self.current_index - def show( - self, toolbar: bool = True, sidecar: bool = False, sidecar_kwargs: dict = None - ): + def show(self, **kwargs): """ Show the widget. + Parameters + ---------- + + kwargs: Any + passed to `Figure.show()` + Returns ------- - WgpuCanvasBase - canvas used by the Figure + BaseRenderCanvas + In Qt or GLFW, the canvas window containing the Figure will be shown. + In jupyter, it will display the plot in the output cell or sidecar. """ - return self.figure.show( - sidecar=sidecar, - sidecar_kwargs=sidecar_kwargs, - ) + return self.figure.show(**kwargs) def close(self): """Close Widget""" diff --git a/setup.py b/setup.py index f4b5eb5db..a1c74d5e5 100644 --- a/setup.py +++ b/setup.py @@ -23,7 +23,7 @@ "pandoc", "jupyterlab", "sidecar", - "imageio[pyav]", + "imageio[ffmpeg]", "matplotlib", "scikit-learn", "imgui-bundle", @@ -39,7 +39,7 @@ "nbmake", "black", "scipy", - "imageio[pyav]", + "imageio[ffmpeg]", "jupyterlab", "jupyter-rfb>=0.4.1", "ipywidgets>=8.0.0,<9", @@ -51,7 +51,7 @@ "tests-desktop": [ "pytest<8.0.0", "scipy", - "imageio[pyav]", + "imageio[ffmpeg]", "scikit-learn", "tqdm", "imgui-bundle", diff --git a/tests/conftest.py b/tests/conftest.py index ffc34d464..3f5414a71 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -5,4 +5,4 @@ def pytest_sessionstart(session): - pygfx.renderers.wgpu.set_wgpu_limits(**{"max-texture-dimension2d": MAX_TEXTURE_SIZE}) + pygfx.renderers.wgpu.set_wgpu_limits(**{"max-texture-dimension-2d": MAX_TEXTURE_SIZE}) diff --git a/tests/test_image_graphic.py b/tests/test_image_graphic.py index 541129079..0ea9979a6 100644 --- a/tests/test_image_graphic.py +++ b/tests/test_image_graphic.py @@ -92,14 +92,14 @@ def test_gray(): new_colors = make_colors(256, "viridis") for child in ig.world_object.children: - npt.assert_almost_equal(child.material.map.data, new_colors) + npt.assert_almost_equal(child.material.map.texture.data, new_colors) ig.cmap = "jet" assert ig.cmap == "jet" new_colors = make_colors(256, "jet") for child in ig.world_object.children: - npt.assert_almost_equal(child.material.map.data, new_colors) + npt.assert_almost_equal(child.material.map.texture.data, new_colors) assert ig.interpolation == "nearest" for child in ig.world_object.children: @@ -113,12 +113,15 @@ def test_gray(): assert ig.cmap_interpolation == "linear" for child in ig.world_object.children: - assert child.material.map_interpolation == "linear" + assert child.material.map.min_filter == "linear" + assert child.material.map.mag_filter == "linear" ig.cmap_interpolation = "nearest" assert ig.cmap_interpolation == "nearest" for child in ig.world_object.children: - assert child.material.map_interpolation == "nearest" + assert child.material.map.min_filter == "nearest" + assert child.material.map.mag_filter == "nearest" + check_event(graphic=ig, feature="cmap_interpolation", value="nearest") npt.assert_almost_equal(ig.vmin, GRAY_IMAGE.min()) From f204f554fa99ba9c706af2f969c6988b74ae2bcd Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Mon, 6 Jan 2025 15:48:29 -0500 Subject: [PATCH 130/185] Update year (#688) * Update LICENSE * Update conf.py --- LICENSE | 2 +- docs/source/conf.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index 256b23cdb..33e2266c5 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2024 Kushal Kolar, Caitlin Lewis + Copyright 2025 Kushal Kolar, Caitlin Lewis Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/docs/source/conf.py b/docs/source/conf.py index 9f8e34ab1..66b3c9317 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -29,7 +29,7 @@ # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information project = "fastplotlib" -copyright = "2024, Kushal Kolar, Caitlin Lewis" +copyright = "2025, Kushal Kolar, Caitlin Lewis" author = "Kushal Kolar, Caitlin Lewis" release = fastplotlib.__version__ From 962c9ba371092e10f23bc5812a6c3109b52c837b Mon Sep 17 00:00:00 2001 From: Flynn <75346097+FlynnOConnell@users.noreply.github.com> Date: Thu, 9 Jan 2025 18:06:15 -0500 Subject: [PATCH 131/185] add `size_space` to `pygfx.PointsMaterial` scatter kwargs (#689) * add size_space to pygfx.PointsMaterial kwargs * add CoordSpace graphic feature * store _coord_space graphic feature * add coordspace to features init * make size_space a property of PositionsGraphic * rename CoordSpace->SizeSpace for consistency / avoid confusion with pygfx.CoordSpace * fix size_space feature, FeatureEvent type=size_space?? * add getters/setters to scatter/line graphics * add size_space to _features cls dict * move size_space properties to base PositionGraphics class, add link to CoordSpace in docstring * fix comment for SizeSpace scatter/line * return .value, set_value() * don't coerce str, check if "Line" in class name * add test for size_space * add tests for setting properties * double quotes for the linting overlords * regen api docs * double quotes on "size_space" * black formatting spaces / trailing commas --- .../source/api/graphic_features/SizeSpace.rst | 35 ++++++++++++++++ docs/source/api/graphic_features/index.rst | 1 + docs/source/api/graphics/LineGraphic.rst | 1 + docs/source/api/graphics/ScatterGraphic.rst | 1 + fastplotlib/graphics/_features/__init__.py | 2 + .../graphics/_features/_positions_graphics.py | 21 ++++++++++ fastplotlib/graphics/_positions_base.py | 16 +++++++ fastplotlib/graphics/line.py | 15 +++++-- fastplotlib/graphics/scatter.py | 11 ++++- tests/test_positions_graphics.py | 42 +++++++++++++++++++ 10 files changed, 140 insertions(+), 5 deletions(-) create mode 100644 docs/source/api/graphic_features/SizeSpace.rst diff --git a/docs/source/api/graphic_features/SizeSpace.rst b/docs/source/api/graphic_features/SizeSpace.rst new file mode 100644 index 000000000..0bca1ecc8 --- /dev/null +++ b/docs/source/api/graphic_features/SizeSpace.rst @@ -0,0 +1,35 @@ +.. _api.SizeSpace: + +SizeSpace +********* + +========= +SizeSpace +========= +.. currentmodule:: fastplotlib.graphics._features + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: SizeSpace_api + + SizeSpace + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: SizeSpace_api + + SizeSpace.value + +Methods +~~~~~~~ +.. autosummary:: + :toctree: SizeSpace_api + + SizeSpace.add_event_handler + SizeSpace.block_events + SizeSpace.clear_event_handlers + SizeSpace.remove_event_handler + SizeSpace.set_value + diff --git a/docs/source/api/graphic_features/index.rst b/docs/source/api/graphic_features/index.rst index ea3ce8903..dc88e97d6 100644 --- a/docs/source/api/graphic_features/index.rst +++ b/docs/source/api/graphic_features/index.rst @@ -7,6 +7,7 @@ Graphic Features VertexColors UniformColor UniformSize + SizeSpace Thickness VertexPositions PointsSizesFeature diff --git a/docs/source/api/graphics/LineGraphic.rst b/docs/source/api/graphics/LineGraphic.rst index cb924b4dc..4302ab56c 100644 --- a/docs/source/api/graphics/LineGraphic.rst +++ b/docs/source/api/graphics/LineGraphic.rst @@ -31,6 +31,7 @@ Properties LineGraphic.offset LineGraphic.right_click_menu LineGraphic.rotation + LineGraphic.size_space LineGraphic.supported_events LineGraphic.thickness LineGraphic.visible diff --git a/docs/source/api/graphics/ScatterGraphic.rst b/docs/source/api/graphics/ScatterGraphic.rst index 8f15e827a..83e734c61 100644 --- a/docs/source/api/graphics/ScatterGraphic.rst +++ b/docs/source/api/graphics/ScatterGraphic.rst @@ -31,6 +31,7 @@ Properties ScatterGraphic.offset ScatterGraphic.right_click_menu ScatterGraphic.rotation + ScatterGraphic.size_space ScatterGraphic.sizes ScatterGraphic.supported_events ScatterGraphic.visible diff --git a/fastplotlib/graphics/_features/__init__.py b/fastplotlib/graphics/_features/__init__.py index 4f9013425..a1915bbe9 100644 --- a/fastplotlib/graphics/_features/__init__.py +++ b/fastplotlib/graphics/_features/__init__.py @@ -2,6 +2,7 @@ VertexColors, UniformColor, UniformSize, + SizeSpace, Thickness, VertexPositions, PointsSizesFeature, @@ -42,6 +43,7 @@ "VertexColors", "UniformColor", "UniformSize", + "SizeSpace", "Thickness", "VertexPositions", "PointsSizesFeature", diff --git a/fastplotlib/graphics/_features/_positions_graphics.py b/fastplotlib/graphics/_features/_positions_graphics.py index ee7927a36..c4e153a31 100644 --- a/fastplotlib/graphics/_features/_positions_graphics.py +++ b/fastplotlib/graphics/_features/_positions_graphics.py @@ -182,6 +182,27 @@ def set_value(self, graphic, value: float | int): self._call_event_handlers(event) +# manages the coordinate space for scatter/line +class SizeSpace(GraphicFeature): + def __init__(self, value: str): + self._value = value + super().__init__() + + @property + def value(self) -> str: + return self._value + + def set_value(self, graphic, value: str): + if "Line" in graphic.world_object.material.__class__.__name__: + graphic.world_object.material.thickness_space = value + else: + graphic.world_object.material.size_space = value + self._value = value + + event = FeatureEvent(type="size_space", info={"value": value}) + self._call_event_handlers(event) + + class VertexPositions(BufferManager): """ +----------+----------------------------------------------------------+------------------------------------------------------------------------------------------+ diff --git a/fastplotlib/graphics/_positions_base.py b/fastplotlib/graphics/_positions_base.py index 3727087cc..565a4cd98 100644 --- a/fastplotlib/graphics/_positions_base.py +++ b/fastplotlib/graphics/_positions_base.py @@ -10,6 +10,7 @@ UniformColor, VertexCmap, PointsSizesFeature, + SizeSpace, ) @@ -54,6 +55,19 @@ def cmap(self, name: str): self._cmap[:] = name + @property + def size_space(self): + """ + The coordinate space in which the size is expressed (‘screen’, ‘world’, ‘model’) + + See https://docs.pygfx.org/stable/_autosummary/utils/utils/enums/pygfx.utils.enums.CoordSpace.html#pygfx.utils.enums.CoordSpace for available options. + """ + return self._size_space.value + + @size_space.setter + def size_space(self, value: str): + self._size_space.set_value(self, value) + def __init__( self, data: Any, @@ -63,6 +77,7 @@ def __init__( cmap: str | VertexCmap = None, cmap_transform: np.ndarray = None, isolated_buffer: bool = True, + size_space: str = "screen", *args, **kwargs, ): @@ -132,6 +147,7 @@ def __init__( self._colors, cmap_name=None, transform=None, alpha=alpha ) + self._size_space = SizeSpace(size_space) super().__init__(*args, **kwargs) def unshare_property(self, property: str): diff --git a/fastplotlib/graphics/line.py b/fastplotlib/graphics/line.py index 1574587fe..8fe505ba9 100644 --- a/fastplotlib/graphics/line.py +++ b/fastplotlib/graphics/line.py @@ -6,11 +6,11 @@ from ._positions_base import PositionsGraphic from .selectors import LinearRegionSelector, LinearSelector, RectangleSelector -from ._features import Thickness +from ._features import Thickness, SizeSpace class LineGraphic(PositionsGraphic): - _features = {"data", "colors", "cmap", "thickness"} + _features = {"data", "colors", "cmap", "thickness", "size_space"} def __init__( self, @@ -22,6 +22,7 @@ def __init__( cmap: str = None, cmap_transform: np.ndarray | Iterable = None, isolated_buffer: bool = True, + size_space: str = "screen", **kwargs, ): """ @@ -53,6 +54,9 @@ def __init__( cmap_transform: 1D array-like of numerical values, optional if provided, these values are used to map the colors from the cmap + size_space: str, default "screen" + coordinate space in which the size is expressed (‘screen’, ‘world’, ‘model’) + **kwargs passed to Graphic @@ -66,6 +70,7 @@ def __init__( cmap=cmap, cmap_transform=cmap_transform, isolated_buffer=isolated_buffer, + size_space=size_space, **kwargs, ) @@ -83,10 +88,14 @@ def __init__( color_mode="uniform", color=self.colors, pick_write=True, + thickness_space=self.size_space, ) else: material = MaterialCls( - thickness=self.thickness, color_mode="vertex", pick_write=True + thickness=self.thickness, + color_mode="vertex", + pick_write=True, + thickness_space=self.size_space, ) geometry = pygfx.Geometry( positions=self._data.buffer, colors=self._colors.buffer diff --git a/fastplotlib/graphics/scatter.py b/fastplotlib/graphics/scatter.py index 39d815c95..8dad7cd43 100644 --- a/fastplotlib/graphics/scatter.py +++ b/fastplotlib/graphics/scatter.py @@ -4,11 +4,11 @@ import pygfx from ._positions_base import PositionsGraphic -from ._features import PointsSizesFeature, UniformSize +from ._features import PointsSizesFeature, UniformSize, SizeSpace class ScatterGraphic(PositionsGraphic): - _features = {"data", "sizes", "colors", "cmap"} + _features = {"data", "sizes", "colors", "cmap", "size_space"} def __init__( self, @@ -21,6 +21,7 @@ def __init__( isolated_buffer: bool = True, sizes: float | np.ndarray | Iterable[float] = 1, uniform_size: bool = False, + size_space: str = "screen", **kwargs, ): """ @@ -60,6 +61,9 @@ def __init__( if True, uses a uniform buffer for the scatter point sizes, basically saves GPU VRAM when all scatter points are the same size + size_space: str, default "screen" + coordinate space in which the size is expressed (‘screen’, ‘world’, ‘model’) + kwargs passed to Graphic @@ -73,6 +77,7 @@ def __init__( cmap=cmap, cmap_transform=cmap_transform, isolated_buffer=isolated_buffer, + size_space=size_space, **kwargs, ) @@ -80,6 +85,7 @@ def __init__( geo_kwargs = {"positions": self._data.buffer} material_kwargs = {"pick_write": True} + self._size_space = SizeSpace(size_space) if uniform_color: material_kwargs["color_mode"] = "uniform" @@ -97,6 +103,7 @@ def __init__( self._sizes = PointsSizesFeature(sizes, n_datapoints=n_datapoints) geo_kwargs["sizes"] = self.sizes.buffer + material_kwargs["size_space"] = self.size_space world_object = pygfx.Points( pygfx.Geometry(**geo_kwargs), material=pygfx.PointsMaterial(**material_kwargs), diff --git a/tests/test_positions_graphics.py b/tests/test_positions_graphics.py index 81403c06b..b76ece2ca 100644 --- a/tests/test_positions_graphics.py +++ b/tests/test_positions_graphics.py @@ -443,3 +443,45 @@ def test_thickness(thickness): else: assert isinstance(graphic.world_object.material, pygfx.LineMaterial) + + +@pytest.mark.parametrize("graphic_type", ["line", "scatter"]) +@pytest.mark.parametrize("size_space", ["screen", "world", "model"]) +def test_size_space(graphic_type, size_space): + fig = fpl.Figure() + + kwargs = dict() + for kwarg in ["size_space"]: + if locals()[kwarg] is not None: + # add to dict of arguments that will be passed + kwargs[kwarg] = locals()[kwarg] + + data = generate_positions_spiral_data("xy") + + if size_space is None: + size_space = "screen" # default space + + # size_space is really an alias for pygfx.utils.enums.CoordSpace + if graphic_type == "line": + graphic = fig[0, 0].add_line(data=data, **kwargs) + + # test getter + assert graphic.world_object.material.thickness_space == size_space + assert graphic.size_space == size_space + + # test setter + graphic.size_space = "world" + assert graphic.size_space == "world" + assert graphic.world_object.material.thickness_space == "world" + + elif graphic_type == "scatter": + + # test getter + graphic = fig[0, 0].add_scatter(data=data, **kwargs) + assert graphic.world_object.material.size_space == size_space + assert graphic.size_space == size_space + + # test setter + graphic.size_space = "world" + assert graphic.size_space == "world" + assert graphic.world_object.material.size_space == "world" From 04be32e283d819db27a61f8f144a68a060035580 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Thu, 16 Jan 2025 11:58:23 -0500 Subject: [PATCH 132/185] small wgpu changes (#691) * fix deprecated wgpu calls * show a warning instead of raising an exception if no WGPU adapter exists * black --- fastplotlib/__init__.py | 7 +++++-- fastplotlib/utils/gui.py | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/fastplotlib/__init__.py b/fastplotlib/__init__.py index 2f3c35a1a..9cc8d8432 100644 --- a/fastplotlib/__init__.py +++ b/fastplotlib/__init__.py @@ -24,7 +24,9 @@ __version__ = f.read().split("\n")[0] if len(enumerate_adapters()) < 1: - raise IndexError( + from warnings import warn + + warn( f"WGPU could not enumerate any adapters, fastplotlib will not work.\n" f"This is caused by one of the following:\n" f"1. You do not have a hardware GPU installed and you do not have " @@ -35,5 +37,6 @@ f"common in cloud computing environments.\n" f"These two links can help you troubleshoot:\n" f"https://wgpu-py.readthedocs.io/en/stable/start.html#platform-requirements\n" - f"https://fastplotlib.readthedocs.io/en/latest/user_guide/gpu.html\n" + f"https://fastplotlib.readthedocs.io/en/latest/user_guide/gpu.html\n", + RuntimeWarning, ) diff --git a/fastplotlib/utils/gui.py b/fastplotlib/utils/gui.py index a8a79920f..6a0d8dfdc 100644 --- a/fastplotlib/utils/gui.py +++ b/fastplotlib/utils/gui.py @@ -59,10 +59,10 @@ def _notebook_print_banner(): image = Image(value=logo_data, format="png", width=300, height=55) # get adapters and info - adapters = [a for a in wgpu.gpu.enumerate_adapters()] + adapters = [a for a in wgpu.gpu.enumerate_adapters_sync()] adapters_info = [a.info for a in adapters] - default_adapter_info = wgpu.gpu.request_adapter().info + default_adapter_info = wgpu.gpu.request_adapter_sync().info default_ix = adapters_info.index(default_adapter_info) if len(adapters) < 1: From 37f9bd4c49bab2d870c54f86469e77fe44705447 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Thu, 16 Jan 2025 11:59:13 -0500 Subject: [PATCH 133/185] update python versions for CI (#692) * drop py3.10 from CI, add py3.13 * use py3.12 for pypi publish * use py3.12 for regenerate screenshots --- .github/workflows/ci.yml | 8 ++++---- .github/workflows/pypi-publish.yml | 2 +- .github/workflows/screenshots.yml | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1fe4a128b..0427b867f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,12 +23,12 @@ jobs: fail-fast: false matrix: include: - - name: Test py310 - pyversion: '3.10' - name: Test py311 pyversion: '3.11' - name: Test py312 pyversion: '3.12' + - name: Test py313 + pyversion: '3.13' steps: - uses: actions/checkout@v4 with: @@ -79,12 +79,12 @@ jobs: fail-fast: false matrix: include: - - name: Test py310 - pyversion: '3.10' - name: Test py311 pyversion: '3.11' - name: Test py312 pyversion: '3.12' + - name: Test py313 + pyversion: '3.13' steps: - uses: actions/checkout@v4 with: diff --git a/.github/workflows/pypi-publish.yml b/.github/workflows/pypi-publish.yml index 9c5f3da59..780bf4d08 100644 --- a/.github/workflows/pypi-publish.yml +++ b/.github/workflows/pypi-publish.yml @@ -27,7 +27,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: '3.10' + python-version: '3.12' - name: Install dependencies run: | python -m pip install --upgrade pip diff --git a/.github/workflows/screenshots.yml b/.github/workflows/screenshots.yml index 87d8ca05a..b8ac88d56 100644 --- a/.github/workflows/screenshots.yml +++ b/.github/workflows/screenshots.yml @@ -20,10 +20,10 @@ jobs: - uses: actions/checkout@v4 with: lfs: true - - name: Set up Python 3.11 + - name: Set up Python 3.12 uses: actions/setup-python@v5 with: - python-version: '3.11' + python-version: '3.12' - name: Install llvmpipe and lavapipe for offscreen canvas run: | sudo apt-get update -y -qq From cf1f3bc7644fdcca732df1f16c3932f491eebdc8 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Thu, 16 Jan 2025 12:05:39 -0500 Subject: [PATCH 134/185] add fft example (#693) * add fft example * github actions has texture size limit of 2048 * position selector to center --- examples/selection_tools/fft.py | 101 ++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 examples/selection_tools/fft.py diff --git a/examples/selection_tools/fft.py b/examples/selection_tools/fft.py new file mode 100644 index 000000000..f249f2c11 --- /dev/null +++ b/examples/selection_tools/fft.py @@ -0,0 +1,101 @@ +""" +Explore fourier transform of images +=================================== +Example showing how to use a `RectangleSelector` to interactively reconstruct +an image using portions of it fourier transform +""" + +# test_example = false +# sphinx_gallery_pygfx_docs = 'screenshot' + +import numpy as np +import fastplotlib as fpl +import imageio.v3 as iio + +image = iio.imread("imageio:camera.png") + +# compute discrete fourier transform of image +img_fft = np.fft.fftshift(np.fft.fft2(image)) + +# image to just visualize absolute magnitudes +log_abs_img_fft = np.log(np.abs(img_fft + 1)) + +# placeholders for displaying fft and inverse fft of selections +zeros = np.zeros(image.shape) + +# create an ImageWidget to display all the images +iw = fpl.ImageWidget( + data=[image, log_abs_img_fft, zeros, zeros, zeros, zeros], + names=["image", "DFT", "selected", "FFT of selected", "not-selected", "IFFT of not-selected"], + figure_shape=(3, 2), # so we can see image and fft side by side + figure_kwargs={"size": (700, 1024)}, + histogram_widget=False, +) + +# we don't need the toolbars here, unclutter the figure +for subplot in iw.figure: + subplot.toolbar = False + +# viridis cmap for the fft images +iw.cmap = "viridis" + +# gray for the non-fft images +iw.managed_graphics[0].cmap = "gray" +iw.managed_graphics[3].cmap = "gray" +iw.managed_graphics[-1].cmap = "gray" + +# set contrast limits based on the full DFT for the DFT-selection images +iw.figure["selected"].graphics[0].vmin, iw.figure["selected"].graphics[0].vmax = log_abs_img_fft.min(), log_abs_img_fft.max() +iw.figure["not-selected"].graphics[0].vmin, iw.figure["not-selected"].graphics[0].vmax = log_abs_img_fft.min(), log_abs_img_fft.max() + +iw.show() + +# create a rectangle selector +rs = iw.managed_graphics[1].add_rectangle_selector(edge_color="w", edge_thickness=2.0) + + +@rs.add_event_handler("selection") +def update_images(ev): + """ + Updates the images when the selection changes + """ + + # get the bbox of the selection + row_ixs, col_ixs = ev.get_selected_indices() + row_slice = slice(row_ixs[0], row_ixs[-1] + 1) + col_slice = slice(col_ixs[0], col_ixs[-1] + 1) + + # fft of the selection + selected_fft = np.zeros(image.shape, dtype=np.complex64) + selected_fft[row_slice, col_slice] = img_fft[row_slice, col_slice] + + # update image graphic with the current fft selection + iw.managed_graphics[2].data = np.log(np.abs(selected_fft + 1)) + + # inverse fft to reconstruct image using only the selection + iw.managed_graphics[3].data = np.fft.ifft2(np.fft.fftshift(selected_fft)) + iw.managed_graphics[3].reset_vmin_vmax() + + # fft of the region outside the selection + unselected_fft = img_fft.copy() + unselected_fft[row_slice, col_slice] = 0 + + # update image graphic with unselected fft area + iw.managed_graphics[4].data = np.log(np.abs(unselected_fft + 1)) + + # inverse fft to reconstruct image using only the unselected part of the fft + iw.managed_graphics[5].data = np.fft.ifft2(np.fft.fftshift(unselected_fft)) + iw.managed_graphics[5].reset_vmin_vmax() + + +# set initial selection to the center +rs.selection = (225, 285, 225, 285) + + +figure = iw.figure + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.loop.run() From 78fa6d90169c8b33c00e23dbf8341999d7edfaf8 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Thu, 16 Jan 2025 12:06:06 -0500 Subject: [PATCH 135/185] fix `ImageWidget.reset_vmin_vmax()` (#690) * fix iw.reset_vmin_vmax() * fix vmin, vmax setting of first frame in imagewidget * update screenshots since vmin changes * update nb iw screenshots --- .../nb-image-widget-movie-single-0-reset.png | 4 +- .../nb-image-widget-movie-single-0.png | 4 +- .../nb-image-widget-movie-single-279.png | 4 +- ...e-widget-movie-single-50-window-max-33.png | 4 +- ...-widget-movie-single-50-window-mean-13.png | 4 +- ...-widget-movie-single-50-window-mean-33.png | 4 +- ...ge-widget-movie-single-50-window-reset.png | 4 +- .../nb-image-widget-movie-single-50.png | 4 +- ...et-zfish-frame-50-frame-apply-gaussian.png | 4 +- ...idget-zfish-frame-50-frame-apply-reset.png | 4 +- ...ge-widget-zfish-frame-50-max-window-13.png | 4 +- ...e-widget-zfish-frame-50-mean-window-13.png | 4 +- ...ge-widget-zfish-frame-50-mean-window-5.png | 4 +- .../nb-image-widget-zfish-frame-50.png | 4 +- .../nb-image-widget-zfish-frame-99.png | 4 +- ...ish-grid-frame-50-frame-apply-gaussian.png | 4 +- ...-zfish-grid-frame-50-frame-apply-reset.png | 4 +- ...dget-zfish-grid-frame-50-max-window-13.png | 4 +- ...get-zfish-grid-frame-50-mean-window-13.png | 4 +- ...dget-zfish-grid-frame-50-mean-window-5.png | 4 +- .../nb-image-widget-zfish-grid-frame-50.png | 4 +- .../nb-image-widget-zfish-grid-frame-99.png | 4 +- ...e-widget-zfish-grid-init-mean-window-5.png | 4 +- ...-image-widget-zfish-init-mean-window-5.png | 4 +- ...dget-zfish-mixed-rgb-cockatoo-frame-50.png | 4 +- .../screenshots/image_widget_single_video.png | 4 +- examples/screenshots/image_widget_videos.png | 4 +- fastplotlib/widgets/image_widget/_widget.py | 44 ++++++++++++++++--- 28 files changed, 91 insertions(+), 61 deletions(-) diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-0-reset.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-0-reset.png index f64393c89..22c7ad73a 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-0-reset.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-0-reset.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8e3f53d21e99424f11a3a920346909dce42f2c344ae9b43af5965bc2302ae9ab -size 117732 +oid sha256:9b122f0ba9bfff0b0868778f09744870238bf7b4945e57410b6aa36341eaaf4a +size 116781 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-0.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-0.png index f64393c89..22c7ad73a 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-0.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8e3f53d21e99424f11a3a920346909dce42f2c344ae9b43af5965bc2302ae9ab -size 117732 +oid sha256:9b122f0ba9bfff0b0868778f09744870238bf7b4945e57410b6aa36341eaaf4a +size 116781 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-279.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-279.png index 812e0f60d..84e2514d0 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-279.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-279.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b59639c87a6d02aaf8a14e8d681d763a795c15b7aa8d2d0a90dba3a5732e4fe5 -size 140917 +oid sha256:fcc5092f35c881da4a9b9f3c216fb608b8dfc27a791b83e0d5184ef3973746cf +size 139375 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-max-33.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-max-33.png index 9907e1473..075116ff4 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-max-33.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-max-33.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5f9f5e1953aae367cca8add259c86d82fd5225f4cf4279c6504b1ecd9d5a0bd1 -size 125867 +oid sha256:3fabd9d52ae2815ae883a4c8c8a8b1385c0824e0212347896a09eb3600c29430 +size 124238 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-13.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-13.png index 695964431..216ae2b9e 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3633ce4d8995ebdb224df9fcd8ebdf22ad9ffa72e3ef80692f4f691895faf903 -size 110162 +oid sha256:86ad31cab3aefa24a1c4c0adc2033cbc9fa594e9cf8ab8e4a6ff0a3630bb7896 +size 109041 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-33.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-33.png index 039cdd25c..99302d4e6 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-33.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-33.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8ba9762d2d3fb7ddaa1628e40588b28780ac3e0185ec97187eb1975016aa32f1 -size 102404 +oid sha256:3ebf4e875199c7e682dc15aa03a36ea9f111618853a94076064b055bf6ce788e +size 101209 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-reset.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-reset.png index a6aae44ba..3bb5081f0 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-reset.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-reset.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f9374ebf448c1692c63c3ca1c28b0d16125bb5ae021d9a7cc8a1beee3c25a183 -size 124817 +oid sha256:d8dbf6b76818315e40d9d4cc97807c4276c27e7a9a09d2643f74adf701ef1cdc +size 123136 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50.png index a6aae44ba..3bb5081f0 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f9374ebf448c1692c63c3ca1c28b0d16125bb5ae021d9a7cc8a1beee3c25a183 -size 124817 +oid sha256:d8dbf6b76818315e40d9d4cc97807c4276c27e7a9a09d2643f74adf701ef1cdc +size 123136 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-gaussian.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-gaussian.png index 0f6223ab4..ec2911374 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-gaussian.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-gaussian.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ecba9807b765ea12ad1183dabc35c9b6a2ba45f95aa0126d772801c3a5aba6e1 -size 92089 +oid sha256:39adce1898e5b00ccf9d8792bd4e76f2da2591a8c3f6e201a5c2af1f814d37cb +size 58692 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-reset.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-reset.png index 0c6b55201..ae72c8175 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-reset.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-reset.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:433190c3f56075ca3e9a5486e5986424d31fb7b6f6145225a15bfafd5e00fa83 -size 74779 +oid sha256:d50b960c66acc6672aaeb6a97f6a69aad14f9e54060c3702679d6a5bf2b70e78 +size 70582 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-max-window-13.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-max-window-13.png index 8321b60e9..66f9136dc 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-max-window-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-max-window-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:de11fd007bad064ccb6574ee682d7cd25c64738768d1dc9e42b53c88cb78c46c -size 155123 +oid sha256:d244a8a91d04380f2ebe255b2b56b3be5249c0a382821877710cae6bdaa2d414 +size 128643 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-13.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-13.png index 27c3af054..230e71c0f 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ec9e3a90abd029a5fb6e149ba721f3340e499c26a2dd4aab6ab07a185bbc4ff0 -size 103878 +oid sha256:24c991457b8b081ee271cbdb03821ea1024c8340db92340e0e445bf3c70aba40 +size 97903 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-5.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-5.png index 72ee543e2..a355670a0 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-5.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f1c9b64c4c67a024a5dc839ad13f68ee60f3b3675144976c7e3b6ce989e0c822 -size 97746 +oid sha256:bdd62a9bd1ca4f1ff110a30fb4064d778f02120295a3e3d30552e06892146e40 +size 93658 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50.png index 572b1e590..c47545ccb 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:51865c6bfd62d9691c638d4bc3b62507a61e873851dc35bad66acb0d41e4fe3e -size 83536 +oid sha256:db7e2cf15ff3ce61b169b114c630e2339c1c6b5c687c580e1ee6964785df6790 +size 74844 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-99.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-99.png index 1ceebf476..69ef49149 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-99.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-99.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9f46a0e116fed8d474217d5a6ca6f9861647707e1abe049e796c67a417e197cd -size 72243 +oid sha256:64d2d3fd91ac8e10736add5a82a312927ae6f976119cfa2aaa1fc1e008bcf6f1 +size 66038 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-gaussian.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-gaussian.png index 8464eed64..bb04d1800 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-gaussian.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-gaussian.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ac0fcaae315baf29a46f2c4151b988535934927441c13eef711b8567951b50cb -size 106873 +oid sha256:8d2a805c85e1cdf5bd2d995600b033019680ac645d7634efeaf1db7d0d00d4aa +size 79403 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-reset.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-reset.png index c81b99e29..5b1a4a8da 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-reset.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-reset.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a001606a953ea6b8a5c7f741532df4a006e7bf4dd31ff9e7b4f9f1025987153c -size 109591 +oid sha256:440623bb4588994c4f52f17e027af29d1f2d5d330c5691630fd2acb9e38f8a25 +size 99033 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-max-window-13.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-max-window-13.png index b7314938c..bd72160dd 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-max-window-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-max-window-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cb99c2827dea7e49d5ffcd4fe10e4052671548500f05001067077a8841d03cc8 -size 168987 +oid sha256:9ee56adf8f2a516ef74a799e9e503f980c36c4dfb41f9ff6d8168cfcf65ad092 +size 132745 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-13.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-13.png index b09d8b4ca..438d1e2d4 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:729b31d419eccf02eb617a10bb29b2f9295726f8167329ac0f7498e752688bb8 -size 113715 +oid sha256:de4733b82c2e77baa659582401eff0c70ec583c50462b33bcbfd2bb00ceaa517 +size 102959 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-5.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-5.png index 9daeb680f..ee081c6df 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-5.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7de21cfe1a21c1ef59bfb7121b2ef6f55dd9931d5885a3ee3c932dcf82389191 -size 116752 +oid sha256:6107f108b5a86ba376c53f5e207841c01a85b686100efb46e5df3565127201d2 +size 106765 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50.png index fff0cca94..c2071c850 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ca70505035a6b8cb8589a8219edd3d79eecdc93642e3d7f7db00516d27bac959 -size 122542 +oid sha256:caa15f6bc21a3be0f480b442414ec4b96b21cc1de2cdcb891b366692962d4ef8 +size 100753 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-99.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-99.png index 57154e0c0..3d90fd77a 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-99.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-99.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a41f79b543ea852345ca5c5c3e401d39c43b9f5d4172805c48d7010d3e85e88a -size 118378 +oid sha256:4e23288d695a5a91188b285f6a0a2c9f0643dd19f3d6dedb56f4389f44ed1f44 +size 98621 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-init-mean-window-5.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-init-mean-window-5.png index e5554d635..3fd5688d9 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-init-mean-window-5.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-init-mean-window-5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d364f4c18516c282cd284c731122618bdf3418b6d536afa7b0556105f89c3607 -size 119048 +oid sha256:8b4e1bb60466d7553b4d1afc14015b7c4edc6e79c724c0afb5acd123a10093d0 +size 105541 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-init-mean-window-5.png b/examples/notebooks/screenshots/nb-image-widget-zfish-init-mean-window-5.png index 94b39e8f9..14d9e8448 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-init-mean-window-5.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-init-mean-window-5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:07bcc6ef243d9d3ffd7fae69facf655e7c02b1cb53ea96a38b40ed672655bf66 -size 86607 +oid sha256:3aad82db14f8100f669d2ad36b5bc3973b7c12457adfdd73adbc81c759338f7b +size 80964 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-frame-50.png b/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-frame-50.png index dab2098fd..af04a6f73 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-frame-50.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-frame-50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7270870881ac478f48a269b060c5bf7ca59e7abe8a42254162a0295f6165230b -size 117870 +oid sha256:e40559eea03790315718c55b4ec4976aacb97a2f07bcdc49d917c044745687c2 +size 117144 diff --git a/examples/screenshots/image_widget_single_video.png b/examples/screenshots/image_widget_single_video.png index aa829125c..5d10d91a6 100644 --- a/examples/screenshots/image_widget_single_video.png +++ b/examples/screenshots/image_widget_single_video.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:11ffeceb298c5b5d429da822e1764c11d862bf85630ce3390475c766366bceab -size 91299 +oid sha256:de1750c9c1c3cd28c356fb51687f4a8f00afb3cc7e365502342168fce8459d3a +size 90307 diff --git a/examples/screenshots/image_widget_videos.png b/examples/screenshots/image_widget_videos.png index 70ad686c6..f0e262e24 100644 --- a/examples/screenshots/image_widget_videos.png +++ b/examples/screenshots/image_widget_videos.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2c17df678e54e9cddbd42869cfe7a32069b7ffa70e6227c95c333537e1efede6 -size 170218 +oid sha256:23d993e0b5b6bcfe67da7aa4ceab3f06e99358b00f287b9703c4c3bff19648ba +size 169541 diff --git a/fastplotlib/widgets/image_widget/_widget.py b/fastplotlib/widgets/image_widget/_widget.py index c52f026db..31a8176e5 100644 --- a/fastplotlib/widgets/image_widget/_widget.py +++ b/fastplotlib/widgets/image_widget/_widget.py @@ -1,3 +1,4 @@ +from copy import deepcopy from typing import Callable from warnings import warn @@ -5,9 +6,9 @@ from rendercanvas import BaseRenderCanvas -from ... import Figure +from ...layouts import ImguiFigure as Figure from ...graphics import ImageGraphic -from ...utils import calculate_figure_shape +from ...utils import calculate_figure_shape, quick_min_max from ...tools import HistogramLUTTool from ._sliders import ImageWidgetSliders @@ -507,6 +508,12 @@ def __init__( graphic_kwargs.update({"cmap": cmap}) + vmin_specified, vmax_specified = None, None + if "vmin" in graphic_kwargs.keys(): + vmin_specified = graphic_kwargs.pop("vmin") + if "vmax" in graphic_kwargs.keys(): + vmax_specified = graphic_kwargs.pop("vmax") + self._figure: Figure = Figure(**figure_kwargs_default) self._histogram_widget = histogram_widget @@ -518,7 +525,34 @@ def __init__( frame = self._process_indices(d, slice_indices=self._current_index) frame = self._process_frame_apply(frame, data_ix) - ig = ImageGraphic(frame, name="image_widget_managed", **graphic_kwargs) + + if (vmin_specified is None) or (vmax_specified is None): + # if either vmin or vmax are not specified, calculate an estimate by subsampling + vmin_estimate, vmax_estimate = quick_min_max(d) + + # decide vmin, vmax passed to ImageGraphic constructor based on whether it's user specified or now + if vmin_specified is None: + # user hasn't specified vmin, use estimated value + vmin = vmin_estimate + else: + # user has provided a specific value, use that + vmin = vmin_specified + + if vmax_specified is None: + vmax = vmax_estimate + else: + vmax = vmax_specified + else: + # both vmin and vmax are specified + vmin, vmax = vmin_specified, vmax_specified + + ig = ImageGraphic( + frame, + name="image_widget_managed", + vmin=vmin, + vmax=vmax, + **graphic_kwargs, + ) subplot.add_graphic(ig) subplot.name = name subplot.set_title(name) @@ -811,10 +845,6 @@ def reset_vmin_vmax(self): hlut = subplot.docks["right"]["histogram_lut"] hlut.set_data(data, reset_vmin_vmax=True) - else: - for ig in self.managed_graphics: - ig.reset_vmin_vmax() - def reset_vmin_vmax_frame(self): """ Resets the vmin vmax and HistogramLUT widgets w.r.t. the current data shown in the From 93ec79d3930a9b2fc46b319d3699ac0fed14ccd4 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Mon, 20 Jan 2025 10:53:05 -0500 Subject: [PATCH 136/185] remove glfw import that leaked in (#696) --- examples/image_widget/image_widget.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/image_widget/image_widget.py b/examples/image_widget/image_widget.py index b500720d5..4fe47b7fe 100644 --- a/examples/image_widget/image_widget.py +++ b/examples/image_widget/image_widget.py @@ -10,7 +10,7 @@ # test_example = true # sphinx_gallery_pygfx_docs = 'screenshot' -import glfw + import fastplotlib as fpl import imageio.v3 as iio # not a fastplotlib dependency, only used for examples From c84e5bad06a1c66bd67f6d19ef5e175a5397d4ca Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Mon, 20 Jan 2025 10:55:15 -0500 Subject: [PATCH 137/185] raise ModuleNotFoundError if user tries to import imgui and it doesn't exist (#697) --- fastplotlib/__init__.py | 2 +- fastplotlib/widgets/image_widget/__init__.py | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/fastplotlib/__init__.py b/fastplotlib/__init__.py index 9cc8d8432..7eb9554e8 100644 --- a/fastplotlib/__init__.py +++ b/fastplotlib/__init__.py @@ -13,10 +13,10 @@ if IMGUI: # default to imgui figure if imgui_bundle is installed from .layouts import ImguiFigure as Figure - from .widgets import ImageWidget else: from .layouts import Figure +from .widgets import ImageWidget from .utils import config, enumerate_adapters, select_adapter, print_wgpu_report diff --git a/fastplotlib/widgets/image_widget/__init__.py b/fastplotlib/widgets/image_widget/__init__.py index 93aaa4ce1..70a1aa8ae 100644 --- a/fastplotlib/widgets/image_widget/__init__.py +++ b/fastplotlib/widgets/image_widget/__init__.py @@ -1 +1,13 @@ -from ._widget import ImageWidget +from ...layouts import IMGUI + +if IMGUI: + from ._widget import ImageWidget + +else: + + class ImageWidget: + def __init__(self, *args, **kwargs): + raise ModuleNotFoundError( + "ImageWidget requires `imgui-bundle` to be installed.\n" + "pip install imgui-bundle" + ) From 29e3ec4b92990e27208f4fa5f54fd1b0e5543ad5 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Mon, 20 Jan 2025 14:59:25 -0500 Subject: [PATCH 138/185] simplify CI and install extras (#698) * simplify CI, WIP * tweak * jupyter rfb v0.5.0 not on pypi yet * tweak * twaek * ok let's make this look ugly * typical bash and sed bleh * ok so github really likes it ugly * needs more sed * see if core dump fixed * imgui and no-imgui matrix for regen screenshots * update test utils * account for imagewidget which needs imgui * modify regen * tweak * skip imgui required tests when not installed * cleanup * move check earlier * more informative artifact upload names * typo * use upload-artifact v4 * add no imgui ground truth imageS * increase jupyter_rfb version * use bash find to exclude/include imagewidget nbs depending on imgui existance * test imagewidget gc in nbs only if imgui installed * regen screenshots nb with and w/o imgui * add nb back to regen matrix * we actually only need 2 variants of regen * prefix no-imgui for nb screenshots * add no-imgui-nb screenshots * python version --- .github/workflows/ci.yml | 93 ++++++------------- .github/workflows/docs-deploy.yml | 2 +- .github/workflows/screenshots.yml | 29 ++++-- CONTRIBUTING.md | 4 +- examples/notebooks/nb_test_utils.py | 21 ++++- .../screenshots/no-imgui-nb-astronaut.png | 3 + .../screenshots/no-imgui-nb-astronaut_RGB.png | 3 + .../screenshots/no-imgui-nb-camera.png | 3 + .../screenshots/no-imgui-nb-lines-3d.png | 3 + .../screenshots/no-imgui-nb-lines-colors.png | 3 + .../screenshots/no-imgui-nb-lines-data.png | 3 + .../no-imgui-nb-lines-underlay.png | 3 + .../screenshots/no-imgui-nb-lines.png | 3 + examples/notebooks/test_gc.ipynb | 70 ++++---------- examples/screenshots/no-imgui-gridplot.png | 3 + .../no-imgui-gridplot_non_square.png | 3 + examples/screenshots/no-imgui-heatmap.png | 3 + examples/screenshots/no-imgui-image_cmap.png | 3 + examples/screenshots/no-imgui-image_rgb.png | 3 + .../no-imgui-image_rgbvminvmax.png | 3 + .../screenshots/no-imgui-image_simple.png | 3 + examples/screenshots/no-imgui-image_small.png | 3 + .../screenshots/no-imgui-image_vminvmax.png | 3 + examples/screenshots/no-imgui-line.png | 3 + examples/screenshots/no-imgui-line_cmap.png | 3 + .../screenshots/no-imgui-line_cmap_more.png | 3 + .../screenshots/no-imgui-line_collection.png | 3 + .../no-imgui-line_collection_cmap_values.png | 3 + ...ine_collection_cmap_values_qualitative.png | 3 + .../no-imgui-line_collection_colors.png | 3 + .../no-imgui-line_collection_slicing.png | 3 + .../screenshots/no-imgui-line_colorslice.png | 3 + .../screenshots/no-imgui-line_dataslice.png | 3 + examples/screenshots/no-imgui-line_stack.png | 3 + ...-linear_region_selectors_match_offsets.png | 3 + .../no-imgui-scatter_cmap_iris.png | 3 + .../no-imgui-scatter_colorslice_iris.png | 3 + .../no-imgui-scatter_dataslice_iris.png | 3 + .../screenshots/no-imgui-scatter_iris.png | 3 + .../screenshots/no-imgui-scatter_size.png | 3 + examples/tests/test_examples.py | 43 +++++++-- setup.py | 21 +---- 42 files changed, 228 insertions(+), 157 deletions(-) create mode 100644 examples/notebooks/screenshots/no-imgui-nb-astronaut.png create mode 100644 examples/notebooks/screenshots/no-imgui-nb-astronaut_RGB.png create mode 100644 examples/notebooks/screenshots/no-imgui-nb-camera.png create mode 100644 examples/notebooks/screenshots/no-imgui-nb-lines-3d.png create mode 100644 examples/notebooks/screenshots/no-imgui-nb-lines-colors.png create mode 100644 examples/notebooks/screenshots/no-imgui-nb-lines-data.png create mode 100644 examples/notebooks/screenshots/no-imgui-nb-lines-underlay.png create mode 100644 examples/notebooks/screenshots/no-imgui-nb-lines.png create mode 100644 examples/screenshots/no-imgui-gridplot.png create mode 100644 examples/screenshots/no-imgui-gridplot_non_square.png create mode 100644 examples/screenshots/no-imgui-heatmap.png create mode 100644 examples/screenshots/no-imgui-image_cmap.png create mode 100644 examples/screenshots/no-imgui-image_rgb.png create mode 100644 examples/screenshots/no-imgui-image_rgbvminvmax.png create mode 100644 examples/screenshots/no-imgui-image_simple.png create mode 100644 examples/screenshots/no-imgui-image_small.png create mode 100644 examples/screenshots/no-imgui-image_vminvmax.png create mode 100644 examples/screenshots/no-imgui-line.png create mode 100644 examples/screenshots/no-imgui-line_cmap.png create mode 100644 examples/screenshots/no-imgui-line_cmap_more.png create mode 100644 examples/screenshots/no-imgui-line_collection.png create mode 100644 examples/screenshots/no-imgui-line_collection_cmap_values.png create mode 100644 examples/screenshots/no-imgui-line_collection_cmap_values_qualitative.png create mode 100644 examples/screenshots/no-imgui-line_collection_colors.png create mode 100644 examples/screenshots/no-imgui-line_collection_slicing.png create mode 100644 examples/screenshots/no-imgui-line_colorslice.png create mode 100644 examples/screenshots/no-imgui-line_dataslice.png create mode 100644 examples/screenshots/no-imgui-line_stack.png create mode 100644 examples/screenshots/no-imgui-linear_region_selectors_match_offsets.png create mode 100644 examples/screenshots/no-imgui-scatter_cmap_iris.png create mode 100644 examples/screenshots/no-imgui-scatter_colorslice_iris.png create mode 100644 examples/screenshots/no-imgui-scatter_dataslice_iris.png create mode 100644 examples/screenshots/no-imgui-scatter_iris.png create mode 100644 examples/screenshots/no-imgui-scatter_size.png diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0427b867f..097a23475 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,20 +15,16 @@ on: jobs: test-build-full: - name: Test Linux, notebook + offscreen + name: Test Linux runs-on: ubuntu-latest - timeout-minutes: 30 + timeout-minutes: 10 if: ${{ !github.event.pull_request.draft }} strategy: fail-fast: false matrix: - include: - - name: Test py311 - pyversion: '3.11' - - name: Test py312 - pyversion: '3.12' - - name: Test py313 - pyversion: '3.13' + python: ["3.11", "3.12", "3.13"] + imgui_dep: ["imgui", ""] + notebook_dep: ["notebook", ""] steps: - uses: actions/checkout@v4 with: @@ -36,7 +32,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: ${{ matrix.pyversion }} + python-version: ${{ matrix.python }} - name: Install llvmpipe and lavapipe for offscreen canvas run: | sudo apt-get update -y -qq @@ -47,7 +43,12 @@ jobs: # remove pygfx from install_requires, we install using pygfx@main sed -i "/pygfx/d" ./setup.py pip install git+https://github.com/pygfx/pygfx.git@main - pip install -e ".["tests"]" + - name: Install fastplotlib + run: | + # create string with one of: tests,imgui,notebook; test,imgui; test,notebook ; tests + # sed removes trailing comma + # install fastplotlib with given extras options from above + pip install -e ".[$(echo "tests,${{ matrix.imgui_dep }},${{ matrix.notebook_dep }}" | sed -e "s/,\+/,/g" -e "s/,$//")]" - name: Show wgpu backend run: python -c "from examples.tests.testutils import wgpu_backend; print(wgpu_backend)" @@ -60,69 +61,27 @@ jobs: PYGFX_EXPECT_LAVAPIPE: true run: | WGPU_FORCE_OFFSCREEN=1 pytest -v tests/ - pytest -v examples - FASTPLOTLIB_NB_TESTS=1 pytest --nbmake examples/notebooks/ - - uses: actions/upload-artifact@v4 - if: ${{ failure() }} - with: - name: screenshot-diffs - path: | - examples/diffs - examples/notebooks/diffs - - test-build-offscreen: - name: Test Linux, only offscreen - runs-on: ubuntu-latest - timeout-minutes: 30 - if: ${{ !github.event.pull_request.draft }} - strategy: - fail-fast: false - matrix: - include: - - name: Test py311 - pyversion: '3.11' - - name: Test py312 - pyversion: '3.12' - - name: Test py313 - pyversion: '3.13' - steps: - - uses: actions/checkout@v4 - with: - lfs: true - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.pyversion }} - - name: Install llvmpipe and lavapipe for offscreen canvas - run: | - sudo apt-get update -y -qq - sudo apt-get install --no-install-recommends -y libegl1-mesa-dev libgl1-mesa-dri libxcb-xfixes0-dev mesa-vulkan-drivers xorg-dev - - name: Install dev dependencies - run: | - python -m pip install --upgrade pip setuptools - # remove pygfx from install_requires, we install using pygfx@main - sed -i "/pygfx/d" ./setup.py - pip install git+https://github.com/pygfx/pygfx.git@main - pip install -e ".["tests-desktop"]" - - name: Show wgpu backend - run: - python -c "from examples.tests.testutils import wgpu_backend; print(wgpu_backend)" - - name: fetch git lfs files - run: | - git lfs fetch --all - git lfs pull - - name: Test examples + WGPU_FORCE_OFFSCREEN=1 pytest -v examples/ + - name: Test examples notebooks, exclude ImageWidget notebook + if: ${{ matrix.notebook_dep == 'notebook' }} env: PYGFX_EXPECT_LAVAPIPE: true - run: | - WGPU_FORCE_OFFSCREEN=1 pytest -v tests/ - pytest -v examples + # test notebooks, exclude ImageWidget notebooks + run: FASTPLOTLIB_NB_TESTS=1 pytest --nbmake $(find ./examples/notebooks/ -maxdepth 1 -type f -name "*.ipynb" ! -name "image_widget*.ipynb" -print | xargs) + - name: Test ImageWidget notebooks + # test image widget notebooks only if imgui is installed + if: ${{ matrix.notebook_dep == 'notebook' && matrix.imgui_dep == 'imgui' }} + env: + PYGFX_EXPECT_LAVAPIPE: true + run: FASTPLOTLIB_NB_TESTS=1 pytest --nbmake $(find ./examples/notebooks/ -maxdepth 1 -type f -name "image_widget*.ipynb" -print | xargs) - uses: actions/upload-artifact@v4 if: ${{ failure() }} with: - name: screenshot-diffs + name: screenshot-diffs-${{ matrix.pyversion }}-${{ matrix.imgui_dep }}-${{ matrix.notebook_dep }} path: | examples/diffs + examples/notebooks/diffs + # test-build-full-mac: # name: Test Mac, notebook + glfw diff --git a/.github/workflows/docs-deploy.yml b/.github/workflows/docs-deploy.yml index 63b0f81d7..fe267291a 100644 --- a/.github/workflows/docs-deploy.yml +++ b/.github/workflows/docs-deploy.yml @@ -42,7 +42,7 @@ jobs: # remove pygfx from install_requires, we install using pygfx@main sed -i "/pygfx/d" ./setup.py pip install git+https://github.com/pygfx/pygfx.git@main - pip install -e ".["docs"]" + pip install -e ".[docs,notebook,imgui]" - name: Show wgpu backend run: python -c "from examples.tests.testutils import wgpu_backend; print(wgpu_backend)" diff --git a/.github/workflows/screenshots.yml b/.github/workflows/screenshots.yml index b8ac88d56..80bf95201 100644 --- a/.github/workflows/screenshots.yml +++ b/.github/workflows/screenshots.yml @@ -16,6 +16,10 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 10 if: ${{ !github.event.pull_request.draft }} + strategy: + fail-fast: false + matrix: + imgui_dep: ["imgui", ""] steps: - uses: actions/checkout@v4 with: @@ -34,21 +38,34 @@ jobs: # remove pygfx from install_requires, we install using pygfx@main sed -i "/pygfx/d" ./setup.py pip install git+https://github.com/pygfx/pygfx.git@main - pip install -e ".["tests"]" + - name: Install fastplotlib + run: | + # create string with one of: tests,imgui,notebook; test,imgui; test,notebook ; tests + # sed removes trailing comma + # install fastplotlib with with or without imgui depending on build matrix + pip install -e ".[$(echo "tests,notebook,${{ matrix.imgui_dep }}" | sed -e "s/,\+/,/g" -e "s/,$//")]" - name: Show wgpu backend run: python -c "from examples.tests.testutils import wgpu_backend; print(wgpu_backend)" - - name: Test examples + - name: Generate screenshots env: PYGFX_EXPECT_LAVAPIPE: true run: | # regenerate screenshots - REGENERATE_SCREENSHOTS=1 pytest -v examples - FASTPLOTLIB_NB_TESTS=1 REGENERATE_SCREENSHOTS=1 pytest --nbmake examples/notebooks/ - - uses: actions/upload-artifact@v3 + WGPU_FORCE_OFFSCREEN=1 REGENERATE_SCREENSHOTS=1 pytest -v examples + - name: Generate screenshots notebook, exclude image widget + env: + PYGFX_EXPECT_LAVAPIPE: true + run: FASTPLOTLIB_NB_TESTS=1 REGENERATE_SCREENSHOTS=1 pytest --nbmake $(find ./examples/notebooks/ -maxdepth 1 -type f -name "*.ipynb" ! -name "image_widget*.ipynb" -print | xargs) + - name: Generate screenshots notebook, include image widget + if: ${{ matrix.imgui_dep == 'imgui' }} + env: + PYGFX_EXPECT_LAVAPIPE: true + run: FASTPLOTLIB_NB_TESTS=1 REGENERATE_SCREENSHOTS=1 pytest --nbmake $(find ./examples/notebooks/ -maxdepth 1 -type f -name "image_widget*.ipynb" -print | xargs) + - uses: actions/upload-artifact@v4 if: always() with: - name: screenshots + name: screenshots-${{ matrix.imgui_dep }} path: | examples/screenshots/ examples/notebooks/screenshots/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6e1a5396c..bb786f9fd 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -63,7 +63,7 @@ cd fastplotlib ```bash # install all extras in place -pip install -e ".[notebook,docs,tests]" +pip install -e ".[imgui, notebook, docs, tests]" ``` 5. Add the upstream remote branch: @@ -280,7 +280,7 @@ to open source software packages. # after cloning cd fastplotlib # install dev dependencies -pip install -e ".[tests, docs, notebook]" +pip install -e ".[imgui, tests, docs, notebook]" ``` 3) Check out a feature branch from `main` diff --git a/examples/notebooks/nb_test_utils.py b/examples/notebooks/nb_test_utils.py index 3d9e50d34..fc74f5875 100644 --- a/examples/notebooks/nb_test_utils.py +++ b/examples/notebooks/nb_test_utils.py @@ -105,11 +105,21 @@ def plot_test(name, fig: fpl.Figure): def regenerate_screenshot(name, data): - iio.imwrite(SCREENSHOTS_DIR.joinpath(f"nb-{name}.png"), data) + if fpl.IMGUI: + prefix = "" + else: + prefix = "no-imgui-" + + iio.imwrite(SCREENSHOTS_DIR.joinpath(f"{prefix}nb-{name}.png"), data) def assert_screenshot_equal(name, data): - ground_truth = iio.imread(SCREENSHOTS_DIR.joinpath(f"nb-{name}.png")) + if fpl.IMGUI: + prefix = "" + else: + prefix = "no-imgui-" + + ground_truth = iio.imread(SCREENSHOTS_DIR.joinpath(f"{prefix}nb-{name}.png")) img = normalize_image(data) ref_img = normalize_image(ground_truth) @@ -140,9 +150,14 @@ def get_diffs_rgba(slicer): diffs_rgba = diffs_rgba.astype("u1") return diffs_rgba[..., slicer] + if fpl.IMGUI: + prefix = "" + else: + prefix = "no-imgui-" + # split into an rgb and an alpha diff diffs = { - DIFFS_DIR.joinpath(f"nb-diff-{name}-rgb.png"): slice(0, 3), + DIFFS_DIR.joinpath(f"{prefix}nb-diff-{name}-rgb.png"): slice(0, 3), } for path, slicer in diffs.items(): diff --git a/examples/notebooks/screenshots/no-imgui-nb-astronaut.png b/examples/notebooks/screenshots/no-imgui-nb-astronaut.png new file mode 100644 index 000000000..a1e524e2a --- /dev/null +++ b/examples/notebooks/screenshots/no-imgui-nb-astronaut.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:915f6c4695c932dc2aa467be750e58a0435fe86fe0e0fa5a52c6065e05ec3193 +size 85456 diff --git a/examples/notebooks/screenshots/no-imgui-nb-astronaut_RGB.png b/examples/notebooks/screenshots/no-imgui-nb-astronaut_RGB.png new file mode 100644 index 000000000..ec3208e01 --- /dev/null +++ b/examples/notebooks/screenshots/no-imgui-nb-astronaut_RGB.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:31cfa60229a4e297be507a8888e08d6950c2a7d4b323d34774c9462419272ada +size 84284 diff --git a/examples/notebooks/screenshots/no-imgui-nb-camera.png b/examples/notebooks/screenshots/no-imgui-nb-camera.png new file mode 100644 index 000000000..31b60d9c0 --- /dev/null +++ b/examples/notebooks/screenshots/no-imgui-nb-camera.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:800845fae18093945ed921237c8756b1afa31ee391fe679b03c57a67929e4ba9 +size 60087 diff --git a/examples/notebooks/screenshots/no-imgui-nb-lines-3d.png b/examples/notebooks/screenshots/no-imgui-nb-lines-3d.png new file mode 100644 index 000000000..35c777e6a --- /dev/null +++ b/examples/notebooks/screenshots/no-imgui-nb-lines-3d.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f4253362c0908e0d983542be3691a3d94f27a0319fb9e7183315c77891dac140 +size 23232 diff --git a/examples/notebooks/screenshots/no-imgui-nb-lines-colors.png b/examples/notebooks/screenshots/no-imgui-nb-lines-colors.png new file mode 100644 index 000000000..b8e34aab3 --- /dev/null +++ b/examples/notebooks/screenshots/no-imgui-nb-lines-colors.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dc95d6291d06ab64d142ba0048318caefa28b404bb4b31635df075dc651eaa08 +size 37276 diff --git a/examples/notebooks/screenshots/no-imgui-nb-lines-data.png b/examples/notebooks/screenshots/no-imgui-nb-lines-data.png new file mode 100644 index 000000000..8f58dbc6d --- /dev/null +++ b/examples/notebooks/screenshots/no-imgui-nb-lines-data.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b8aa0b8303f0a69609198ea312800fc0eb98007c18d0ebc37672a9cf4f1cbaff +size 46780 diff --git a/examples/notebooks/screenshots/no-imgui-nb-lines-underlay.png b/examples/notebooks/screenshots/no-imgui-nb-lines-underlay.png new file mode 100644 index 000000000..b33cde5a6 --- /dev/null +++ b/examples/notebooks/screenshots/no-imgui-nb-lines-underlay.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:822410f43d48d12e70930b5b581bafe624ea72475d53ca0d98cdaa5649338c63 +size 51849 diff --git a/examples/notebooks/screenshots/no-imgui-nb-lines.png b/examples/notebooks/screenshots/no-imgui-nb-lines.png new file mode 100644 index 000000000..5d7e704ca --- /dev/null +++ b/examples/notebooks/screenshots/no-imgui-nb-lines.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8e3ba744fcfa43df839fddce88f79fb8d7c5eafdd22f271e6b885e09b8891072 +size 31222 diff --git a/examples/notebooks/test_gc.ipynb b/examples/notebooks/test_gc.ipynb index 6a0130d8e..df08e7a2d 100644 --- a/examples/notebooks/test_gc.ipynb +++ b/examples/notebooks/test_gc.ipynb @@ -202,57 +202,27 @@ "metadata": {}, "outputs": [], "source": [ - "movies = [np.random.rand(100, 100, 100) for i in range(6)]\n", - "\n", - "iw = fpl.ImageWidget(movies)\n", - "\n", - "# add some events onto all the image graphics\n", - "for g in iw.managed_graphics:\n", - " for f in g._features:\n", - " g.add_event_handler(feature_changed_handler, f)\n", - "\n", - "iw.show()" - ] - }, - { - "cell_type": "markdown", - "id": "189bcd7a-40a2-4e84-abcf-c334e50f5544", - "metadata": {}, - "source": [ - "# Test that setting new data with different dims clears old ImageGraphics" + "if fpl.IMGUI:\n", + " # do image widget tests only if imgui is installed\n", + " movies = [np.random.rand(100, 100, 100) for i in range(6)]\n", + " \n", + " iw = fpl.ImageWidget(movies)\n", + " \n", + " # add some events onto all the image graphics\n", + " for g in iw.managed_graphics:\n", + " for f in g._features:\n", + " g.add_event_handler(feature_changed_handler, f)\n", + " \n", + " iw.show()\n", + " \n", + " old_graphics = [weakref.proxy(g) for g in iw.managed_graphics]\n", + " \n", + " # Test that setting new data with different dims clears old ImageGraphics\n", + " new_movies = [np.random.rand(100, 200, 200) for i in range(6)]\n", + " \n", + " iw.set_data(new_movies)\n", + " test_references(old_graphics)" ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7e855043-91c1-4f6c-bed3-b69cf4a87f84", - "metadata": {}, - "outputs": [], - "source": [ - "old_graphics = [weakref.proxy(g) for g in iw.managed_graphics]\n", - "\n", - "new_movies = [np.random.rand(100, 200, 200) for i in range(6)]\n", - "\n", - "iw.set_data(new_movies)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "59e3c193-5672-4a66-bdca-12f1dd675d32", - "metadata": {}, - "outputs": [], - "source": [ - "test_references(old_graphics)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ad3d2a24-88b3-4071-a49c-49667d5a7813", - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { diff --git a/examples/screenshots/no-imgui-gridplot.png b/examples/screenshots/no-imgui-gridplot.png new file mode 100644 index 000000000..45571161d --- /dev/null +++ b/examples/screenshots/no-imgui-gridplot.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a27ccf2230628980d16ab22a17df64504268da35a27cd1adb44102e64df033af +size 329247 diff --git a/examples/screenshots/no-imgui-gridplot_non_square.png b/examples/screenshots/no-imgui-gridplot_non_square.png new file mode 100644 index 000000000..f8c307c22 --- /dev/null +++ b/examples/screenshots/no-imgui-gridplot_non_square.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:58f50c4fc1b00c9e78c840193d1e15d008b9fe1e7f2a3d8b90065be91e2178f5 +size 236474 diff --git a/examples/screenshots/no-imgui-heatmap.png b/examples/screenshots/no-imgui-heatmap.png new file mode 100644 index 000000000..3d1cf5ef2 --- /dev/null +++ b/examples/screenshots/no-imgui-heatmap.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4fac55efd9339b180b9e34d5cf244c473d6439e57e34f272c1a7e59183f1afa2 +size 98573 diff --git a/examples/screenshots/no-imgui-image_cmap.png b/examples/screenshots/no-imgui-image_cmap.png new file mode 100644 index 000000000..6c565ca2b --- /dev/null +++ b/examples/screenshots/no-imgui-image_cmap.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:82f7176a61e2c6953c22171bea561845bb79cb8179d76b20eef2b2cc475bbb23 +size 237327 diff --git a/examples/screenshots/no-imgui-image_rgb.png b/examples/screenshots/no-imgui-image_rgb.png new file mode 100644 index 000000000..355238724 --- /dev/null +++ b/examples/screenshots/no-imgui-image_rgb.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fce532d713d2c664eb3b676e0128060ebf17241387134812b490d3ad398d42c2 +size 269508 diff --git a/examples/screenshots/no-imgui-image_rgbvminvmax.png b/examples/screenshots/no-imgui-image_rgbvminvmax.png new file mode 100644 index 000000000..6282f2438 --- /dev/null +++ b/examples/screenshots/no-imgui-image_rgbvminvmax.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:42e01469f0f7da37d3c1c90225bf7c03c44badd1f3612ac9bf88eaed5eeb6850 +size 50145 diff --git a/examples/screenshots/no-imgui-image_simple.png b/examples/screenshots/no-imgui-image_simple.png new file mode 100644 index 000000000..d00a166ce --- /dev/null +++ b/examples/screenshots/no-imgui-image_simple.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c8bb29f192617b9dde2490ce36c69bd8352b6ba5d068434bc53edaad91871356 +size 237960 diff --git a/examples/screenshots/no-imgui-image_small.png b/examples/screenshots/no-imgui-image_small.png new file mode 100644 index 000000000..aca14cd69 --- /dev/null +++ b/examples/screenshots/no-imgui-image_small.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e1ea4bcf76158169bc06973457ea09997c13ecd4a91e6e634566beb31348ef68 +size 13194 diff --git a/examples/screenshots/no-imgui-image_vminvmax.png b/examples/screenshots/no-imgui-image_vminvmax.png new file mode 100644 index 000000000..6282f2438 --- /dev/null +++ b/examples/screenshots/no-imgui-image_vminvmax.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:42e01469f0f7da37d3c1c90225bf7c03c44badd1f3612ac9bf88eaed5eeb6850 +size 50145 diff --git a/examples/screenshots/no-imgui-line.png b/examples/screenshots/no-imgui-line.png new file mode 100644 index 000000000..29610c612 --- /dev/null +++ b/examples/screenshots/no-imgui-line.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:709458b03d535bcf407fdae1720ccdcd11a5f79ccf673e85c7e64c5748f6d25e +size 173422 diff --git a/examples/screenshots/no-imgui-line_cmap.png b/examples/screenshots/no-imgui-line_cmap.png new file mode 100644 index 000000000..9340e191e --- /dev/null +++ b/examples/screenshots/no-imgui-line_cmap.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:69426f5aac61e59a08764626b2aded602e576479e652d76b6b3bf646e3218cc1 +size 48028 diff --git a/examples/screenshots/no-imgui-line_cmap_more.png b/examples/screenshots/no-imgui-line_cmap_more.png new file mode 100644 index 000000000..f0cea4ec1 --- /dev/null +++ b/examples/screenshots/no-imgui-line_cmap_more.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:df9a2ef9d54b417e0387116eb6e6215c54b7c939867d0d62c768768baae27e5f +size 129510 diff --git a/examples/screenshots/no-imgui-line_collection.png b/examples/screenshots/no-imgui-line_collection.png new file mode 100644 index 000000000..ca74d3362 --- /dev/null +++ b/examples/screenshots/no-imgui-line_collection.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:90f281301e8b23a22a5333e7b34316475907ac25ffc9a23b7395b7431c965343 +size 106518 diff --git a/examples/screenshots/no-imgui-line_collection_cmap_values.png b/examples/screenshots/no-imgui-line_collection_cmap_values.png new file mode 100644 index 000000000..df237aa1b --- /dev/null +++ b/examples/screenshots/no-imgui-line_collection_cmap_values.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3f5a7257d121a15a8a35ca6e9c70de9d6fbb4977221c840dd34e25e67136f4ea +size 67209 diff --git a/examples/screenshots/no-imgui-line_collection_cmap_values_qualitative.png b/examples/screenshots/no-imgui-line_collection_cmap_values_qualitative.png new file mode 100644 index 000000000..0347f7361 --- /dev/null +++ b/examples/screenshots/no-imgui-line_collection_cmap_values_qualitative.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:89a7bc62495e6454ee008e15f1504211777cc01e52f303c18f6068fd38ab3c12 +size 70090 diff --git a/examples/screenshots/no-imgui-line_collection_colors.png b/examples/screenshots/no-imgui-line_collection_colors.png new file mode 100644 index 000000000..dff4f83db --- /dev/null +++ b/examples/screenshots/no-imgui-line_collection_colors.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:78b14e90e5ae1e185abb51d94ac9d99c1a4318b0ddf79c26a55e6061f22c0ed9 +size 60447 diff --git a/examples/screenshots/no-imgui-line_collection_slicing.png b/examples/screenshots/no-imgui-line_collection_slicing.png new file mode 100644 index 000000000..70c343361 --- /dev/null +++ b/examples/screenshots/no-imgui-line_collection_slicing.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d6b4090d3ae9e38256c9f04e17bf2499f0a35348552f62e9c8d8dc97c9e760a7 +size 132125 diff --git a/examples/screenshots/no-imgui-line_colorslice.png b/examples/screenshots/no-imgui-line_colorslice.png new file mode 100644 index 000000000..3befac6da --- /dev/null +++ b/examples/screenshots/no-imgui-line_colorslice.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9f161ad7f351b56c988e1b27155e3963be5191dc09cbaa55615026d07df07334 +size 56338 diff --git a/examples/screenshots/no-imgui-line_dataslice.png b/examples/screenshots/no-imgui-line_dataslice.png new file mode 100644 index 000000000..957462d09 --- /dev/null +++ b/examples/screenshots/no-imgui-line_dataslice.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d2f737e0afd8f57c7d621197d37fcf30199086f6c083ec0d3d8e5497965e6d12 +size 67938 diff --git a/examples/screenshots/no-imgui-line_stack.png b/examples/screenshots/no-imgui-line_stack.png new file mode 100644 index 000000000..26f4a3af8 --- /dev/null +++ b/examples/screenshots/no-imgui-line_stack.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4dd69dc4be7a2283ec11a8427a75a2ddfe4be0cdbbdaedef3dcbf5f567c11ea7 +size 130519 diff --git a/examples/screenshots/no-imgui-linear_region_selectors_match_offsets.png b/examples/screenshots/no-imgui-linear_region_selectors_match_offsets.png new file mode 100644 index 000000000..9871d65c1 --- /dev/null +++ b/examples/screenshots/no-imgui-linear_region_selectors_match_offsets.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:747b0915eeaf5985346e3b6807a550da53b516769d2517d7c2e0f189baefef91 +size 100604 diff --git a/examples/screenshots/no-imgui-scatter_cmap_iris.png b/examples/screenshots/no-imgui-scatter_cmap_iris.png new file mode 100644 index 000000000..35812357a --- /dev/null +++ b/examples/screenshots/no-imgui-scatter_cmap_iris.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:74438dc47ff3fc1391b6952a52c66160fece0545de4ad40c13d3d56b2e093257 +size 59951 diff --git a/examples/screenshots/no-imgui-scatter_colorslice_iris.png b/examples/screenshots/no-imgui-scatter_colorslice_iris.png new file mode 100644 index 000000000..61812c8d7 --- /dev/null +++ b/examples/screenshots/no-imgui-scatter_colorslice_iris.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a02a21459deeca379a69b30054bebcc3739553b9d377d25b953315094e714d1a +size 35763 diff --git a/examples/screenshots/no-imgui-scatter_dataslice_iris.png b/examples/screenshots/no-imgui-scatter_dataslice_iris.png new file mode 100644 index 000000000..9ef39785c --- /dev/null +++ b/examples/screenshots/no-imgui-scatter_dataslice_iris.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:21ccf85a9242f6d7a724c38797688abd804d9a565e818b81ea0c8931aa05ca4e +size 38337 diff --git a/examples/screenshots/no-imgui-scatter_iris.png b/examples/screenshots/no-imgui-scatter_iris.png new file mode 100644 index 000000000..91dc29397 --- /dev/null +++ b/examples/screenshots/no-imgui-scatter_iris.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7ec960574580af159f3502da09f1f34e841267985edb52b89baf034c1d49125e +size 37410 diff --git a/examples/screenshots/no-imgui-scatter_size.png b/examples/screenshots/no-imgui-scatter_size.png new file mode 100644 index 000000000..6fadfec4d --- /dev/null +++ b/examples/screenshots/no-imgui-scatter_size.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:94b4b9d39f3d4ef2c46b6b4dd7f712ca612f31a7fc94ab5fad8015e48c637e91 +size 70290 diff --git a/examples/tests/test_examples.py b/examples/tests/test_examples.py index caa8e9dca..67519187b 100644 --- a/examples/tests/test_examples.py +++ b/examples/tests/test_examples.py @@ -10,6 +10,7 @@ import numpy as np import imageio.v3 as iio import pygfx +import fastplotlib as fpl MAX_TEXTURE_SIZE = 2048 pygfx.renderers.wgpu.set_wgpu_limits(**{"max-texture-dimension-2d": MAX_TEXTURE_SIZE}) @@ -35,9 +36,21 @@ examples_to_test = find_examples(query="# test_example = true") +def check_skip_imgui(module): + # skip any imgui or ImageWidget tests + with open(module, "r") as f: + contents = f.read() + if "ImageWidget" in contents: + pytest.skip("skipping ImageWidget tests since they require imgui") + elif "imgui" in contents or "imgui_bundle" in contents: + pytest.skip("skipping tests that require imgui") + + @pytest.mark.parametrize("module", examples_to_run, ids=lambda x: x.stem) def test_examples_run(module, force_offscreen): """Run every example marked to see if they run without error.""" + if not fpl.IMGUI: + check_skip_imgui(module) runpy.run_path(module, run_name="__main__") @@ -75,15 +88,20 @@ def import_from_path(module_name, filename): @pytest.mark.parametrize("module", examples_to_test, ids=lambda x: x.stem) def test_example_screenshots(module, force_offscreen): """Make sure that every example marked outputs the expected.""" + + if not fpl.IMGUI: + # skip any imgui or ImageWidget tests + check_skip_imgui(module) + # import the example module example = import_from_path(module.stem, module) - # there doesn't seem to be a resize event for the manual offscreen canvas - example.figure.imgui_renderer._backend.io.display_size = example.figure.canvas.get_logical_size() - - # run this once so any edge widgets set their sizes and therefore the subplots get the correct rect - # hacky but it works for now - example.figure.imgui_renderer.render() + if fpl.IMGUI: + # there doesn't seem to be a resize event for the manual offscreen canvas + example.figure.imgui_renderer._backend.io.display_size = example.figure.canvas.get_logical_size() + # run this once so any edge widgets set their sizes and therefore the subplots get the correct rect + # hacky but it works for now + example.figure.imgui_renderer.render() # render each subplot for subplot in example.figure: @@ -94,8 +112,9 @@ def test_example_screenshots(module, force_offscreen): # flush pygfx renderer example.figure.renderer.flush() - # render imgui - example.figure.imgui_renderer.render() + if fpl.IMGUI: + # render imgui + example.figure.imgui_renderer.render() # render a frame img = np.asarray(example.figure.renderer.target.draw()) @@ -107,7 +126,13 @@ def test_example_screenshots(module, force_offscreen): if not os.path.exists(screenshots_dir): os.mkdir(screenshots_dir) - screenshot_path = screenshots_dir / f"{module.stem}.png" + # test screenshots for both imgui and non-gui installs + if not fpl.IMGUI: + prefix = "no-imgui-" + else: + prefix = "" + + screenshot_path = screenshots_dir / f"{prefix}{module.stem}.png" black = np.zeros(img.shape).astype(np.uint8) black[:, :, -1] = 255 diff --git a/setup.py b/setup.py index a1c74d5e5..befed8333 100644 --- a/setup.py +++ b/setup.py @@ -16,45 +16,28 @@ "sphinx-gallery", "pydata-sphinx-theme", "glfw", - "jupyter-rfb>=0.4.1", # required so ImageWidget docs show up "ipywidgets>=8.0.0,<9", "sphinx-copybutton", "sphinx-design", "pandoc", - "jupyterlab", - "sidecar", "imageio[ffmpeg]", "matplotlib", "scikit-learn", - "imgui-bundle", ], "notebook": [ "jupyterlab", - "jupyter-rfb>=0.4.1", + "jupyter-rfb>=0.5.1", "ipywidgets>=8.0.0,<9", "sidecar", ], "tests": [ - "pytest<8.0.0", + "pytest", "nbmake", "black", "scipy", "imageio[ffmpeg]", - "jupyterlab", - "jupyter-rfb>=0.4.1", - "ipywidgets>=8.0.0,<9", - "scikit-learn", - "tqdm", - "sidecar", - "imgui-bundle", - ], - "tests-desktop": [ - "pytest<8.0.0", - "scipy", - "imageio[ffmpeg]", "scikit-learn", "tqdm", - "imgui-bundle", ], "imgui": ["imgui-bundle"], } From 959006e8d78eeeb49e5b311b1f06e83c60ee6568 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Tue, 21 Jan 2025 09:53:53 -0500 Subject: [PATCH 139/185] update screenshot (#700) * update screenshot * I am too restrictive lol * fine 25 min timeout --- .github/workflows/ci.yml | 2 +- .../notebooks/screenshots/nb-image-widget-movie-set_data.png | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 097a23475..472d81dd4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: test-build-full: name: Test Linux runs-on: ubuntu-latest - timeout-minutes: 10 + timeout-minutes: 25 if: ${{ !github.event.pull_request.draft }} strategy: fail-fast: false diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-set_data.png b/examples/notebooks/screenshots/nb-image-widget-movie-set_data.png index 8fdf2fd89..8c353442a 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-set_data.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-set_data.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1faa3db006aa7f9d41757564783cef67d1a906dc67bca045c2c30501a86306c2 -size 43947 +oid sha256:4d8563587c4f642d5e4edb34f41b569673d7ea71bcbafdb734369272776baeef +size 62316 From 570709b13be0eabc8e940440c8a4d12e537fe970 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Thu, 23 Jan 2025 10:56:11 -0500 Subject: [PATCH 140/185] Update ci step (#701) * Update ci.yml * Update screenshots.yml --- .github/workflows/ci.yml | 10 +++++----- .github/workflows/screenshots.yml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 472d81dd4..b12687a34 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,7 +37,7 @@ jobs: run: | sudo apt-get update -y -qq sudo apt-get install --no-install-recommends -y ffmpeg libegl1-mesa-dev libgl1-mesa-dri libxcb-xfixes0-dev mesa-vulkan-drivers xorg-dev - - name: Install dev dependencies + - name: Install pygx from main run: | python -m pip install --upgrade pip setuptools # remove pygfx from install_requires, we install using pygfx@main @@ -52,15 +52,15 @@ jobs: - name: Show wgpu backend run: python -c "from examples.tests.testutils import wgpu_backend; print(wgpu_backend)" - - name: fetch git lfs files + - name: Test components + env: + PYGFX_EXPECT_LAVAPIPE: true run: | - git lfs fetch --all - git lfs pull + WGPU_FORCE_OFFSCREEN=1 pytest -v tests/ - name: Test examples env: PYGFX_EXPECT_LAVAPIPE: true run: | - WGPU_FORCE_OFFSCREEN=1 pytest -v tests/ WGPU_FORCE_OFFSCREEN=1 pytest -v examples/ - name: Test examples notebooks, exclude ImageWidget notebook if: ${{ matrix.notebook_dep == 'notebook' }} diff --git a/.github/workflows/screenshots.yml b/.github/workflows/screenshots.yml index 80bf95201..c7f3add5e 100644 --- a/.github/workflows/screenshots.yml +++ b/.github/workflows/screenshots.yml @@ -32,7 +32,7 @@ jobs: run: | sudo apt-get update -y -qq sudo apt-get install --no-install-recommends -y ffmpeg libegl1-mesa-dev libgl1-mesa-dri libxcb-xfixes0-dev mesa-vulkan-drivers - - name: Install dev dependencies + - name: Install pygx from main run: | python -m pip install --upgrade pip setuptools # remove pygfx from install_requires, we install using pygfx@main From 9ea662f83a1b14bb142c49d09056105276052b66 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Sat, 25 Jan 2025 00:20:54 -0500 Subject: [PATCH 141/185] add job that uses release version of pygfx (#706) * add job that uses release version of pygfx * Update ci.yml * too complicated, just add pygfx release/main to matrix * other way around --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b12687a34..5b93cb317 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,6 +25,7 @@ jobs: python: ["3.11", "3.12", "3.13"] imgui_dep: ["imgui", ""] notebook_dep: ["notebook", ""] + pygfx_version: ["pygfx-release", "pygfx-main"] steps: - uses: actions/checkout@v4 with: @@ -38,6 +39,7 @@ jobs: sudo apt-get update -y -qq sudo apt-get install --no-install-recommends -y ffmpeg libegl1-mesa-dev libgl1-mesa-dri libxcb-xfixes0-dev mesa-vulkan-drivers xorg-dev - name: Install pygx from main + if: ${{ matrix.pygfx_version == 'pygfx-main' }} run: | python -m pip install --upgrade pip setuptools # remove pygfx from install_requires, we install using pygfx@main From 0336e261afb747d4ae02099ce890b108b5e6fd21 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Sat, 25 Jan 2025 13:05:02 -0500 Subject: [PATCH 142/185] add macos-latest to CI matrix (#707) * add macos-latest to CI matrix * Update ci.yml * fix quotes * Update ci.yml * Update ci.yml * Update ci.yml * Update ci.yml * remove expect lavapipe, not really necessary anymore * increase tolerance --- .github/workflows/ci.yml | 116 +++++----------------------- examples/notebooks/nb_test_utils.py | 2 +- 2 files changed, 22 insertions(+), 96 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5b93cb317..a312896ea 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,8 +15,7 @@ on: jobs: test-build-full: - name: Test Linux - runs-on: ubuntu-latest + name: Tests timeout-minutes: 25 if: ${{ !github.event.pull_request.draft }} strategy: @@ -26,6 +25,8 @@ jobs: imgui_dep: ["imgui", ""] notebook_dep: ["notebook", ""] pygfx_version: ["pygfx-release", "pygfx-main"] + os: ["ubuntu-latest", "macos-latest"] + runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 with: @@ -35,9 +36,19 @@ jobs: with: python-version: ${{ matrix.python }} - name: Install llvmpipe and lavapipe for offscreen canvas + if: ${{ matrix.os == 'ubuntu-latest' }} run: | sudo apt-get update -y -qq sudo apt-get install --no-install-recommends -y ffmpeg libegl1-mesa-dev libgl1-mesa-dri libxcb-xfixes0-dev mesa-vulkan-drivers xorg-dev + - name: Set up Homebrew + if: ${{ matrix.os == 'macos-latest' }} + id: set-up-homebrew + uses: Homebrew/actions/setup-homebrew@master + - name: Install gsed + if: ${{ matrix.os == 'macos-latest' }} + run: | + brew install gnu-sed + echo "/opt/homebrew/opt/gnu-sed/libexec/gnubin" >> "$GITHUB_PATH" - name: Install pygx from main if: ${{ matrix.pygfx_version == 'pygfx-main' }} run: | @@ -56,26 +67,26 @@ jobs: python -c "from examples.tests.testutils import wgpu_backend; print(wgpu_backend)" - name: Test components env: - PYGFX_EXPECT_LAVAPIPE: true + WGPU_FORCE_OFFSCREEN: 1 run: | - WGPU_FORCE_OFFSCREEN=1 pytest -v tests/ + pytest -v tests/ - name: Test examples env: - PYGFX_EXPECT_LAVAPIPE: true + WGPU_FORCE_OFFSCREEN: 1 run: | - WGPU_FORCE_OFFSCREEN=1 pytest -v examples/ + pytest -v examples/ - name: Test examples notebooks, exclude ImageWidget notebook if: ${{ matrix.notebook_dep == 'notebook' }} env: - PYGFX_EXPECT_LAVAPIPE: true + FASTPLOTLIB_NB_TESTS: 1 # test notebooks, exclude ImageWidget notebooks - run: FASTPLOTLIB_NB_TESTS=1 pytest --nbmake $(find ./examples/notebooks/ -maxdepth 1 -type f -name "*.ipynb" ! -name "image_widget*.ipynb" -print | xargs) + run: pytest --nbmake $(find ./examples/notebooks/ -maxdepth 1 -type f -name "*.ipynb" ! -name "image_widget*.ipynb" -print | xargs) - name: Test ImageWidget notebooks # test image widget notebooks only if imgui is installed if: ${{ matrix.notebook_dep == 'notebook' && matrix.imgui_dep == 'imgui' }} env: - PYGFX_EXPECT_LAVAPIPE: true - run: FASTPLOTLIB_NB_TESTS=1 pytest --nbmake $(find ./examples/notebooks/ -maxdepth 1 -type f -name "image_widget*.ipynb" -print | xargs) + FASTPLOTLIB_NB_TESTS: 1 + run: pytest --nbmake $(find ./examples/notebooks/ -maxdepth 1 -type f -name "image_widget*.ipynb" -print | xargs) - uses: actions/upload-artifact@v4 if: ${{ failure() }} with: @@ -83,88 +94,3 @@ jobs: path: | examples/diffs examples/notebooks/diffs - - -# test-build-full-mac: -# name: Test Mac, notebook + glfw -# runs-on: macos-14 -# if: ${{ !github.event.pull_request.draft }} -# strategy: -# fail-fast: false -# matrix: -# include: -# - name: Test py310 -# pyversion: '3.10' -# - name: Test py311 -# pyversion: '3.11' -# - name: Test py312 -# pyversion: '3.12' -# steps: -# - uses: actions/checkout@v3 -# with: -# lfs: true -# - name: Set up Python -# uses: actions/setup-python@v3 -# with: -# python-version: ${{ matrix.pyversion }} -# - name: Install dev dependencies -# run: | -# python -m pip install --upgrade pip setuptools -# # remove pygfx from install_requires, we install using pygfx@main -# pip install -e ".["tests"]" -# pip install git+https://github.com/pygfx/pygfx.git@main -# - name: Show wgpu backend -# run: -# python -c "from examples.tests.testutils import wgpu_backend; print(wgpu_backend)" -# - name: Test examples -# run: | -# pytest -v examples -# pytest --nbmake examples/notebooks/ -# - uses: actions/upload-artifact@v3 -# if: ${{ failure() }} -# with: -# name: screenshot-diffs -# path: | -# examples/desktop/diffs -# examples/notebooks/diffs -# -# test-build-glfw-mac: -# name: Test Mac, glfw -# runs-on: macos-14 -# if: ${{ !github.event.pull_request.draft }} -# strategy: -# fail-fast: false -# matrix: -# include: -# - name: Test py310 -# pyversion: '3.10' -# - name: Test py311 -# pyversion: '3.11' -# - name: Test py312 -# pyversion: '3.12' -# steps: -# - uses: actions/checkout@v3 -# with: -# lfs: true -# - name: Set up Python -# uses: actions/setup-python@v3 -# with: -# python-version: ${{ matrix.pyversion }} -# - name: Install dev dependencies -# run: | -# python -m pip install --upgrade pip setuptools -# # remove pygfx from install_requires, we install using pygfx@main -# pip install -e ".["tests-desktop"]" -# pip install git+https://github.com/pygfx/pygfx.git@main -# - name: Show wgpu backend -# run: -# python -c "from examples.tests.testutils import wgpu_backend; print(wgpu_backend)" -# - name: Test examples -# run: | -# pytest -v examples -# - uses: actions/upload-artifact@v3 -# if: ${{ failure() }} -# with: -# name: screenshot-diffs -# path: | -# examples/desktop/diffs diff --git a/examples/notebooks/nb_test_utils.py b/examples/notebooks/nb_test_utils.py index fc74f5875..e1c32e0a0 100644 --- a/examples/notebooks/nb_test_utils.py +++ b/examples/notebooks/nb_test_utils.py @@ -16,7 +16,7 @@ os.makedirs(SCREENSHOTS_DIR, exist_ok=True) os.makedirs(DIFFS_DIR, exist_ok=True) -TOLERANCE = 0.05 +TOLERANCE = 0.1 # store all the failures to allow the nb to proceed to test other examples FAILURES = list() From f3c78797cd99e40ddd1b21e85cbade19c2b50ac8 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Sat, 25 Jan 2025 21:15:23 -0500 Subject: [PATCH 143/185] Fix imgui popup menus (#695) * remove event filters attr * fix standard right click menu * fix colormap picker menu for imgui-bundle v1.6.0 * black --- fastplotlib/ui/_base.py | 2 -- .../ui/right_click_menus/_colormap_picker.py | 2 +- .../ui/right_click_menus/_standard_menu.py | 16 +++++++++------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/fastplotlib/ui/_base.py b/fastplotlib/ui/_base.py index 0abc81e88..6c134d415 100644 --- a/fastplotlib/ui/_base.py +++ b/fastplotlib/ui/_base.py @@ -248,8 +248,6 @@ def __init__(self, figure: Figure, *args, **kwargs): self._figure = figure self._fa_icons = self._figure._fa_icons - self._event_filter_names = set() - self.is_open = False def open(self, pos: tuple[int, int], *args, **kwargs): diff --git a/fastplotlib/ui/right_click_menus/_colormap_picker.py b/fastplotlib/ui/right_click_menus/_colormap_picker.py index 03176f673..3c48bd4d8 100644 --- a/fastplotlib/ui/right_click_menus/_colormap_picker.py +++ b/fastplotlib/ui/right_click_menus/_colormap_picker.py @@ -153,7 +153,7 @@ def update(self): * imgui.get_font().font_size ) - 2 - if imgui.menu_item("Reset vmin-vmax", None, False)[0]: + if imgui.menu_item("Reset vmin-vmax", "", False)[0]: self._lut_tool.image_graphic.reset_vmin_vmax() # add all the cmap options diff --git a/fastplotlib/ui/right_click_menus/_standard_menu.py b/fastplotlib/ui/right_click_menus/_standard_menu.py index cb1763d6d..9a584043c 100644 --- a/fastplotlib/ui/right_click_menus/_standard_menu.py +++ b/fastplotlib/ui/right_click_menus/_standard_menu.py @@ -82,14 +82,14 @@ def update(self): imgui.separator() # autoscale, center, maintain aspect - if imgui.menu_item(f"Autoscale", None, False)[0]: + if imgui.menu_item(f"Autoscale", "", False)[0]: self.get_subplot().auto_scale() - if imgui.menu_item(f"Center", None, False)[0]: + if imgui.menu_item(f"Center", "", False)[0]: self.get_subplot().center_scene() _, maintain_aspect = imgui.menu_item( - "Maintain Aspect", None, self.get_subplot().camera.maintain_aspect + "Maintain Aspect", "", self.get_subplot().camera.maintain_aspect ) self.get_subplot().camera.maintain_aspect = maintain_aspect @@ -98,7 +98,9 @@ def update(self): # toggles to flip axes cameras for axis in ["x", "y", "z"]: scale = getattr(self.get_subplot().camera.local, f"scale_{axis}") - changed, flip = imgui.menu_item(f"Flip {axis} axis", None, scale < 0) + changed, flip = imgui.menu_item( + f"Flip {axis} axis", "", bool(scale < 0) + ) if changed: flip_axis(self.get_subplot(), axis, flip) @@ -109,7 +111,7 @@ def update(self): for plane in ["xy", "xz", "yz"]: grid = getattr(self.get_subplot().axes.grids, plane) visible = grid.visible - changed, new_visible = imgui.menu_item(f"Grid {plane}", None, visible) + changed, new_visible = imgui.menu_item(f"Grid {plane}", "", visible) if changed: grid.visible = new_visible @@ -140,7 +142,7 @@ def update(self): # controller options if imgui.begin_menu("Controller"): _, enabled = imgui.menu_item( - "Enabled", None, self.get_subplot().controller.enabled + "Enabled", "", self.get_subplot().controller.enabled ) self.get_subplot().controller.enabled = enabled @@ -163,7 +165,7 @@ def update(self): clicked, _ = imgui.menu_item( label=name, - shortcut=None, + shortcut="", p_selected=current_type is controller_type_iter, ) From 0955078580fc2f135cdabe3ebaa3dfb77f528af6 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Sat, 25 Jan 2025 21:21:29 -0500 Subject: [PATCH 144/185] pin pygfx to v0.7.x for release (#708) --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index befed8333..14d0f0c5b 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ install_requires = [ "numpy>=1.23.0", - "pygfx>=0.5.0", + "pygfx~=0.7.0", "wgpu>=0.18.1", "cmap>=0.1.3", ] From a1a2c55c011d5e0a8540bc73d01ad686aec2df2f Mon Sep 17 00:00:00 2001 From: Caitlin Lewis <69729525+clewis7@users.noreply.github.com> Date: Sat, 25 Jan 2025 23:16:22 -0500 Subject: [PATCH 145/185] Update the README (#704) * start readme remake * move some stuff to faq * small edits * fix header * requested changes * fix dev installs * add gif * Update README.md * small changes * Update README.md --- README.md | 54 ++++++++++++++++++++++++---------- docs/source/user_guide/faq.rst | 8 +++-- 2 files changed, 45 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 9ae948cbf..5109d26aa 100644 --- a/README.md +++ b/README.md @@ -15,11 +15,28 @@ [**Examples**](https://github.com/kushalkolar/fastplotlib#examples) | [**Contributing**](https://github.com/kushalkolar/fastplotlib#heart-contributing) -Next-gen plotting library built using the [`pygfx`](https://github.com/pygfx/pygfx) rendering engine that can utilize [Vulkan](https://en.wikipedia.org/wiki/Vulkan), [DX12](https://en.wikipedia.org/wiki/DirectX#DirectX_12), or [Metal](https://developer.apple.com/metal/) via WGPU, so it is very fast! `fastplotlib` is an expressive plotting library that enables rapid prototyping for large scale exploratory scientific visualization. +Next-gen plotting library built using the [`pygfx`](https://github.com/pygfx/pygfx) rendering engine that utilizes [Vulkan](https://en.wikipedia.org/wiki/Vulkan), [DX12](https://en.wikipedia.org/wiki/DirectX#DirectX_12), or [Metal](https://developer.apple.com/metal/) via WGPU, so it is very fast! `fastplotlib` is an expressive plotting library that enables rapid prototyping for large scale exploratory scientific visualization. -> **Note** -> -> `fastplotlib` is currently in the **late alpha stage**, but you're welcome to use it or contribute! See our [Roadmap](https://github.com/kushalkolar/fastplotlib/issues/55). See this for a discussion on API stability: https://github.com/fastplotlib/fastplotlib/issues/121 +
+ +
+ +> **Note:** +> `fastplotlib` is currently in the **late alpha stage**, but you're welcome to use it or contribute! See our [Roadmap](https://github.com/kushalkolar/fastplotlib/issues/55). Also, see this for a discussion on API stability: https://github.com/fastplotlib/fastplotlib/issues/121 + +# What are *some* things I can do with `fastplotlib`? + +- GPU-accelerated visualization + +- interactive visualization via an intuitive and expressive API + +- rapid prototyping and algorithm design + +- easy exploration and fast rendering of large-scale data + +- design, develop, evaluate, and ship machine learning models + +- create visualizations for real-time acquisition systems for scientific instruments (cameras, etc.) # Supported frameworks @@ -30,11 +47,7 @@ Next-gen plotting library built using the [`pygfx`](https://github.com/pygfx/pyg :heavy_check_mark: `glfw`\ :heavy_check_mark: `wxPython` -**Notes:**\ -:heavy_check_mark: Non-blocking interactive Qt/PySide output is supported in ipython and notebooks, see the [interactive shells section in the user guide](http://fastplotlib.org/ver/dev/user_guide/guide.html#using-fastplotlib-in-an-interactive-shell) \ -:grey_exclamation: We do not officially support `jupyter notebook` through `jupyter_rfb`, this may change with notebook v7\ -:grey_exclamation: We only support jupyterlab for use in notebooks. This means that we do not support VSCode notebooks or any other python notebook platform. Jupyterlab is the most reliable way to use widget-based libraries in notebooks.\ -:disappointed: [`jupyter_rfb`](https://github.com/vispy/jupyter_rfb) does not work in collab, see https://github.com/vispy/jupyter_rfb/pull/77 +Write your code once and run it anywhere. Whether you are using `Qt`, `glfw`, `jupyter lab`, or doing offscreen rendering, `fastplotlib` works across all major platforms (Linux, Windows, Mac OS X) :smile: See the [FAQ](https://www.fastplotlib.org/ver/dev/user_guide/faq.html) for more details on where and how you can use `fastplotlib`. # Documentation @@ -63,7 +76,7 @@ pip install -U "fastplotlib[notebook]" We strongly recommend installing ``simplejpeg`` for use in notebooks, you must first install [libjpeg-turbo](https://libjpeg-turbo.org/) - If you use ``conda``, you can get ``libjpeg-turbo`` through conda. -- If you are on linux you can get it through your distro's package manager. +- If you are on linux, you can get it through your distro's package manager. - For Windows and Mac compiled binaries are available on their release page: https://github.com/libjpeg-turbo/libjpeg-turbo/releases Once you have ``libjpeg-turbo``: @@ -72,8 +85,7 @@ Once you have ``libjpeg-turbo``: pip install simplejpeg ``` -> **Note** -> +> **Note:** > `fastplotlib` and `pygfx` are fast evolving projects, the version available through pip might be outdated, you will need to follow the "For developers" instructions below if you want the latest features. You can find the release history here: https://github.com/fastplotlib/fastplotlib/releases ### For developers @@ -85,7 +97,7 @@ git clone https://github.com/fastplotlib/fastplotlib.git cd fastplotlib # install all extras in place -pip install -e ".[notebook,docs,tests]" +pip install -e ".[notebook,docs,tests,imgui]" # install latest pygfx pip install git+https://github.com/pygfx/pygfx.git@main @@ -105,7 +117,7 @@ User guide: http://fastplotlib.org/ver/dev/user_guide/guide.html The `quickstart.ipynb` tutorial notebook is a great way to get familiar with the API: https://github.com/fastplotlib/fastplotlib/tree/main/examples/notebooks/quickstart.ipynb -## GPU drivers and requirements +# GPU drivers and requirements Generally if your GPU is from 2017 or later it should be fine. Modern integrated graphics are usually fine for many use cases. The exact requirements will depend on how complex your visualization is and how many objects you need to render. @@ -113,8 +125,20 @@ More detailed information on GPUs and drivers is here: http://fastplotlib.org/ve For more detailed information, such as use on cloud computing infrastructure, see the WGPU docs: https://wgpu-py.readthedocs.io/en/stable/start.html#cloud-compute -# :heart: Contributing +# Contributing :heart: We welcome contributions! See the contributing guide: https://github.com/fastplotlib/fastplotlib/blob/main/CONTRIBUTING.md You can also take a look at our [**Roadmap for 2025**](https://github.com/fastplotlib/fastplotlib/issues/55) and [**Issues**](https://github.com/fastplotlib/fastplotlib/issues) for ideas on how to contribute! + +# Developers :brain: + +- [**Kushal Kolar**](https://github.com/kushalkolar) + +- [**Caitlin Lewis**](https://github.com/clewis7) + +- [**Almar Klein**](https://github.com/almarklein) + +- [**Amol Pasarkar**](https://github.com/apasarkar) + +A special thanks to all of the `pygfx` developers and the amazing work they have done. diff --git a/docs/source/user_guide/faq.rst b/docs/source/user_guide/faq.rst index ffa140ac2..029daabab 100644 --- a/docs/source/user_guide/faq.rst +++ b/docs/source/user_guide/faq.rst @@ -95,8 +95,12 @@ What frameworks does ``fastplotlib`` support? - `glfw` - `wxPython` - Note: Use in Google Colab is not highly functional. We recommend using an inexpensive alternative cloud provider - such as CodeOcean or Lambda Cloud. We have tested these and `fastplotlib` works very well. + Notes: + - `jupyter_rfb `_ does not work in Google Collab, see https://github.com/vispy/jupyter_rfb/pull/77 + - Non-blocking interactive Qt/PySide output is supported in ipython and notebooks, see the `interactive shells section in the user guide `_ + - We do not officially support `jupyter notebook` through `jupyter_rfb` and strongly recommend using `jupyter lab`. Jupyter Notebook v7 might work, but this has not been extensively tested. + - We only support `jupyterlab` for use in notebooks. This means that we do not support VSCode notebooks or any other python notebook platform. Jupyterlab is the most reliable way to use widget-based libraries in notebooks. + How can I use ``fastplotlib`` interactively? -------------------------------------------- From 497f5742c1528e8c2a8df1825bfd432efbb5d516 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Sat, 25 Jan 2025 23:51:02 -0500 Subject: [PATCH 146/185] separate CI workflow that uses release version of pygfx (#709) * Create ci-pygfx-release.yml * Update ci.yml * Update ci-pygfx-release.yml * Update CONTRIBUTING.md * Update CONTRIBUTING.md Co-authored-by: Caitlin Lewis <69729525+clewis7@users.noreply.github.com> --------- Co-authored-by: Caitlin Lewis <69729525+clewis7@users.noreply.github.com> --- .github/workflows/ci-pygfx-release.yml | 88 ++++++++++++++++++++++++++ .github/workflows/ci.yml | 2 - CONTRIBUTING.md | 5 +- 3 files changed, 92 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/ci-pygfx-release.yml diff --git a/.github/workflows/ci-pygfx-release.yml b/.github/workflows/ci-pygfx-release.yml new file mode 100644 index 000000000..e93f82fd5 --- /dev/null +++ b/.github/workflows/ci-pygfx-release.yml @@ -0,0 +1,88 @@ +name: CI + +on: + push: + branches: + - main + pull_request: + branches: + - main + types: + - opened + - reopened + - synchronize + - ready_for_review + +jobs: + test-build-full: + name: Tests - pygfx release + timeout-minutes: 25 + if: ${{ !github.event.pull_request.draft }} + strategy: + fail-fast: false + matrix: + python: ["3.11", "3.12", "3.13"] + imgui_dep: ["imgui", ""] + notebook_dep: ["notebook", ""] + os: ["ubuntu-latest", "macos-latest"] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + with: + lfs: true + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python }} + - name: Install llvmpipe and lavapipe for offscreen canvas + if: ${{ matrix.os == 'ubuntu-latest' }} + run: | + sudo apt-get update -y -qq + sudo apt-get install --no-install-recommends -y ffmpeg libegl1-mesa-dev libgl1-mesa-dri libxcb-xfixes0-dev mesa-vulkan-drivers xorg-dev + - name: Set up Homebrew + if: ${{ matrix.os == 'macos-latest' }} + id: set-up-homebrew + uses: Homebrew/actions/setup-homebrew@master + - name: Install gsed + if: ${{ matrix.os == 'macos-latest' }} + run: | + brew install gnu-sed + echo "/opt/homebrew/opt/gnu-sed/libexec/gnubin" >> "$GITHUB_PATH" + - name: Install fastplotlib + run: | + # create string with one of: tests,imgui,notebook; test,imgui; test,notebook ; tests + # sed removes trailing comma + # install fastplotlib with given extras options from above + pip install -e ".[$(echo "tests,${{ matrix.imgui_dep }},${{ matrix.notebook_dep }}" | sed -e "s/,\+/,/g" -e "s/,$//")]" + - name: Show wgpu backend + run: + python -c "from examples.tests.testutils import wgpu_backend; print(wgpu_backend)" + - name: Test components + env: + WGPU_FORCE_OFFSCREEN: 1 + run: | + pytest -v tests/ + - name: Test examples + env: + WGPU_FORCE_OFFSCREEN: 1 + run: | + pytest -v examples/ + - name: Test examples notebooks, exclude ImageWidget notebook + if: ${{ matrix.notebook_dep == 'notebook' }} + env: + FASTPLOTLIB_NB_TESTS: 1 + # test notebooks, exclude ImageWidget notebooks + run: pytest --nbmake $(find ./examples/notebooks/ -maxdepth 1 -type f -name "*.ipynb" ! -name "image_widget*.ipynb" -print | xargs) + - name: Test ImageWidget notebooks + # test image widget notebooks only if imgui is installed + if: ${{ matrix.notebook_dep == 'notebook' && matrix.imgui_dep == 'imgui' }} + env: + FASTPLOTLIB_NB_TESTS: 1 + run: pytest --nbmake $(find ./examples/notebooks/ -maxdepth 1 -type f -name "image_widget*.ipynb" -print | xargs) + - uses: actions/upload-artifact@v4 + if: ${{ failure() }} + with: + name: screenshot-diffs-${{ matrix.pyversion }}-${{ matrix.imgui_dep }}-${{ matrix.notebook_dep }} + path: | + examples/diffs + examples/notebooks/diffs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a312896ea..0f50b9623 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,6 @@ jobs: python: ["3.11", "3.12", "3.13"] imgui_dep: ["imgui", ""] notebook_dep: ["notebook", ""] - pygfx_version: ["pygfx-release", "pygfx-main"] os: ["ubuntu-latest", "macos-latest"] runs-on: ${{ matrix.os }} steps: @@ -50,7 +49,6 @@ jobs: brew install gnu-sed echo "/opt/homebrew/opt/gnu-sed/libexec/gnubin" >> "$GITHUB_PATH" - name: Install pygx from main - if: ${{ matrix.pygfx_version == 'pygfx-main' }} run: | python -m pip install --upgrade pip setuptools # remove pygfx from install_requires, we install using pygfx@main diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bb786f9fd..347275b6a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -304,5 +304,8 @@ FASTPLOTLIB_NB_TESTS=1 pytest --nbmake examples/notebooks/ 5) Update screenshots if necessary ([see testing](#testing-details)) -6) Push and open a draft PR against `main` +6) Push and open a PR (pull request) against the `main` branch +> **Note:** +> The tests labeled "CI / Tests" must pass, but the tests labeled "CI / Tests - pygfx release" do not necessarily need to pass. The difference between these two workflows is "CI / Tests" uses the `main` branch of [`pygfx`](https://github.com/pygfx/pygfx) whereas "CI / Tests - pygfx release" uses the latest release of `pygfx`. +> Since `fastplotlib`, `pygfx`, and `wgpu` are all under rapid development we aim to keep `fastplotlib` up to date with `pygfx@main` until `pygfx` stabilizes. The workflow "CI / Tests - pygfx release" is to inform us if any test failures using the release version of `pygfx` are a significant release blocker for `fastplotlib`. Once you make a PR we will help guide you through any failures with "CI / Tests - pygfx release"! From d4af1a900a8d7c89bcc25601badf1bf1a9a92ff2 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Sun, 26 Jan 2025 15:25:38 -0500 Subject: [PATCH 147/185] reorganized Figure export stuff (#710) --- fastplotlib/layouts/_figure.py | 203 ++++++--------------------------- 1 file changed, 37 insertions(+), 166 deletions(-) diff --git a/fastplotlib/layouts/_figure.py b/fastplotlib/layouts/_figure.py index 775d72dbf..70a4d41be 100644 --- a/fastplotlib/layouts/_figure.py +++ b/fastplotlib/layouts/_figure.py @@ -568,6 +568,37 @@ def clear(self): for subplot in self: subplot.clear() + def export_numpy(self, rgb: bool = False) -> np.ndarray: + """ + Export a snapshot of the Figure as numpy array. + + Parameters + ---------- + rgb: bool, default ``False`` + if True, use alpha blending to return an RGB image. + if False, returns an RGBA array + + Returns + ------- + np.ndarray + [n_rows, n_cols, 3] for RGB or [n_rows, n_cols, 4] for RGBA + """ + snapshot = self.renderer.snapshot() + + if rgb: + bg = np.zeros(snapshot.shape).astype(np.uint8) + bg[:, :, -1] = 255 + + img_alpha = snapshot[..., -1] / 255 + + rgb = snapshot[..., :-1] * img_alpha[..., None] + bg[..., :-1] * np.ones( + img_alpha.shape + )[..., None] * (1 - img_alpha[..., None]) + + return rgb.astype(np.uint8) + + return snapshot + def export(self, uri: str | Path | bytes, **kwargs): """ Use ``imageio`` for writing the current Figure to a file, or return a byte string. @@ -593,24 +624,18 @@ def export(self, uri: str | Path | bytes, **kwargs): "conda install -c conda-forge imageio\n" ) else: - snapshot = self.renderer.snapshot() - remove_alpha = True - # image formats that support alpha channel: # https://en.wikipedia.org/wiki/Alpha_compositing#Image_formats_supporting_alpha_channels alpha_support = [".png", ".exr", ".tiff", ".tif", ".gif", ".jxl", ".svg"] - if isinstance(uri, str): - if any([uri.endswith(ext) for ext in alpha_support]): - remove_alpha = False + uri = Path(uri) - elif isinstance(uri, Path): - if uri.suffix in alpha_support: - remove_alpha = False + if uri.suffix in alpha_support: + rgb = False + else: + rgb = True - if remove_alpha: - # remove alpha channel if it's not supported - snapshot = snapshot[..., :-1].shape + snapshot = self.export_numpy(rgb=rgb) return iio.imwrite(uri, snapshot, **kwargs) @@ -660,157 +685,3 @@ def __repr__(self): f"\t{newline.join(subplot.__str__() for subplot in self)}" f"\n" ) - - -class FigureRecorder: - def __init__(self, figure: Figure): - self._figure = figure - self._video_writer: VideoWriterAV = None - self._video_writer_queue = Queue() - self._record_fps = 25 - self._record_timer = 0 - self._record_start_time = 0 - - def _record(self): - """ - Sends frame to VideoWriter through video writer queue - """ - # current time - t = time() - - # put frame in queue only if enough time as passed according to the desired framerate - # otherwise it tries to record EVERY frame on every rendering cycle, which just blocks the rendering - if t - self._record_timer < (1 / self._record_fps): - return - - # reset timer - self._record_timer = t - - if self._video_writer is not None: - ss = self._figure.canvas.snapshot() - # exclude alpha channel - self._video_writer_queue.put(ss.data[..., :-1]) - - def start( - self, - path: str | Path, - fps: int = 25, - codec: str = "mpeg4", - pixel_format: str = "yuv420p", - options: dict = None, - ): - """ - Start a recording, experimental. Call ``record_end()`` to end a recording. - Note: playback duration does not exactly match recording duration. - - Requires PyAV: https://github.com/PyAV-Org/PyAV - - **Do not resize canvas during a recording, the width and height must remain constant!** - - Parameters - ---------- - path: str or Path - path to save the recording - - fps: int, default ``25`` - framerate, do not use > 25 within jupyter - - codec: str, default "mpeg4" - codec to use, see ``ffmpeg`` list: https://www.ffmpeg.org/ffmpeg-codecs.html . - In general, ``"mpeg4"`` should work on most systems. ``"libx264"`` is a - better option if you have it installed. - - pixel_format: str, default "yuv420p" - pixel format - - options: dict, optional - Codec options. For example, if using ``"mpeg4"`` you can use ``{"q:v": "20"}`` to set the quality between - 1-31, where "1" is highest and "31" is lowest. If using ``"libx264"``` you can use ``{"crf": "30"}`` where - the "crf" value is between "0" (highest quality) and "50" (lowest quality). See ``ffmpeg`` docs for more - info on codec options - - Examples - -------- - - With ``"mpeg4"`` - - .. code-block:: python - - # start recording video - figure.recorder.start("./video.mp4", options={"q:v": "20"} - - # do stuff like interacting with the plot, change things, etc. - - # end recording - figure.recorder.stop() - - With ``"libx264"`` - - .. code-block:: python - - # start recording video - figure.recorder.start("./vid_x264.mp4", codec="libx264", options={"crf": "25"}) - - # do stuff like interacting with the plot, change things, etc. - - # end recording - figure.recorder.stop() - - """ - - if Path(path).exists(): - raise FileExistsError(f"File already exists at given path: {path}") - - # queue for sending frames to VideoWriterAV process - self._video_writer_queue = Queue() - - # snapshot to get canvas width height - ss = self._figure.canvas.snapshot() - - # writer process - self._video_writer = VideoWriterAV( - path=str(path), - queue=self._video_writer_queue, - fps=int(fps), - width=ss.width, - height=ss.height, - codec=codec, - pixel_format=pixel_format, - options=options, - ) - - # start writer process - self._video_writer.start() - - # 1.3 seems to work well to reduce that difference between playback time and recording time - # will properly investigate later - self._record_fps = fps * 1.3 - self._record_start_time = time() - - # record timer used to maintain desired framerate - self._record_timer = time() - - self._figure.add_animations(self._record) - - def stop(self) -> float: - """ - End a current recording. Returns the real duration of the recording - - Returns - ------- - float - recording duration - """ - - # tell video writer that recording has finished - self._video_writer_queue.put(None) - - # wait for writer to finish - self._video_writer.join(timeout=5) - - self._video_writer = None - - # so self._record() is no longer called on every render cycle - self._figure.remove_animation(self._record) - - return time() - self._record_start_time From 75b2fecb32dc6773139db55f5faa9d18dd175129 Mon Sep 17 00:00:00 2001 From: Caitlin Lewis <69729525+clewis7@users.noreply.github.com> Date: Wed, 5 Feb 2025 14:17:55 -0500 Subject: [PATCH 148/185] solidify governance (#719) --- GOVERNANCE.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/GOVERNANCE.md b/GOVERNANCE.md index 59b844621..e7e4fc8f4 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -116,6 +116,7 @@ Governance decisions, meeting minutes, and voting outcomes are publicly document ## Changes to this governance document -### Until February 28, 2025 +**Effective until February 5, 2026** -During early stages of fastplotlib development, changes to the governance document may be made directly through unanimous approval by the original maintainers, Kushal Kolar & Caitlin Lewis. They (Kushal & Caitlin) may also add new members to the advisory committee through unanimous approval. +Moving forward, `fastplotlib` will maintain the governance model as outlined above. The core maintainers (Kushal Kolar & Caitlin Lewis) will revisit in +one year to propose any necessary changes to the governance structure. From ef4399dfeff5f7550391edb1e8d5b0195fae34b2 Mon Sep 17 00:00:00 2001 From: Amol Pasarkar Date: Wed, 5 Feb 2025 17:48:36 -0500 Subject: [PATCH 149/185] removes size attribute use for histogram component of imagewidget (#712) --- fastplotlib/utils/functions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastplotlib/utils/functions.py b/fastplotlib/utils/functions.py index 02dcd0572..910eba8e8 100644 --- a/fastplotlib/utils/functions.py +++ b/fastplotlib/utils/functions.py @@ -289,7 +289,7 @@ def quick_min_max(data: np.ndarray) -> tuple[float, float]: ): return data.min, data.max - while data.size > 1e6: + while np.prod(data.shape) > 1e6: ax = np.argmax(data.shape) sl = [slice(None)] * data.ndim sl[ax] = slice(None, None, 2) From e0fd9e4bf41e008f4ff3db73b3392035669309e5 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Thu, 6 Feb 2025 15:50:43 -0500 Subject: [PATCH 150/185] fit old rtd links in faq (#720) --- docs/source/user_guide/faq.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/user_guide/faq.rst b/docs/source/user_guide/faq.rst index 029daabab..0061a04d4 100644 --- a/docs/source/user_guide/faq.rst +++ b/docs/source/user_guide/faq.rst @@ -44,8 +44,8 @@ How does ``fastplotlib`` relate to ``matplotlib``? How can I learn to use ``fastplotlib``? --------------------------------------- - We want `fastplotlib` to be easy to learn and use. To get started with the library we recommend taking a look at our `guide `_ and - `examples gallery `_. + We want `fastplotlib` to be easy to learn and use. To get started with the library we recommend taking a look at our `guide `_ and + `examples gallery `_. In general, if you are familiar with numpy and array notation you will already have a intuitive understanding of interacting with your data in `fastplotlib`. If you have any questions, please do not hesitate to post an issue or discussion forum post. From 6c967a81c32ee0814b5cf1c3ec3c54fbd550a75f Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Thu, 13 Feb 2025 14:47:38 -0500 Subject: [PATCH 151/185] bump version to 0.4.0 (#713) * Update VERSION * remove pygfx version pin after release --- fastplotlib/VERSION | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fastplotlib/VERSION b/fastplotlib/VERSION index 0d91a54c7..1d0ba9ea1 100644 --- a/fastplotlib/VERSION +++ b/fastplotlib/VERSION @@ -1 +1 @@ -0.3.0 +0.4.0 diff --git a/setup.py b/setup.py index 14d0f0c5b..9834884aa 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ install_requires = [ "numpy>=1.23.0", - "pygfx~=0.7.0", + "pygfx>=0.7.0", "wgpu>=0.18.1", "cmap>=0.1.3", ] From 0097810061d67785fe9ac3bcd82450f90f994ee9 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Thu, 13 Feb 2025 14:48:18 -0500 Subject: [PATCH 152/185] Update `WGPU_FORCE_OFFSCREEN` to `RENDERCANVAS_FORCE_OFFSCREEN` (#723) * Update ci-pygfx-release.yml * Update ci.yml * Update screenshots.yml --- .github/workflows/ci-pygfx-release.yml | 4 ++-- .github/workflows/ci.yml | 4 ++-- .github/workflows/screenshots.yml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci-pygfx-release.yml b/.github/workflows/ci-pygfx-release.yml index e93f82fd5..5c50e44b8 100644 --- a/.github/workflows/ci-pygfx-release.yml +++ b/.github/workflows/ci-pygfx-release.yml @@ -59,12 +59,12 @@ jobs: python -c "from examples.tests.testutils import wgpu_backend; print(wgpu_backend)" - name: Test components env: - WGPU_FORCE_OFFSCREEN: 1 + RENDERCANVAS_FORCE_OFFSCREEN: 1 run: | pytest -v tests/ - name: Test examples env: - WGPU_FORCE_OFFSCREEN: 1 + RENDERCANVAS_FORCE_OFFSCREEN: 1 run: | pytest -v examples/ - name: Test examples notebooks, exclude ImageWidget notebook diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0f50b9623..61b12e02f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -65,12 +65,12 @@ jobs: python -c "from examples.tests.testutils import wgpu_backend; print(wgpu_backend)" - name: Test components env: - WGPU_FORCE_OFFSCREEN: 1 + RENDERCANVAS_FORCE_OFFSCREEN: 1 run: | pytest -v tests/ - name: Test examples env: - WGPU_FORCE_OFFSCREEN: 1 + RENDERCANVAS_FORCE_OFFSCREEN: 1 run: | pytest -v examples/ - name: Test examples notebooks, exclude ImageWidget notebook diff --git a/.github/workflows/screenshots.yml b/.github/workflows/screenshots.yml index c7f3add5e..0985fc179 100644 --- a/.github/workflows/screenshots.yml +++ b/.github/workflows/screenshots.yml @@ -52,7 +52,7 @@ jobs: PYGFX_EXPECT_LAVAPIPE: true run: | # regenerate screenshots - WGPU_FORCE_OFFSCREEN=1 REGENERATE_SCREENSHOTS=1 pytest -v examples + RENDERCANVAS_FORCE_OFFSCREEN=1 REGENERATE_SCREENSHOTS=1 pytest -v examples - name: Generate screenshots notebook, exclude image widget env: PYGFX_EXPECT_LAVAPIPE: true From c734f02d552154c195728539f04671569fa33f54 Mon Sep 17 00:00:00 2001 From: Flynn <75346097+FlynnOConnell@users.noreply.github.com> Date: Sun, 16 Feb 2025 04:37:45 -0500 Subject: [PATCH 153/185] Get nearest graphics indices (#699) * get_nearest_graphics_indices plot helper * map_screen_to_world can return None * black format source dir only --- fastplotlib/layouts/_plot_area.py | 2 +- fastplotlib/utils/_plot_helpers.py | 41 +++++++++++++++++++++++++----- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/fastplotlib/layouts/_plot_area.py b/fastplotlib/layouts/_plot_area.py index e096a7f21..a17c94d58 100644 --- a/fastplotlib/layouts/_plot_area.py +++ b/fastplotlib/layouts/_plot_area.py @@ -299,7 +299,7 @@ def get_rect(self) -> tuple[float, float, float, float]: def map_screen_to_world( self, pos: tuple[float, float] | pygfx.PointerEvent - ) -> np.ndarray: + ) -> np.ndarray | None: """ Map screen position to world position diff --git a/fastplotlib/utils/_plot_helpers.py b/fastplotlib/utils/_plot_helpers.py index ac0ff2cda..5a39b76d0 100644 --- a/fastplotlib/utils/_plot_helpers.py +++ b/fastplotlib/utils/_plot_helpers.py @@ -6,13 +6,14 @@ from ..graphics._collection_base import GraphicCollection -def get_nearest_graphics( +def get_nearest_graphics_indices( pos: tuple[float, float] | tuple[float, float, float], graphics: Sequence[Graphic] | GraphicCollection, -) -> np.ndarray[Graphic]: +) -> np.ndarray[int]: """ - Returns the nearest ``graphics`` to the passed position ``pos`` in world space. - Uses the distance between ``pos`` and the center of the bounding sphere for each graphic. + Returns indices of the nearest ``graphics`` to the passed position ``pos`` in world space + in order of closest to furtherst. Uses the distance between ``pos`` and the center of the + bounding sphere for each graphic. Parameters ---------- @@ -25,11 +26,10 @@ def get_nearest_graphics( Returns ------- - tuple[Graphic] - nearest graphics to ``pos`` in order + ndarray[int] + indices of the nearest nearest graphics to ``pos`` in order """ - if isinstance(graphics, GraphicCollection): graphics = graphics.graphics @@ -50,4 +50,31 @@ def get_nearest_graphics( distances = np.linalg.norm(centers[:, : len(pos)] - pos, ord=2, axis=1) sort_indices = np.argsort(distances) + return sort_indices + + +def get_nearest_graphics( + pos: tuple[float, float] | tuple[float, float, float], + graphics: Sequence[Graphic] | GraphicCollection, +) -> np.ndarray[Graphic]: + """ + Returns the nearest ``graphics`` to the passed position ``pos`` in world space. + Uses the distance between ``pos`` and the center of the bounding sphere for each graphic. + + Parameters + ---------- + pos: (x, y) | (x, y, z) + position in world space, z-axis is ignored when calculating L2 norms if ``pos`` is 2D + + graphics: Sequence, i.e. array, list, tuple, etc. of Graphic | GraphicCollection + the graphics from which to return a sorted array of graphics in order of closest + to furthest graphic + + Returns + ------- + ndarray[Graphic] + nearest graphics to ``pos`` in order + + """ + sort_indices = get_nearest_graphics_indices(pos, graphics) return np.asarray(graphics)[sort_indices] From a141f515e1b40bbb0e110456a2ddce3b4d2ab22e Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Fri, 21 Feb 2025 13:50:04 -0500 Subject: [PATCH 154/185] replace weird quotes, update GraphicMethodsMixin (#735) --- fastplotlib/graphics/line.py | 2 +- fastplotlib/graphics/scatter.py | 2 +- fastplotlib/layouts/_graphic_methods_mixin.py | 12 +++++++++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/fastplotlib/graphics/line.py b/fastplotlib/graphics/line.py index 8fe505ba9..489c64930 100644 --- a/fastplotlib/graphics/line.py +++ b/fastplotlib/graphics/line.py @@ -55,7 +55,7 @@ def __init__( if provided, these values are used to map the colors from the cmap size_space: str, default "screen" - coordinate space in which the size is expressed (‘screen’, ‘world’, ‘model’) + coordinate space in which the size is expressed ("screen", "world", "model") **kwargs passed to Graphic diff --git a/fastplotlib/graphics/scatter.py b/fastplotlib/graphics/scatter.py index 8dad7cd43..189af4844 100644 --- a/fastplotlib/graphics/scatter.py +++ b/fastplotlib/graphics/scatter.py @@ -62,7 +62,7 @@ def __init__( basically saves GPU VRAM when all scatter points are the same size size_space: str, default "screen" - coordinate space in which the size is expressed (‘screen’, ‘world’, ‘model’) + coordinate space in which the size is expressed ("screen", "world", "model") kwargs passed to Graphic diff --git a/fastplotlib/layouts/_graphic_methods_mixin.py b/fastplotlib/layouts/_graphic_methods_mixin.py index ea553f119..a08e9b110 100644 --- a/fastplotlib/layouts/_graphic_methods_mixin.py +++ b/fastplotlib/layouts/_graphic_methods_mixin.py @@ -45,7 +45,7 @@ def add_image( ---------- data: array-like array-like, usually numpy.ndarray, must support ``memoryview()`` - | shape must be ``[x_dim, y_dim]`` + | shape must be ``[n_rows, n_cols]``, ``[n_rows, n_cols, 3]`` for RGB or ``[n_rows, n_cols, 4]`` for RGBA vmin: int, optional minimum value for color scaling, calculated from data if not provided @@ -185,6 +185,7 @@ def add_line( cmap: str = None, cmap_transform: Union[numpy.ndarray, Iterable] = None, isolated_buffer: bool = True, + size_space: str = "screen", **kwargs ) -> LineGraphic: """ @@ -217,6 +218,9 @@ def add_line( cmap_transform: 1D array-like of numerical values, optional if provided, these values are used to map the colors from the cmap + size_space: str, default "screen" + coordinate space in which the size is expressed ("screen", "world", "model") + **kwargs passed to Graphic @@ -232,6 +236,7 @@ def add_line( cmap, cmap_transform, isolated_buffer, + size_space, **kwargs ) @@ -346,6 +351,7 @@ def add_scatter( isolated_buffer: bool = True, sizes: Union[float, numpy.ndarray, Iterable[float]] = 1, uniform_size: bool = False, + size_space: str = "screen", **kwargs ) -> ScatterGraphic: """ @@ -386,6 +392,9 @@ def add_scatter( if True, uses a uniform buffer for the scatter point sizes, basically saves GPU VRAM when all scatter points are the same size + size_space: str, default "screen" + coordinate space in which the size is expressed ("screen", "world", "model") + kwargs passed to Graphic @@ -402,6 +411,7 @@ def add_scatter( isolated_buffer, sizes, uniform_size, + size_space, **kwargs ) From cf5b11ba6944c4b360ad8c5c59c458310894254a Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Fri, 21 Feb 2025 13:54:08 -0500 Subject: [PATCH 155/185] move viewport rect logic from subplot and docks to Figure (#724) * move viewport rect logic from subplot and docks to Figure * progress * refactored rect code works well * add tests for viewport rects * figure refactor works, tested backend and passes * cleanup iw * update tests * update right click menu * update imgui figure * add gridplot viewport rect verification screenshots * black complains * remove cell * update docs * include OS in screenshot diff artifacts filename * comments * modify nb test, should work again now * try to make first render look right on github actions * update last groundtruth screenshot --- .github/workflows/ci-pygfx-release.yml | 2 +- .github/workflows/ci.yml | 2 +- docs/source/api/layouts/figure.rst | 5 +- docs/source/api/layouts/imgui_figure.rst | 5 +- docs/source/api/layouts/subplot.rst | 4 - examples/gridplot/gridplot_viewports_check.py | 37 ++ .../image_widget_viewports_check.py | 35 ++ examples/notebooks/nb_test_utils.py | 20 + examples/notebooks/quickstart.ipynb | 16 - .../screenshots/gridplot_viewports_check.png | 3 + .../image_widget_viewports_check.png | 3 + .../no-imgui-gridplot_viewports_check.png | 3 + examples/tests/test_examples.py | 7 +- fastplotlib/graphics/_axes.py | 2 +- fastplotlib/layouts/_figure.py | 419 ++++++++++++++---- fastplotlib/layouts/_imgui_figure.py | 23 +- fastplotlib/layouts/_plot_area.py | 31 +- fastplotlib/layouts/_subplot.py | 231 ++-------- fastplotlib/ui/_subplot_toolbar.py | 7 +- .../ui/right_click_menus/_standard_menu.py | 9 +- fastplotlib/widgets/image_widget/_widget.py | 11 +- 21 files changed, 478 insertions(+), 397 deletions(-) create mode 100644 examples/gridplot/gridplot_viewports_check.py create mode 100644 examples/image_widget/image_widget_viewports_check.py create mode 100644 examples/screenshots/gridplot_viewports_check.png create mode 100644 examples/screenshots/image_widget_viewports_check.png create mode 100644 examples/screenshots/no-imgui-gridplot_viewports_check.png diff --git a/.github/workflows/ci-pygfx-release.yml b/.github/workflows/ci-pygfx-release.yml index 5c50e44b8..87ed1a113 100644 --- a/.github/workflows/ci-pygfx-release.yml +++ b/.github/workflows/ci-pygfx-release.yml @@ -82,7 +82,7 @@ jobs: - uses: actions/upload-artifact@v4 if: ${{ failure() }} with: - name: screenshot-diffs-${{ matrix.pyversion }}-${{ matrix.imgui_dep }}-${{ matrix.notebook_dep }} + name: screenshot-diffs-${{ matrix.os }}-${{ matrix.pyversion }}-${{ matrix.imgui_dep }}-${{ matrix.notebook_dep }} path: | examples/diffs examples/notebooks/diffs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 61b12e02f..0274add7d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -88,7 +88,7 @@ jobs: - uses: actions/upload-artifact@v4 if: ${{ failure() }} with: - name: screenshot-diffs-${{ matrix.pyversion }}-${{ matrix.imgui_dep }}-${{ matrix.notebook_dep }} + name: screenshot-diffs-${{ matrix.os }}-${{ matrix.pyversion }}-${{ matrix.imgui_dep }}-${{ matrix.notebook_dep }} path: | examples/diffs examples/notebooks/diffs diff --git a/docs/source/api/layouts/figure.rst b/docs/source/api/layouts/figure.rst index 17ee965b6..3d6c745e9 100644 --- a/docs/source/api/layouts/figure.rst +++ b/docs/source/api/layouts/figure.rst @@ -23,9 +23,11 @@ Properties Figure.cameras Figure.canvas Figure.controllers + Figure.mode Figure.names Figure.renderer Figure.shape + Figure.spacing Methods ~~~~~~~ @@ -36,10 +38,9 @@ Methods Figure.clear Figure.close Figure.export + Figure.export_numpy Figure.get_pygfx_render_area Figure.open_popup Figure.remove_animation - Figure.render Figure.show - Figure.start_render diff --git a/docs/source/api/layouts/imgui_figure.rst b/docs/source/api/layouts/imgui_figure.rst index 38a546ae9..6d6bb2dd4 100644 --- a/docs/source/api/layouts/imgui_figure.rst +++ b/docs/source/api/layouts/imgui_figure.rst @@ -25,9 +25,11 @@ Properties ImguiFigure.controllers ImguiFigure.guis ImguiFigure.imgui_renderer + ImguiFigure.mode ImguiFigure.names ImguiFigure.renderer ImguiFigure.shape + ImguiFigure.spacing Methods ~~~~~~~ @@ -39,11 +41,10 @@ Methods ImguiFigure.clear ImguiFigure.close ImguiFigure.export + ImguiFigure.export_numpy ImguiFigure.get_pygfx_render_area ImguiFigure.open_popup ImguiFigure.register_popup ImguiFigure.remove_animation - ImguiFigure.render ImguiFigure.show - ImguiFigure.start_render diff --git a/docs/source/api/layouts/subplot.rst b/docs/source/api/layouts/subplot.rst index 3de44222d..1cf9be31c 100644 --- a/docs/source/api/layouts/subplot.rst +++ b/docs/source/api/layouts/subplot.rst @@ -31,7 +31,6 @@ Properties Subplot.name Subplot.objects Subplot.parent - Subplot.position Subplot.renderer Subplot.scene Subplot.selectors @@ -58,12 +57,9 @@ Methods Subplot.clear Subplot.delete_graphic Subplot.get_figure - Subplot.get_rect Subplot.insert_graphic Subplot.map_screen_to_world Subplot.remove_animation Subplot.remove_graphic - Subplot.render Subplot.set_title - Subplot.set_viewport_rect diff --git a/examples/gridplot/gridplot_viewports_check.py b/examples/gridplot/gridplot_viewports_check.py new file mode 100644 index 000000000..99584b411 --- /dev/null +++ b/examples/gridplot/gridplot_viewports_check.py @@ -0,0 +1,37 @@ +""" +GridPlot test viewport rects +============================ + +Test figure to test that viewport rects are positioned correctly +""" + +# test_example = true +# sphinx_gallery_pygfx_docs = 'hidden' + +import fastplotlib as fpl +import numpy as np + + +figure = fpl.Figure( + shape=(2, 3), + size=(700, 560), + names=list(map(str, range(6))) +) + +np.random.seed(0) +a = np.random.rand(6, 10, 10) + +for data, subplot in zip(a, figure): + subplot.add_image(data) + subplot.docks["left"].size = 20 + subplot.docks["right"].size = 30 + subplot.docks["bottom"].size = 40 + +figure.show() + + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.loop.run() diff --git a/examples/image_widget/image_widget_viewports_check.py b/examples/image_widget/image_widget_viewports_check.py new file mode 100644 index 000000000..057134341 --- /dev/null +++ b/examples/image_widget/image_widget_viewports_check.py @@ -0,0 +1,35 @@ +""" +ImageWidget test viewport rects +=============================== + +Test Figure to test that viewport rects are positioned correctly in an image widget +""" + +# test_example = true +# sphinx_gallery_pygfx_docs = 'hidden' + +import fastplotlib as fpl +import numpy as np + +np.random.seed(0) +a = np.random.rand(6, 15, 10, 10) + +iw = fpl.ImageWidget( + data=[img for img in a], + names=list(map(str, range(6))), + figure_kwargs={"size": (700, 560)}, +) + +for subplot in iw.figure: + subplot.docks["left"].size = 10 + subplot.docks["bottom"].size = 40 + +iw.show() + +figure = iw.figure + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.loop.run() diff --git a/examples/notebooks/nb_test_utils.py b/examples/notebooks/nb_test_utils.py index e1c32e0a0..f1505f98a 100644 --- a/examples/notebooks/nb_test_utils.py +++ b/examples/notebooks/nb_test_utils.py @@ -94,6 +94,26 @@ def plot_test(name, fig: fpl.Figure): if not TESTING: return + # otherwise the first render is wrong + if fpl.IMGUI: + # there doesn't seem to be a resize event for the manual offscreen canvas + fig.imgui_renderer._backend.io.display_size = fig.canvas.get_logical_size() + # run this once so any edge widgets set their sizes and therefore the subplots get the correct rect + # hacky but it works for now + fig.imgui_renderer.render() + + fig._set_viewport_rects() + # render each subplot + for subplot in fig: + subplot.viewport.render(subplot.scene, subplot.camera) + + # flush pygfx renderer + fig.renderer.flush() + + if fpl.IMGUI: + # render imgui + fig.imgui_renderer.render() + snapshot = fig.canvas.snapshot() rgb_img = rgba_to_rgb(snapshot.data) diff --git a/examples/notebooks/quickstart.ipynb b/examples/notebooks/quickstart.ipynb index 09317110d..737aee3e7 100644 --- a/examples/notebooks/quickstart.ipynb +++ b/examples/notebooks/quickstart.ipynb @@ -1695,22 +1695,6 @@ "figure_grid[\"top-right-plot\"]" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "cb7566a5", - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false - } - }, - "outputs": [], - "source": [ - "# view its position\n", - "figure_grid[\"top-right-plot\"].position" - ] - }, { "cell_type": "code", "execution_count": null, diff --git a/examples/screenshots/gridplot_viewports_check.png b/examples/screenshots/gridplot_viewports_check.png new file mode 100644 index 000000000..050067e22 --- /dev/null +++ b/examples/screenshots/gridplot_viewports_check.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:250959179b0f998e1c586951864e9cbce3ac63bf6d2e12a680a47b9b1be061a1 +size 46456 diff --git a/examples/screenshots/image_widget_viewports_check.png b/examples/screenshots/image_widget_viewports_check.png new file mode 100644 index 000000000..6bfbc0153 --- /dev/null +++ b/examples/screenshots/image_widget_viewports_check.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:27e8aaab0085d15965649f0a4b367e313bab382c13b39de0354d321398565a46 +size 99567 diff --git a/examples/screenshots/no-imgui-gridplot_viewports_check.png b/examples/screenshots/no-imgui-gridplot_viewports_check.png new file mode 100644 index 000000000..8dea071d0 --- /dev/null +++ b/examples/screenshots/no-imgui-gridplot_viewports_check.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0cda256658e84b14b48bf5151990c828092ff461f394fb9e54341ab601918aa1 +size 45113 diff --git a/examples/tests/test_examples.py b/examples/tests/test_examples.py index 67519187b..d5f3e8ab9 100644 --- a/examples/tests/test_examples.py +++ b/examples/tests/test_examples.py @@ -58,11 +58,11 @@ def test_examples_run(module, force_offscreen): @pytest.fixture def force_offscreen(): """Force the offscreen canvas to be selected by the auto gui module.""" - os.environ["WGPU_FORCE_OFFSCREEN"] = "true" + os.environ["RENDERCANVAS_FORCE_OFFSCREEN"] = "true" try: yield finally: - del os.environ["WGPU_FORCE_OFFSCREEN"] + del os.environ["RENDERCANVAS_FORCE_OFFSCREEN"] def test_that_we_are_on_lavapipe(): @@ -103,11 +103,10 @@ def test_example_screenshots(module, force_offscreen): # hacky but it works for now example.figure.imgui_renderer.render() + example.figure._set_viewport_rects() # render each subplot for subplot in example.figure: subplot.viewport.render(subplot.scene, subplot.camera) - for dock in subplot.docks.values(): - dock.set_viewport_rect() # flush pygfx renderer example.figure.renderer.flush() diff --git a/fastplotlib/graphics/_axes.py b/fastplotlib/graphics/_axes.py index 9541dceeb..4938b1a97 100644 --- a/fastplotlib/graphics/_axes.py +++ b/fastplotlib/graphics/_axes.py @@ -516,7 +516,7 @@ def update_using_camera(self): return if self._plot_area.camera.fov == 0: - xpos, ypos, width, height = self._plot_area.get_rect() + xpos, ypos, width, height = self._plot_area.viewport.rect # orthographic projection, get ranges using inverse # get range of screen space by getting the corners diff --git a/fastplotlib/layouts/_figure.py b/fastplotlib/layouts/_figure.py index 70a4d41be..5f253b82f 100644 --- a/fastplotlib/layouts/_figure.py +++ b/fastplotlib/layouts/_figure.py @@ -20,10 +20,14 @@ from .. import ImageGraphic +# number of pixels taken by the imgui toolbar when present +IMGUI_TOOLBAR_HEIGHT = 39 + + class Figure: def __init__( self, - shape: tuple[int, int] = (1, 1), + shape: list[tuple[int, int, int, int]] | tuple[int, int] = (1, 1), cameras: ( Literal["2d", "3d"] | Iterable[Iterable[Literal["2d", "3d"]]] @@ -51,8 +55,8 @@ def __init__( Parameters ---------- - shape: (int, int), default (1, 1) - (n_rows, n_cols) + shape: list[tuple[int, int, int, int]] | tuple[int, int], default (1, 1) + grid of shape [n_rows, n_cols] or list of bounding boxes: [x, y, width, height] (NOT YET IMPLEMENTED) cameras: "2d", "3", list of "2d" | "3d", Iterable of camera instances, or Iterable of "2d" | "3d", optional | if str, one of ``"2d"`` or ``"3d"`` indicating 2D or 3D cameras for all subplots @@ -69,7 +73,6 @@ def __init__( controller_ids: str, list of int, np.ndarray of int, or list with sublists of subplot str names, optional | If `None` a unique controller is created for each subplot | If "sync" all the subplots use the same controller - | If array/list it must be reshapeable to ``grid_shape``. This allows custom assignment of controllers @@ -97,15 +100,47 @@ def __init__( subplot names """ + if isinstance(shape, list): + raise NotImplementedError("bounding boxes for shape not yet implemented") + if not all(isinstance(v, (tuple, list)) for v in shape): + raise TypeError( + "shape argument must be a list of bounding boxes or a tuple[n_rows, n_cols]" + ) + for item in shape: + if not all(isinstance(v, (int, np.integer)) for v in item): + raise TypeError( + "shape argument must be a list of bounding boxes or a tuple[n_rows, n_cols]" + ) + # constant that sets the Figure to be in "rect" mode + self._mode: str = "rect" + + elif isinstance(shape, tuple): + if not all(isinstance(v, (int, np.integer)) for v in shape): + raise TypeError( + "shape argument must be a list of bounding boxes or a tuple[n_rows, n_cols]" + ) + # constant that sets the Figure to be in "grid" mode + self._mode: str = "grid" + + # shape is [n_subplots, row_col_index] + self._subplot_grid_positions: dict[Subplot, tuple[int, int]] = dict() + + else: + raise TypeError( + "shape argument must be a list of bounding boxes or a tuple[n_rows, n_cols]" + ) + self._shape = shape + # default spacing of 2 pixels between subplots + self._spacing = 2 + if names is not None: - if len(list(chain(*names))) != len(self): + subplot_names = np.asarray(names).flatten() + if subplot_names.size != len(self): raise ValueError( "must provide same number of subplot `names` as specified by Figure `shape`" ) - - subplot_names = np.asarray(names).reshape(self.shape) else: subplot_names = None @@ -113,29 +148,30 @@ def __init__( canvas, renderer, canvas_kwargs={"size": size} ) + canvas.add_event_handler(self._set_viewport_rects, "resize") + if isinstance(cameras, str): # create the array representing the views for each subplot in the grid - cameras = np.array([cameras] * len(self)).reshape(self.shape) + cameras = np.array([cameras] * len(self)) - # list -> array if necessary - cameras = np.asarray(cameras).reshape(self.shape) + # list/tuple -> array if necessary + cameras = np.asarray(cameras).flatten() - if cameras.shape != self.shape: - raise ValueError("Number of cameras does not match the number of subplots") + if cameras.size != len(self): + raise ValueError( + f"Number of cameras: {cameras.size} does not match the number of subplots: {len(self)}" + ) # create the cameras - subplot_cameras = np.empty(self.shape, dtype=object) - for i, j in product(range(self.shape[0]), range(self.shape[1])): - subplot_cameras[i, j] = create_camera(camera_type=cameras[i, j]) + subplot_cameras = np.empty(len(self), dtype=object) + for index in range(len(self)): + subplot_cameras[index] = create_camera(camera_type=cameras[index]) # if controller instances have been specified for each subplot if controllers is not None: - # one controller for all subplots if isinstance(controllers, pygfx.Controller): controllers = [controllers] * len(self) - # subplot_controllers[:] = controllers - # # subplot_controllers = np.asarray([controllers] * len(self), dtype=object) # individual controller instance specified for each subplot else: @@ -152,32 +188,28 @@ def __init__( "pygfx.Controller instances" ) - try: - controllers = np.asarray(controllers).reshape(shape) - except ValueError: + subplot_controllers: np.ndarray[pygfx.Controller] = np.asarray( + controllers + ).flatten() + if not subplot_controllers.size == len(self): raise ValueError( f"number of controllers passed must be the same as the number of subplots specified " - f"by shape: {self.shape}. You have passed: <{controllers.size}> controllers" + f"by shape: {len(self)}. You have passed: {subplot_controllers.size} controllers" ) from None - subplot_controllers: np.ndarray[pygfx.Controller] = np.empty( - self.shape, dtype=object - ) - - for i, j in product(range(self.shape[0]), range(self.shape[1])): - subplot_controllers[i, j] = controllers[i, j] - subplot_controllers[i, j].add_camera(subplot_cameras[i, j]) + for index in range(len(self)): + subplot_controllers[index].add_camera(subplot_cameras[index]) - # parse controller_ids and controller_types to make desired controller for each supblot + # parse controller_ids and controller_types to make desired controller for each subplot else: if controller_ids is None: # individual controller for each subplot - controller_ids = np.arange(len(self)).reshape(self.shape) + controller_ids = np.arange(len(self)) elif isinstance(controller_ids, str): if controller_ids == "sync": - # this will eventually make one controller for all subplots - controller_ids = np.zeros(self.shape, dtype=int) + # this will end up creating one controller to control the camera of every subplot + controller_ids = np.zeros(len(self), dtype=int) else: raise ValueError( f"`controller_ids` must be one of 'sync', an array/list of subplot names, or an array/list of " @@ -207,20 +239,24 @@ def __init__( ) # initialize controller_ids array - ids_init = np.arange(len(self)).reshape(self.shape) + ids_init = np.arange(len(self)) # set id based on subplot position for each synced sublist - for i, sublist in enumerate(controller_ids): + for row_ix, sublist in enumerate(controller_ids): for name in sublist: ids_init[subplot_names == name] = -( - i + 1 - ) # use negative numbers because why not + row_ix + 1 + ) # use negative numbers to avoid collision with positive numbers from np.arange controller_ids = ids_init # integer ids elif all([isinstance(item, (int, np.integer)) for item in ids_flat]): - controller_ids = np.asarray(controller_ids).reshape(self.shape) + controller_ids = np.asarray(controller_ids).flatten() + if controller_ids.max() < 0: + raise ValueError( + "if passing an integer array of `controller_ids`, all the integers must be positive." + ) else: raise TypeError( @@ -228,25 +264,27 @@ def __init__( f"you have passed: {controller_ids}" ) - if controller_ids.shape != self.shape: + if controller_ids.size != len(self): raise ValueError( "Number of controller_ids does not match the number of subplots" ) if controller_types is None: # `create_controller()` will auto-determine controller for each subplot based on defaults - controller_types = np.array(["default"] * len(self)).reshape(self.shape) + controller_types = np.array(["default"] * len(self)) # valid controller types if isinstance(controller_types, str): - controller_types = [[controller_types]] + controller_types = np.array([controller_types] * len(self)) - types_flat = list(chain(*controller_types)) + controller_types: np.ndarray[pygfx.Controller] = np.asarray( + controller_types + ).flatten() # str controller_type or pygfx instances valid_str = list(valid_controller_types.keys()) + ["default"] # make sure each controller type is valid - for controller_type in types_flat: + for controller_type in controller_types: if controller_type is None: continue @@ -256,12 +294,8 @@ def __init__( f"Valid `controller_types` arguments are:\n {valid_str}" ) - controller_types: np.ndarray[pygfx.Controller] = np.asarray( - controller_types - ).reshape(self.shape) - # make the real controllers for each subplot - subplot_controllers = np.empty(shape=self.shape, dtype=object) + subplot_controllers = np.empty(shape=len(self), dtype=object) for cid in np.unique(controller_ids): cont_type = controller_types[controller_ids == cid] if np.unique(cont_type).size > 1: @@ -292,32 +326,34 @@ def __init__( self._canvas = canvas self._renderer = renderer - nrows, ncols = self.shape + if self.mode == "grid": + nrows, ncols = self.shape - self._subplots: np.ndarray[Subplot] = np.ndarray( - shape=(nrows, ncols), dtype=object - ) + self._subplots: np.ndarray[Subplot] = np.ndarray( + shape=(nrows, ncols), dtype=object + ) - for i, j in self._get_iterator(): - position = (i, j) - camera = subplot_cameras[i, j] - controller = subplot_controllers[i, j] + for i, (row_ix, col_ix) in enumerate(product(range(nrows), range(ncols))): + camera = subplot_cameras[i] + controller = subplot_controllers[i] - if subplot_names is not None: - name = subplot_names[i, j] - else: - name = None - - self._subplots[i, j] = Subplot( - parent=self, - position=position, - parent_dims=(nrows, ncols), - camera=camera, - controller=controller, - canvas=canvas, - renderer=renderer, - name=name, - ) + if subplot_names is not None: + name = subplot_names[i] + else: + name = None + + subplot = Subplot( + parent=self, + camera=camera, + controller=controller, + canvas=canvas, + renderer=renderer, + name=name, + ) + + self._subplots[row_ix, col_ix] = subplot + + self._subplot_grid_positions[subplot] = (row_ix, col_ix) self._animate_funcs_pre: list[callable] = list() self._animate_funcs_post: list[callable] = list() @@ -328,11 +364,37 @@ def __init__( self._output = None + self._pause_render = False + @property - def shape(self) -> tuple[int, int]: + def shape(self) -> list[tuple[int, int, int, int]] | tuple[int, int]: """[n_rows, n_cols]""" return self._shape + @property + def mode(self) -> str: + """ + one of 'grid' or 'rect' + + Used by Figure to determine certain aspects, such as how to calculate + rects and shapes of properties for cameras, controllers, and subplots arrays + """ + return self._mode + + @property + def spacing(self) -> int: + """spacing between subplots, in pixels""" + return self._spacing + + @spacing.setter + def spacing(self, value: int): + """set the spacing between subplots, in pixels""" + if not isinstance(value, (int, np.integer)): + raise TypeError("spacing must be of type ") + + self._spacing = value + self._set_viewport_rects() + @property def canvas(self) -> BaseRenderCanvas: """The canvas this Figure is drawn onto""" @@ -346,54 +408,62 @@ def renderer(self) -> pygfx.WgpuRenderer: @property def controllers(self) -> np.ndarray[pygfx.Controller]: """controllers, read-only array, access individual subplots to change a controller""" - controllers = np.asarray( - [subplot.controller for subplot in self], dtype=object - ).reshape(self.shape) + controllers = np.asarray([subplot.controller for subplot in self], dtype=object) + + if self.mode == "grid": + controllers = controllers.reshape(self.shape) + controllers.flags.writeable = False return controllers @property def cameras(self) -> np.ndarray[pygfx.Camera]: """cameras, read-only array, access individual subplots to change a camera""" - cameras = np.asarray( - [subplot.camera for subplot in self], dtype=object - ).reshape(self.shape) + cameras = np.asarray([subplot.camera for subplot in self], dtype=object) + + if self.mode == "grid": + cameras = cameras.reshape(self.shape) + cameras.flags.writeable = False return cameras @property def names(self) -> np.ndarray[str]: """subplot names, read-only array, access individual subplots to change a name""" - names = np.asarray([subplot.name for subplot in self]).reshape(self.shape) + names = np.asarray([subplot.name for subplot in self]) + + if self.mode == "grid": + names = names.reshape(self.shape) + names.flags.writeable = False return names - def __getitem__(self, index: tuple[int, int] | str) -> Subplot: + def __getitem__(self, index: str | int | tuple[int, int]) -> Subplot: if isinstance(index, str): for subplot in self._subplots.ravel(): if subplot.name == index: return subplot raise IndexError(f"no subplot with given name: {index}") - else: + + if self.mode == "grid": return self._subplots[index[0], index[1]] - def render(self, draw=True): + return self._subplots[index] + + def _render(self, draw=True): # call the animation functions before render self._call_animate_functions(self._animate_funcs_pre) - for subplot in self: - subplot.render() + subplot._render() self.renderer.flush() - if draw: - self.canvas.request_draw() # call post-render animate functions self._call_animate_functions(self._animate_funcs_post) - def start_render(self): + def _start_render(self): """start render cycle""" - self.canvas.request_draw(self.render) + self.canvas.request_draw(self._render) def show( self, @@ -431,7 +501,7 @@ def show( if self._output: return self._output - self.start_render() + self._start_render() if sidecar_kwargs is None: sidecar_kwargs = dict() @@ -471,8 +541,8 @@ def show( elif self.canvas.__class__.__name__ == "OffscreenRenderCanvas": # for test and docs gallery screenshots + self._set_viewport_rects() for subplot in self: - subplot.set_viewport_rect() subplot.axes.update_using_camera() # render call is blocking only on github actions for some reason, @@ -481,7 +551,7 @@ def show( # but it is necessary for the gallery images too so that's why this check is here if "RTD_BUILD" in os.environ.keys(): if os.environ["RTD_BUILD"] == "1": - self.render() + self._render() else: # assume GLFW self._output = self.canvas @@ -642,6 +712,161 @@ def export(self, uri: str | Path | bytes, **kwargs): def open_popup(self, *args, **kwargs): warn("popups only supported by ImguiFigure") + def _fpl_set_subplot_viewport_rect(self, subplot: Subplot): + """ + Sets the viewport rect for the given subplot + """ + + if self.mode == "grid": + # row, col position of this subplot within the grid + row_ix, col_ix = self._subplot_grid_positions[subplot] + + # number of rows, cols in the grid + nrows, ncols = self.shape + + # get starting positions and dimensions for the pygfx portion of the canvas + # anything outside the pygfx portion of the canvas is for imgui + x0_canvas, y0_canvas, width_canvas, height_canvas = ( + self.get_pygfx_render_area() + ) + + # width of an individual subplot + width_subplot = width_canvas / ncols + # height of an individual subplot + height_subplot = height_canvas / nrows + + # x position of this subplot + x_pos = ( + ((col_ix - 1) * width_subplot) + + width_subplot + + x0_canvas + + self.spacing + ) + # y position of this subplot + y_pos = ( + ((row_ix - 1) * height_subplot) + + height_subplot + + y0_canvas + + self.spacing + ) + + if self.__class__.__name__ == "ImguiFigure" and subplot.toolbar: + # leave space for imgui toolbar + height_subplot -= IMGUI_TOOLBAR_HEIGHT + + # clip so that min (w, h) is always 1, otherwise JupyterRenderCanvas causes issues because it + # initializes with a width, height of (0, 0) + rect = np.array( + [ + x_pos, + y_pos, + width_subplot - self.spacing, + height_subplot - self.spacing, + ] + ).clip(min=[0, 0, 1, 1]) + + # adjust if a subplot dock is present + adjust = np.array( + [ + # add left dock size to x_pos + subplot.docks["left"].size, + # add top dock size to y_pos + subplot.docks["top"].size, + # remove left and right dock sizes from width + -subplot.docks["right"].size - subplot.docks["left"].size, + # remove top and bottom dock sizes from height + -subplot.docks["top"].size - subplot.docks["bottom"].size, + ] + ) + + subplot.viewport.rect = rect + adjust + + def _fpl_set_subplot_dock_viewport_rect(self, subplot, position): + """ + Sets the viewport rect for the given subplot dock + """ + + dock = subplot.docks[position] + + if dock.size == 0: + dock.viewport.rect = None + return + + if self.mode == "grid": + # row, col position of this subplot within the grid + row_ix, col_ix = self._subplot_grid_positions[subplot] + + # number of rows, cols in the grid + nrows, ncols = self.shape + + x0_canvas, y0_canvas, width_canvas, height_canvas = ( + self.get_pygfx_render_area() + ) + + # width of an individual subplot + width_subplot = width_canvas / ncols + # height of an individual subplot + height_subplot = height_canvas / nrows + + # calculate the rect based on the dock position + match position: + case "right": + x_pos = ( + ((col_ix - 1) * width_subplot) + (width_subplot * 2) - dock.size + ) + y_pos = ( + ((row_ix - 1) * height_subplot) + height_subplot + self.spacing + ) + width_viewport = dock.size + height_viewport = height_subplot - self.spacing + + case "left": + x_pos = ((col_ix - 1) * width_subplot) + width_subplot + y_pos = ( + ((row_ix - 1) * height_subplot) + height_subplot + self.spacing + ) + width_viewport = dock.size + height_viewport = height_subplot - self.spacing + + case "top": + x_pos = ( + ((col_ix - 1) * width_subplot) + width_subplot + self.spacing + ) + y_pos = ( + ((row_ix - 1) * height_subplot) + height_subplot + self.spacing + ) + width_viewport = width_subplot - self.spacing + height_viewport = dock.size + + case "bottom": + x_pos = ( + ((col_ix - 1) * width_subplot) + width_subplot + self.spacing + ) + y_pos = ( + ((row_ix - 1) * height_subplot) + + (height_subplot * 2) + - dock.size + ) + width_viewport = width_subplot - self.spacing + height_viewport = dock.size + + case _: + raise ValueError("invalid position") + + dock.viewport.rect = [ + x_pos + x0_canvas, + y_pos + y0_canvas, + width_viewport, + height_viewport, + ] + + def _set_viewport_rects(self, *ev): + """set the viewport rects for all subplots, *ev argument is not used, exists because of renderer resize event""" + for subplot in self: + self._fpl_set_subplot_viewport_rect(subplot) + for dock_pos in subplot.docks.keys(): + self._fpl_set_subplot_dock_viewport_rect(subplot, dock_pos) + def get_pygfx_render_area(self, *args) -> tuple[int, int, int, int]: """ Fet rect for the portion of the canvas that the pygfx renderer draws to, @@ -658,20 +883,20 @@ def get_pygfx_render_area(self, *args) -> tuple[int, int, int, int]: return 0, 0, width, height - def _get_iterator(self): - return product(range(self.shape[0]), range(self.shape[1])) - def __iter__(self): - self._current_iter = self._get_iterator() + self._current_iter = iter(range(len(self))) return self def __next__(self) -> Subplot: pos = self._current_iter.__next__() - return self._subplots[pos] + return self._subplots.ravel()[pos] def __len__(self): """number of subplots""" - return self.shape[0] * self.shape[1] + if isinstance(self._shape, tuple): + return self.shape[0] * self.shape[1] + if isinstance(self._shape, list): + return len(self._shape) def __str__(self): return f"{self.__class__.__name__} @ {hex(id(self))}" diff --git a/fastplotlib/layouts/_imgui_figure.py b/fastplotlib/layouts/_imgui_figure.py index 8621f4464..2e77f350d 100644 --- a/fastplotlib/layouts/_imgui_figure.py +++ b/fastplotlib/layouts/_imgui_figure.py @@ -20,7 +20,7 @@ class ImguiFigure(Figure): def __init__( self, - shape: tuple[int, int] = (1, 1), + shape: list[tuple[int, int, int, int]] | tuple[int, int] = (1, 1), cameras: ( Literal["2d", "3d"] | Iterable[Iterable[Literal["2d", "3d"]]] @@ -80,12 +80,12 @@ def __init__( self.imgui_renderer.set_gui(self._draw_imgui) self._subplot_toolbars: np.ndarray[SubplotToolbar] = np.empty( - shape=self._subplots.shape, dtype=object + shape=self._subplots.size, dtype=object ) - for subplot in self._subplots.ravel(): + for i, subplot in enumerate(self._subplots.ravel()): toolbar = SubplotToolbar(subplot=subplot, fa_icons=self._fa_icons) - self._subplot_toolbars[subplot.position] = toolbar + self._subplot_toolbars[i] = toolbar self._right_click_menu = StandardRightClickMenu( figure=self, fa_icons=self._fa_icons @@ -105,8 +105,8 @@ def imgui_renderer(self) -> ImguiRenderer: """imgui renderer""" return self._imgui_renderer - def render(self, draw=False): - super().render(draw) + def _render(self, draw=False): + super()._render(draw) self.imgui_renderer.render() self.canvas.request_draw() @@ -164,7 +164,7 @@ def add_gui(self, gui: EdgeWindow): self.guis[location] = gui - self._reset_viewports() + self._set_viewport_rects() def get_pygfx_render_area(self, *args) -> tuple[int, int, int, int]: """ @@ -200,15 +200,6 @@ def get_pygfx_render_area(self, *args) -> tuple[int, int, int, int]: return xpos, ypos, max(1, width), max(1, height) - def _reset_viewports(self): - # TODO: think about moving this to Figure later, - # maybe also refactor Subplot and PlotArea so that - # the resize event is handled at the Figure level instead - for subplot in self: - subplot.set_viewport_rect() - for dock in subplot.docks.values(): - dock.set_viewport_rect() - def register_popup(self, popup: Popup.__class__): """ Register a popup class. Note that this takes the class, not an instance diff --git a/fastplotlib/layouts/_plot_area.py b/fastplotlib/layouts/_plot_area.py index a17c94d58..c4e6a9d70 100644 --- a/fastplotlib/layouts/_plot_area.py +++ b/fastplotlib/layouts/_plot_area.py @@ -28,7 +28,6 @@ class PlotArea: def __init__( self, parent: Union["PlotArea", "Figure"], - position: tuple[int, int] | str, camera: pygfx.PerspectiveCamera, controller: pygfx.Controller, scene: pygfx.Scene, @@ -70,7 +69,6 @@ def __init__( """ self._parent = parent - self._position = position self._scene = scene self._canvas = canvas @@ -88,8 +86,6 @@ def __init__( self._animate_funcs_pre: list[callable] = list() self._animate_funcs_post: list[callable] = list() - self.renderer.add_event_handler(self.set_viewport_rect, "resize") - # list of hex id strings for all graphics managed by this PlotArea # the real Graphic instances are managed by REFERENCES self._graphics: list[Graphic] = list() @@ -120,8 +116,6 @@ def __init__( self._background = pygfx.Background(None, self._background_material) self.scene.add(self._background) - self.set_viewport_rect() - def get_figure(self, obj=None): """Get Figure instance that contains this plot area""" if obj is None: @@ -141,11 +135,6 @@ def parent(self): """A parent if relevant""" return self._parent - @property - def position(self) -> tuple[int, int] | str: - """Position of this plot area within a larger layout (such as a Figure) if relevant""" - return self._position - @property def scene(self) -> pygfx.Scene: """The Scene where Graphics lie in this plot area""" @@ -284,19 +273,6 @@ def background_color(self, colors: str | tuple[float]): """1, 2, or 4 colors, each color must be acceptable by pygfx.Color""" self._background_material.set_colors(*colors) - def get_rect(self) -> tuple[float, float, float, float]: - """ - Returns the viewport rect to define the rectangle - occupied by the viewport w.r.t. the Canvas. - - If this is a subplot within a Figure, it returns the rectangle - for only this subplot w.r.t. the parent canvas. - - Must return: [x_pos, y_pos, width_viewport, height_viewport] - - """ - raise NotImplementedError("Must be implemented in subclass") - def map_screen_to_world( self, pos: tuple[float, float] | pygfx.PointerEvent ) -> np.ndarray | None: @@ -333,17 +309,14 @@ def map_screen_to_world( # default z is zero for now return np.array([*pos_world[:2], 0]) - def set_viewport_rect(self, *args): - self.viewport.rect = self.get_rect() - - def render(self): + def _render(self): self._call_animate_functions(self._animate_funcs_pre) # does not flush, flush must be implemented in user-facing Plot objects self.viewport.render(self.scene, self.camera) for child in self.children: - child.render() + child._render() self._call_animate_functions(self._animate_funcs_post) diff --git a/fastplotlib/layouts/_subplot.py b/fastplotlib/layouts/_subplot.py index 7d52ebab2..a97e89b0d 100644 --- a/fastplotlib/layouts/_subplot.py +++ b/fastplotlib/layouts/_subplot.py @@ -1,7 +1,5 @@ from typing import Literal, Union -import numpy as np - import pygfx from rendercanvas import BaseRenderCanvas @@ -13,16 +11,10 @@ from ..graphics._axes import Axes -# number of pixels taken by the imgui toolbar when present -IMGUI_TOOLBAR_HEIGHT = 39 - - class Subplot(PlotArea, GraphicMethodsMixin): def __init__( self, parent: Union["Figure"], - position: tuple[int, int], - parent_dims: tuple[int, int], camera: Literal["2d", "3d"] | pygfx.PerspectiveCamera, controller: pygfx.Controller, canvas: BaseRenderCanvas | pygfx.Texture, @@ -44,9 +36,6 @@ def __init__( position: (int, int), optional corresponds to the [row, column] position of the subplot within a ``Figure`` - parent_dims: (int, int), optional - dimensions of the parent ``Figure`` - camera: str or pygfx.PerspectiveCamera, default '2d' indicates the FOV for the camera, '2d' sets ``fov = 0``, '3d' sets ``fov = 50``. ``fov`` can be changed at any time. @@ -69,29 +58,18 @@ def __init__( super(GraphicMethodsMixin, self).__init__() - if position is None: - position = (0, 0) - - if parent_dims is None: - parent_dims = (1, 1) - - self.nrows, self.ncols = parent_dims - camera = create_camera(camera) controller = create_controller(controller_type=controller, camera=camera) self._docks = dict() - self.spacing = 2 - self._title_graphic: TextGraphic = None self._toolbar = True super(Subplot, self).__init__( parent=parent, - position=position, camera=camera, controller=controller, scene=pygfx.Scene(), @@ -122,8 +100,17 @@ def name(self) -> str: @name.setter def name(self, name: str): + if name is None: + self._name = None + return + + for subplot in self.get_figure(self): + if (subplot is self) or (subplot is None): + continue + if subplot.name == name: + raise ValueError("subplot names must be unique") + self._name = name - self.set_title(name) @property def docks(self) -> dict: @@ -148,11 +135,11 @@ def toolbar(self) -> bool: @toolbar.setter def toolbar(self, visible: bool): self._toolbar = bool(visible) - self.set_viewport_rect() + self.get_figure()._fpl_set_subplot_viewport_rect(self) - def render(self): + def _render(self): self.axes.update_using_camera() - super().render() + super()._render() def set_title(self, text: str): """Sets the plot title, stored as a ``TextGraphic`` in the "top" dock area""" @@ -180,54 +167,6 @@ def center_title(self): self.docks["top"].center_graphic(self._title_graphic, zoom=1.5) self._title_graphic.world_object.position_y = -3.5 - def get_rect(self) -> np.ndarray: - """ - Returns the bounding box that defines the Subplot within the canvas. - - Returns - ------- - np.ndarray - x_position, y_position, width, height - - """ - row_ix, col_ix = self.position - - x_start_render, y_start_render, width_canvas_render, height_canvas_render = ( - self.parent.get_pygfx_render_area() - ) - - x_pos = ( - ( - (width_canvas_render / self.ncols) - + ((col_ix - 1) * (width_canvas_render / self.ncols)) - ) - + self.spacing - + x_start_render - ) - y_pos = ( - ( - (height_canvas_render / self.nrows) - + ((row_ix - 1) * (height_canvas_render / self.nrows)) - ) - + self.spacing - + y_start_render - ) - width_subplot = (width_canvas_render / self.ncols) - self.spacing - height_subplot = (height_canvas_render / self.nrows) - self.spacing - - if self.parent.__class__.__name__ == "ImguiFigure" and self.toolbar: - # leave space for imgui toolbar - height_subplot -= IMGUI_TOOLBAR_HEIGHT - - # clip so that min values are always 1, otherwise JupyterRenderCanvas causes issues because it - # initializes with a width of (0, 0) - rect = np.array([x_pos, y_pos, width_subplot, height_subplot]).clip(1) - - for dv in self.docks.values(): - rect = rect + dv.get_parent_rect_adjust() - - return rect - class Dock(PlotArea): _valid_positions = ["right", "left", "top", "bottom"] @@ -244,10 +183,10 @@ def __init__( ) self._size = size + self._position = position super().__init__( parent=parent, - position=position, camera=pygfx.OrthographicCamera(), controller=pygfx.PanZoomController(), scene=pygfx.Scene(), @@ -255,6 +194,10 @@ def __init__( renderer=parent.renderer, ) + @property + def position(self) -> str: + return self._position + @property def size(self) -> int: """Get or set the size of this dock""" @@ -263,141 +206,17 @@ def size(self) -> int: @size.setter def size(self, s: int): self._size = s - self.parent.set_viewport_rect() - self.set_viewport_rect() - - def get_rect(self, *args): - """ - Returns the bounding box that defines this dock area within the canvas. - - Returns - ------- - np.ndarray - x_position, y_position, width, height - """ - if self.size == 0: - self.viewport.rect = None + if self.position == "top": + # TODO: treat title dock separately, do not allow user to change viewport stuff return - row_ix_parent, col_ix_parent = self.parent.position - - x_start_render, y_start_render, width_render_canvas, height_render_canvas = ( - self.parent.parent.get_pygfx_render_area() + self.get_figure(self.parent)._fpl_set_subplot_viewport_rect(self.parent) + self.get_figure(self.parent)._fpl_set_subplot_dock_viewport_rect( + self.parent, self._position ) - spacing = 2 # spacing in pixels - - if self.position == "right": - x_pos = ( - (width_render_canvas / self.parent.ncols) - + ((col_ix_parent - 1) * (width_render_canvas / self.parent.ncols)) - + (width_render_canvas / self.parent.ncols) - - self.size - ) - y_pos = ( - (height_render_canvas / self.parent.nrows) - + ((row_ix_parent - 1) * (height_render_canvas / self.parent.nrows)) - ) + spacing - width_viewport = self.size - height_viewport = (height_render_canvas / self.parent.nrows) - spacing - - elif self.position == "left": - x_pos = (width_render_canvas / self.parent.ncols) + ( - (col_ix_parent - 1) * (width_render_canvas / self.parent.ncols) - ) - y_pos = ( - (height_render_canvas / self.parent.nrows) - + ((row_ix_parent - 1) * (height_render_canvas / self.parent.nrows)) - ) + spacing - width_viewport = self.size - height_viewport = (height_render_canvas / self.parent.nrows) - spacing - - elif self.position == "top": - x_pos = ( - (width_render_canvas / self.parent.ncols) - + ((col_ix_parent - 1) * (width_render_canvas / self.parent.ncols)) - + spacing - ) - y_pos = ( - (height_render_canvas / self.parent.nrows) - + ((row_ix_parent - 1) * (height_render_canvas / self.parent.nrows)) - ) + spacing - width_viewport = (width_render_canvas / self.parent.ncols) - spacing - height_viewport = self.size - - elif self.position == "bottom": - x_pos = ( - (width_render_canvas / self.parent.ncols) - + ((col_ix_parent - 1) * (width_render_canvas / self.parent.ncols)) - + spacing - ) - y_pos = ( - ( - (height_render_canvas / self.parent.nrows) - + ((row_ix_parent - 1) * (height_render_canvas / self.parent.nrows)) - ) - + (height_render_canvas / self.parent.nrows) - - self.size - ) - width_viewport = (width_render_canvas / self.parent.ncols) - spacing - height_viewport = self.size - else: - raise ValueError("invalid position") - - if self.parent.__class__.__name__ == "ImguiFigure" and self.parent.toolbar: - # leave space for imgui toolbar - height_viewport -= IMGUI_TOOLBAR_HEIGHT - - return [ - x_pos + x_start_render, - y_pos + y_start_render, - width_viewport, - height_viewport, - ] - - def get_parent_rect_adjust(self): - if self.position == "right": - return np.array( - [ - 0, # parent subplot x-position is same - 0, - -self.size, # width of parent subplot is `self.size` smaller - 0, - ] - ) - - elif self.position == "left": - return np.array( - [ - self.size, # `self.size` added to parent subplot x-position - 0, - -self.size, # width of parent subplot is `self.size` smaller - 0, - ] - ) - - elif self.position == "top": - return np.array( - [ - 0, - self.size, # `self.size` added to parent subplot y-position - 0, - -self.size, # height of parent subplot is `self.size` smaller - ] - ) - - elif self.position == "bottom": - return np.array( - [ - 0, - 0, # parent subplot y-position is same, - 0, - -self.size, # height of parent subplot is `self.size` smaller - ] - ) - - def render(self): + def _render(self): if self.size == 0: return - super().render() + super()._render() diff --git a/fastplotlib/ui/_subplot_toolbar.py b/fastplotlib/ui/_subplot_toolbar.py index 6c1a81f73..7d183bf6d 100644 --- a/fastplotlib/ui/_subplot_toolbar.py +++ b/fastplotlib/ui/_subplot_toolbar.py @@ -16,7 +16,8 @@ def __init__(self, subplot: Subplot, fa_icons: imgui.ImFont): def update(self): # get subplot rect - x, y, width, height = self._subplot.get_rect() + x, y, width, height = self._subplot.viewport.rect + y += self._subplot.docks["bottom"].size # place the toolbar window below the subplot pos = (x, y + height) @@ -25,14 +26,14 @@ def update(self): imgui.set_next_window_pos(pos) flags = imgui.WindowFlags_.no_collapse | imgui.WindowFlags_.no_title_bar - imgui.begin(f"Toolbar-{self._subplot.position}", p_open=None, flags=flags) + imgui.begin(f"Toolbar-{hex(id(self._subplot))}", p_open=None, flags=flags) # icons for buttons imgui.push_font(self._fa_icons) # push ID to prevent conflict between multiple figs with same UI imgui.push_id(self._id_counter) - with imgui_ctx.begin_horizontal(f"toolbar-{self._subplot.position}"): + with imgui_ctx.begin_horizontal(f"toolbar-{hex(id(self._subplot))}"): # autoscale button if imgui.button(fa.ICON_FA_MAXIMIZE): self._subplot.auto_scale() diff --git a/fastplotlib/ui/right_click_menus/_standard_menu.py b/fastplotlib/ui/right_click_menus/_standard_menu.py index 9a584043c..772baa170 100644 --- a/fastplotlib/ui/right_click_menus/_standard_menu.py +++ b/fastplotlib/ui/right_click_menus/_standard_menu.py @@ -74,12 +74,11 @@ def update(self): return name = self.get_subplot().name - if name is None: - name = self.get_subplot().position - # text label at the top of the menu - imgui.text(f"subplot: {name}") - imgui.separator() + if name is not None: + # text label at the top of the menu + imgui.text(f"subplot: {name}") + imgui.separator() # autoscale, center, maintain aspect if imgui.menu_item(f"Autoscale", "", False)[0]: diff --git a/fastplotlib/widgets/image_widget/_widget.py b/fastplotlib/widgets/image_widget/_widget.py index 31a8176e5..0fbc02be3 100644 --- a/fastplotlib/widgets/image_widget/_widget.py +++ b/fastplotlib/widgets/image_widget/_widget.py @@ -347,8 +347,6 @@ def __init__( """ self._initialized = False - self._names = None - if figure_kwargs is None: figure_kwargs = dict() @@ -425,7 +423,6 @@ def __init__( raise ValueError( "number of `names` for subplots must be same as the number of data arrays" ) - self._names = names else: raise TypeError( @@ -496,7 +493,7 @@ def __init__( self._dims_max_bounds[_dim], array.shape[i] ) - figure_kwargs_default = {"controller_ids": "sync"} + figure_kwargs_default = {"controller_ids": "sync", "names": names} # update the default kwargs with any user-specified kwargs # user specified kwargs will overwrite the defaults @@ -518,10 +515,6 @@ def __init__( self._histogram_widget = histogram_widget for data_ix, (d, subplot) in enumerate(zip(self.data, self.figure)): - if self._names is not None: - name = self._names[data_ix] - else: - name = None frame = self._process_indices(d, slice_indices=self._current_index) frame = self._process_frame_apply(frame, data_ix) @@ -554,8 +547,6 @@ def __init__( **graphic_kwargs, ) subplot.add_graphic(ig) - subplot.name = name - subplot.set_title(name) if self._histogram_widget: hlut = HistogramLUTTool(data=d, image_graphic=ig, name="histogram_lut") From 3bff88ea50e69eaafc510793e64924de25bcfbe9 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Fri, 21 Feb 2025 13:57:46 -0500 Subject: [PATCH 156/185] remove old video writer code (#736) --- fastplotlib/layouts/_figure.py | 3 - fastplotlib/layouts/_video_writer.py | 82 ---------------------------- 2 files changed, 85 deletions(-) delete mode 100644 fastplotlib/layouts/_video_writer.py diff --git a/fastplotlib/layouts/_figure.py b/fastplotlib/layouts/_figure.py index 5f253b82f..e09005a4c 100644 --- a/fastplotlib/layouts/_figure.py +++ b/fastplotlib/layouts/_figure.py @@ -1,8 +1,6 @@ import os from itertools import product, chain -from multiprocessing import Queue from pathlib import Path -from time import time import numpy as np from typing import Literal, Iterable @@ -13,7 +11,6 @@ from rendercanvas import BaseRenderCanvas -from ._video_writer import VideoWriterAV from ._utils import make_canvas_and_renderer, create_controller, create_camera from ._utils import controller_types as valid_controller_types from ._subplot import Subplot diff --git a/fastplotlib/layouts/_video_writer.py b/fastplotlib/layouts/_video_writer.py deleted file mode 100644 index b7e111b50..000000000 --- a/fastplotlib/layouts/_video_writer.py +++ /dev/null @@ -1,82 +0,0 @@ -from pathlib import Path -from multiprocessing import Queue, Process - - -def _get_av(): - try: - import av - except ImportError: - raise ModuleNotFoundError( - "Recording to video file requires `av`:\n" - "https://github.com/PyAV-Org/PyAV" - ) from None - else: - return av - - -class VideoWriterAV(Process): - """Video writer, uses PyAV in an external process to write frames to disk""" - - def __init__( - self, - path: Path | str, - queue: Queue, - fps: int, - width: int, - height: int, - codec: str, - pixel_format: str, - options: dict = None, - ): - super().__init__() - self.queue = queue - - av = _get_av() - self.container = av.open(path, mode="w") - - self.stream = self.container.add_stream(codec, rate=fps, options=options) - - # in case libx264, trim last rows and/or column - # because libx264 doesn't like non-even number width or height - if width % 2 != 0: - width -= 1 - if height % 2 != 0: - height -= 1 - - self.stream.width = width - self.stream.height = height - - self.stream.pix_fmt = pixel_format - - def run(self): - av = _get_av() - while True: - if self.queue.empty(): # no frame to write - continue - - frame = self.queue.get() - - # recording has ended - if frame is None: - self.container.close() - break - - frame = av.VideoFrame.from_ndarray( - frame[ - : self.stream.height, : self.stream.width - ], # trim if necessary because of x264 - format="rgb24", - ) - - for packet in self.stream.encode(frame): - self.container.mux(packet) - - # I don't exactly know what this does, copied from pyav example - for packet in self.stream.encode(): - self.container.mux(packet) - - # close file - self.container.close() - - # close process, release resources - self.close() From b564192f3d248d5b1443ba68237360841c3f0464 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Mon, 24 Feb 2025 18:16:13 -0500 Subject: [PATCH 157/185] Update CONTRIBUTING.md (#737) * Update CONTRIBUTING.md * Update CONTRIBUTING.md --- CONTRIBUTING.md | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 347275b6a..be9e175e6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -100,11 +100,11 @@ git checkout -b my_feature_branch After you have made changes on this branch, add and commit them when you are ready: ```bash -# lint your code -black . +# black format only the source code +black fastplotlib/ # run tests from the repo root dir -WGPU_FORCE_OFFSCREEN=1 pytest tests/ +RENDERCANVAS_FORCE_OFFSCREEN=1 pytest tests/ # desktop examples pytest -v examples @@ -195,6 +195,13 @@ The tests will produce slightly different imperceptible (to a human) results on ground-truth. A small RMSE tolerance has been chosen, `0.025` for most examples. If the output image and ground-truth image are within that tolerance the test will pass. +If the test image and ground-truth image are above the threshold, the test will fail and a difference image will be located in the follow directory: + +``` +examples/desktop/diffs +examples/notebooks/diffs +``` + Some feature development may require the ground-truth screenshots to be updated. In the event that your changes require this, please do the following: @@ -288,12 +295,12 @@ pip install -e ".[imgui, tests, docs, notebook]" 4) Lint codebase and make sure tests pass ```bash -# lint codebase -black . +# black format only the source code +black fastplotlib/ # run tests # backend tests -WGPU_FORCE_OFFSCREEN=1 pytest tests/ +RENDERCANVAS_FORCE_OFFSCREEN=1 pytest tests/ # desktop examples pytest -v examples From bd131f9740e4c359657cc84311700a7d729d4288 Mon Sep 17 00:00:00 2001 From: Caitlin Lewis <69729525+clewis7@users.noreply.github.com> Date: Mon, 24 Feb 2025 18:17:07 -0500 Subject: [PATCH 158/185] add kmeans clustering example (#734) * add kmeans clustering example * update conf * switch to tool tip * switch to linear interp and 3D camera for kmeans * increase timeout for deploy docs connection * increase log level * requested changes --------- Co-authored-by: Kushal Kolar --- .github/workflows/docs-deploy.yml | 2 + docs/source/conf.py | 2 - examples/machine_learning/kmeans.py | 119 ++++++++++++++++++++++++++++ 3 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 examples/machine_learning/kmeans.py diff --git a/.github/workflows/docs-deploy.yml b/.github/workflows/docs-deploy.yml index fe267291a..f854ed70d 100644 --- a/.github/workflows/docs-deploy.yml +++ b/.github/workflows/docs-deploy.yml @@ -98,6 +98,8 @@ jobs: server: ${{ secrets.DOCS_SERVER }} username: ${{ secrets.DOCS_USERNAME }} password: ${{ secrets.DOCS_PASSWORD }} + log-level: verbose + timeout: 60000 local-dir: docs/build/html/ server-dir: ./ # deploy to the root dir exclude: | # don't delete the /ver/ dir diff --git a/docs/source/conf.py b/docs/source/conf.py index 66b3c9317..76298d4ff 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -88,8 +88,6 @@ templates_path = ["_templates"] exclude_patterns = [] -napoleon_custom_sections = ["Features"] - # -- Options for HTML output ------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output diff --git a/examples/machine_learning/kmeans.py b/examples/machine_learning/kmeans.py new file mode 100644 index 000000000..620fa15fb --- /dev/null +++ b/examples/machine_learning/kmeans.py @@ -0,0 +1,119 @@ +""" +K-Means Clustering of MNIST Dataset +=================================== + +Example showing how you can perform K-Means clustering on the MNIST dataset. +""" + +# test_example = false +# sphinx_gallery_pygfx_docs = 'screenshot' + +import fastplotlib as fpl +import numpy as np +from sklearn.datasets import load_digits +from sklearn.cluster import KMeans +from sklearn.decomposition import PCA + +# load the data +mnist = load_digits() + +# get the data and labels +data = mnist['data'] # (1797, 64) +labels = mnist['target'] # (1797,) + +# visualize the first 5 digits +# NOTE: this is just to give a sense of the dataset if you are unfamiliar, +# the more interesting visualization is below :D +fig_data = fpl.Figure(shape=(1, 5), size=(900, 300)) + +# iterate through each subplot +for i, subplot in enumerate(fig_data): + # reshape each image to (8, 8) + subplot.add_image(data[i].reshape(8,8), cmap="gray", interpolation="linear") + # add the label as a title + subplot.set_title(f"Label: {labels[i]}") + # turn off the axes and toolbar + subplot.axes.visible = False + subplot.toolbar = False + +fig_data.show() + +# project the data from 64 dimensions down to the number of unique digits +n_digits = len(np.unique(labels)) # 10 + +reduced_data = PCA(n_components=n_digits).fit_transform(data) # (1797, 10) + +# performs K-Means clustering, take the best of 4 runs +kmeans = KMeans(n_clusters=n_digits, n_init=4) +# fit the lower-dimension data +kmeans.fit(reduced_data) + +# get the centroids (center of the clusters) +centroids = kmeans.cluster_centers_ + +# plot the kmeans result and corresponding original image +figure = fpl.Figure( + shape=(1,2), + size=(700, 400), + cameras=["3d", "2d"], + controller_types=[["fly", "panzoom"]] +) + +# set the axes to False +figure[0, 0].axes.visible = False +figure[0, 1].axes.visible = False + +figure[0, 0].set_title(f"K-means clustering of PCA-reduced data") + +# plot the centroids +figure[0, 0].add_scatter( + data=np.vstack([centroids[:, 0], centroids[:, 1], centroids[:, 2]]).T, + colors="white", + sizes=15 +) +# plot the down-projected data +digit_scatter = figure[0,0].add_scatter( + data=np.vstack([reduced_data[:, 0], reduced_data[:, 1], reduced_data[:, 2]]).T, + sizes=5, + cmap="tab10", # use a qualitative cmap + cmap_transform=kmeans.labels_, # color by the predicted cluster +) + +# initial index +ix = 0 + +# plot the initial image +digit_img = figure[0, 1].add_image( + data=data[ix].reshape(8,8), + cmap="gray", + name="digit", + interpolation="linear" +) + +# change the color and size of the initial selected data point +digit_scatter.colors[ix] = "magenta" +digit_scatter.sizes[ix] = 10 + +# define event handler to update the selected data point +@digit_scatter.add_event_handler("pointer_enter") +def update(ev): + # reset colors and sizes + digit_scatter.cmap = "tab10" + digit_scatter.sizes = 5 + + # update with new seleciton + ix = ev.pick_info["vertex_index"] + + digit_scatter.colors[ix] = "magenta" + digit_scatter.sizes[ix] = 10 + + # update digit fig + figure[0, 1]["digit"].data = data[ix].reshape(8, 8) + +figure.show() + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.loop.run() \ No newline at end of file From 33626695b892bad2eaa05078a58e2dae9ad89e59 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Wed, 5 Mar 2025 17:17:35 -0500 Subject: [PATCH 159/185] implemenet `@block_reentrance` decorator (#744) * implemenet block_reentrance decorator * add unit circle example * raise original exception correctly, comments * cleanup, comments --- examples/selection_tools/unit_circle.py | 114 ++++++++++++++++++ fastplotlib/graphics/_features/_base.py | 33 +++++ fastplotlib/graphics/_features/_common.py | 7 +- fastplotlib/graphics/_features/_image.py | 8 +- .../graphics/_features/_positions_graphics.py | 11 +- .../graphics/_features/_selection_features.py | 7 +- fastplotlib/graphics/_features/_text.py | 7 +- 7 files changed, 181 insertions(+), 6 deletions(-) create mode 100644 examples/selection_tools/unit_circle.py diff --git a/examples/selection_tools/unit_circle.py b/examples/selection_tools/unit_circle.py new file mode 100644 index 000000000..76f6a207c --- /dev/null +++ b/examples/selection_tools/unit_circle.py @@ -0,0 +1,114 @@ +""" +Unit circle +=========== + +Example with linear selectors on a sine and cosine function that demonstrates the unit circle. + +This shows how fastplotlib supports bidirectional events, drag the linear selector on the sine +or cosine function and they will both move together. + +Click on the sine or cosine function to set the colormap transform to illustrate the sine or +cosine function output values on the unit circle. +""" + +# test_example = false +# sphinx_gallery_pygfx_docs = 'screenshot' + + +import numpy as np +import fastplotlib as fpl + + +# helper function to make a cirlce +def make_circle(center, radius: float, n_points: int) -> np.ndarray: + theta = np.linspace(0, 2 * np.pi, n_points) + xs = radius * np.cos(theta) + ys = radius * np.sin(theta) + + return np.column_stack([xs, ys]) + center + + +# create a figure with 3 subplots +figure = fpl.Figure((3, 1), names=["unit circle", "sin(x)", "cos(x)"], size=(700, 1024)) + +# set the axes to intersect at (0, 0, 0) to better illustrate the unit circle +for subplot in figure: + subplot.axes.intersection = (0, 0, 0) + +figure["sin(x)"].camera.maintain_aspect = False +figure["cos(x)"].camera.maintain_aspect = False + +# create sine and cosine data +xs = np.linspace(0, 2 * np.pi, 360) +sine = np.sin(xs) +cosine = np.cos(xs) + +# circle data +circle_data = make_circle(center=(0, 0), radius=1, n_points=360) + +# make the circle line graphic, set the cmap transform using the sine function +circle_graphic = figure["unit circle"].add_line( + circle_data, thickness=4, cmap="bwr", cmap_transform=sine +) + +# line to show the circle radius +# use it to indicate the current position of the sine and cosine selctors (below) +radius_data = np.array([[0, 0, 0], [*circle_data[0], 0]]) +circle_radius = figure["unit circle"].add_line( + radius_data, thickness=6, colors="magenta" +) + +# sine line graphic, cmap transform set from the sine function +sine_graphic = figure["sin(x)"].add_line( + sine, thickness=10, cmap="bwr", cmap_transform=sine +) + +# cosine line graphic, cmap transform set from the sine function +# illustrates the sine function values on the cosine graphic +cosine_graphic = figure["cos(x)"].add_line( + cosine, thickness=10, cmap="bwr", cmap_transform=sine +) + +# add linear selectors to the sine and cosine line graphics +sine_selector = sine_graphic.add_linear_selector() +cosine_selector = cosine_graphic.add_linear_selector() + +def set_circle_cmap(ev): + # sets the cmap transforms + + cmap_transform = ev.graphic.data[:, 1] # y-val data of the sine or cosine graphic + for g in [sine_graphic, cosine_graphic]: + g.cmap.transform = cmap_transform + + # set circle cmap transform + circle_graphic.cmap.transform = cmap_transform + +# when the sine or cosine graphic is clicked, the cmap_transform +# of the sine, cosine and circle line graphics are all set from +# the y-values of the clicked line +sine_graphic.add_event_handler(set_circle_cmap, "click") +cosine_graphic.add_event_handler(set_circle_cmap, "click") + + +def set_x_val(ev): + # used to sync the two selectors + value = ev.info["value"] + index = ev.get_selected_index() + + sine_selector.selection = value + cosine_selector.selection = value + + circle_radius.data[1, :-1] = circle_data[index] + +# add same event handler to both graphics +sine_selector.add_event_handler(set_x_val, "selection") +cosine_selector.add_event_handler(set_x_val, "selection") + +figure.show() + + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.loop.run() diff --git a/fastplotlib/graphics/_features/_base.py b/fastplotlib/graphics/_features/_base.py index 1612414a1..1088dc005 100644 --- a/fastplotlib/graphics/_features/_base.py +++ b/fastplotlib/graphics/_features/_base.py @@ -53,6 +53,9 @@ def __init__(self, **kwargs): self._event_handlers = list() self._block_events = False + # used by @block_reentrance decorator to block re-entrance into set_value functions + self._reentrant_block: bool = False + @property def value(self) -> Any: """Graphic Feature value, must be implemented in subclass""" @@ -316,3 +319,33 @@ def __len__(self): def __repr__(self): return f"{self.__class__.__name__} buffer data:\n" f"{self.value.__repr__()}" + + +def block_reentrance(set_value): + # decorator to block re-entrant set_value methods + # useful when creating complex, circular, bidirectional event graphs + def set_value_wrapper(self: GraphicFeature, graphic_or_key, value): + """ + wraps GraphicFeature.set_value + + self: GraphicFeature instance + + graphic_or_key: graphic, or key if a BufferManager + + value: the value passed to set_value() + """ + # set_value is already in the middle of an execution, block re-entrance + if self._reentrant_block: + return + try: + # block re-execution of set_value until it has *fully* finished executing + self._reentrant_block = True + set_value(self, graphic_or_key, value) + except Exception as exc: + # raise original exception + raise exc # set_value has raised. The line above and the lines 2+ steps below are probably more relevant! + finally: + # set_value has finished executing, now allow future executions + self._reentrant_block = False + + return set_value_wrapper diff --git a/fastplotlib/graphics/_features/_common.py b/fastplotlib/graphics/_features/_common.py index fe32a485f..e9c49a475 100644 --- a/fastplotlib/graphics/_features/_common.py +++ b/fastplotlib/graphics/_features/_common.py @@ -1,6 +1,6 @@ import numpy as np -from ._base import GraphicFeature, FeatureEvent +from ._base import GraphicFeature, FeatureEvent, block_reentrance class Name(GraphicFeature): @@ -14,6 +14,7 @@ def __init__(self, value: str): def value(self) -> str: return self._value + @block_reentrance def set_value(self, graphic, value: str): if not isinstance(value, str): raise TypeError("`Graphic` name must be of type ") @@ -44,6 +45,7 @@ def _validate(self, value): def value(self) -> np.ndarray: return self._value + @block_reentrance def set_value(self, graphic, value: np.ndarray | list | tuple): self._validate(value) @@ -74,6 +76,7 @@ def _validate(self, value): def value(self) -> np.ndarray: return self._value + @block_reentrance def set_value(self, graphic, value: np.ndarray | list | tuple): self._validate(value) @@ -96,6 +99,7 @@ def __init__(self, value: bool): def value(self) -> bool: return self._value + @block_reentrance def set_value(self, graphic, value: bool): graphic.world_object.visible = value self._value = value @@ -117,6 +121,7 @@ def __init__(self, value: bool): def value(self) -> bool: return self._value + @block_reentrance def set_value(self, graphic, value: bool): self._value = value event = FeatureEvent(type="deleted", info={"value": value}) diff --git a/fastplotlib/graphics/_features/_image.py b/fastplotlib/graphics/_features/_image.py index b67bf1cd4..c0e2b28d2 100644 --- a/fastplotlib/graphics/_features/_image.py +++ b/fastplotlib/graphics/_features/_image.py @@ -5,7 +5,7 @@ import numpy as np import pygfx -from ._base import GraphicFeature, FeatureEvent +from ._base import GraphicFeature, FeatureEvent, block_reentrance from ...utils import ( make_colors, @@ -135,6 +135,7 @@ def __next__(self) -> tuple[pygfx.Texture, tuple[int, int], tuple[slice, slice]] def __getitem__(self, item): return self.value[item] + @block_reentrance def __setitem__(self, key, value): self.value[key] = value @@ -159,6 +160,7 @@ def __init__(self, value: float): def value(self) -> float: return self._value + @block_reentrance def set_value(self, graphic, value: float): vmax = graphic._material.clim[1] graphic._material.clim = (value, vmax) @@ -179,6 +181,7 @@ def __init__(self, value: float): def value(self) -> float: return self._value + @block_reentrance def set_value(self, graphic, value: float): vmin = graphic._material.clim[0] graphic._material.clim = (vmin, value) @@ -200,6 +203,7 @@ def __init__(self, value: str): def value(self) -> str: return self._value + @block_reentrance def set_value(self, graphic, value: str): new_colors = make_colors(256, value) graphic._material.map.texture.data[:] = new_colors @@ -226,6 +230,7 @@ def _validate(self, value): def value(self) -> str: return self._value + @block_reentrance def set_value(self, graphic, value: str): self._validate(value) @@ -254,6 +259,7 @@ def _validate(self, value): def value(self) -> str: return self._value + @block_reentrance def set_value(self, graphic, value: str): self._validate(value) diff --git a/fastplotlib/graphics/_features/_positions_graphics.py b/fastplotlib/graphics/_features/_positions_graphics.py index c4e153a31..78e53f545 100644 --- a/fastplotlib/graphics/_features/_positions_graphics.py +++ b/fastplotlib/graphics/_features/_positions_graphics.py @@ -1,4 +1,4 @@ -from typing import Any, List +from typing import Any import numpy as np import pygfx @@ -11,6 +11,7 @@ BufferManager, FeatureEvent, to_gpu_supported_dtype, + block_reentrance, ) from .utils import parse_colors @@ -58,6 +59,7 @@ def __init__( super().__init__(data=data, isolated_buffer=isolated_buffer) + @block_reentrance def __setitem__( self, key: int | slice | np.ndarray[int | bool] | tuple[slice, ...], @@ -155,6 +157,7 @@ def __init__( def value(self) -> pygfx.Color: return self._value + @block_reentrance def set_value(self, graphic, value: str | np.ndarray | tuple | list | pygfx.Color): value = pygfx.Color(value) graphic.world_object.material.color = value @@ -174,6 +177,7 @@ def __init__(self, value: int | float): def value(self) -> float: return self._value + @block_reentrance def set_value(self, graphic, value: float | int): graphic.world_object.material.size = float(value) self._value = value @@ -192,6 +196,7 @@ def __init__(self, value: str): def value(self) -> str: return self._value + @block_reentrance def set_value(self, graphic, value: str): if "Line" in graphic.world_object.material.__class__.__name__: graphic.world_object.material.thickness_space = value @@ -243,6 +248,7 @@ def _fix_data(self, data): return to_gpu_supported_dtype(data) + @block_reentrance def __setitem__( self, key: int | slice | np.ndarray[int | bool] | tuple[slice, ...], @@ -318,6 +324,7 @@ def _fix_sizes( return sizes + @block_reentrance def __setitem__( self, key: int | slice | np.ndarray[int | bool] | list[int | bool], @@ -344,6 +351,7 @@ def __init__(self, value: float): def value(self) -> float: return self._value + @block_reentrance def set_value(self, graphic, value: float): graphic.world_object.material.thickness = value self._value = value @@ -392,6 +400,7 @@ def __init__( # set vertex colors from cmap self._vertex_colors[:] = colors + @block_reentrance def __setitem__(self, key: slice, cmap_name): if not isinstance(key, slice): raise TypeError( diff --git a/fastplotlib/graphics/_features/_selection_features.py b/fastplotlib/graphics/_features/_selection_features.py index c385f820f..c157023b4 100644 --- a/fastplotlib/graphics/_features/_selection_features.py +++ b/fastplotlib/graphics/_features/_selection_features.py @@ -1,9 +1,9 @@ -from typing import Sequence, Tuple +from typing import Sequence import numpy as np from ...utils import mesh_masks -from ._base import GraphicFeature, FeatureEvent +from ._base import GraphicFeature, FeatureEvent, block_reentrance class LinearSelectionFeature(GraphicFeature): @@ -54,6 +54,7 @@ def value(self) -> np.float32: """ return self._value + @block_reentrance def set_value(self, selector, value: float): # clip value between limits value = np.clip(value, self._limits[0], self._limits[1], dtype=np.float32) @@ -117,6 +118,7 @@ def axis(self) -> str: """one of "x" | "y" """ return self._axis + @block_reentrance def set_value(self, selector, value: Sequence[float]): """ Set start, stop range of selector @@ -231,6 +233,7 @@ def value(self) -> np.ndarray[float]: """ return self._value + @block_reentrance def set_value(self, selector, value: Sequence[float]): """ Set the selection of the rectangle selector. diff --git a/fastplotlib/graphics/_features/_text.py b/fastplotlib/graphics/_features/_text.py index baa2734d5..90af7c719 100644 --- a/fastplotlib/graphics/_features/_text.py +++ b/fastplotlib/graphics/_features/_text.py @@ -2,7 +2,7 @@ import pygfx -from ._base import GraphicFeature, FeatureEvent +from ._base import GraphicFeature, FeatureEvent, block_reentrance class TextData(GraphicFeature): @@ -14,6 +14,7 @@ def __init__(self, value: str): def value(self) -> str: return self._value + @block_reentrance def set_value(self, graphic, value: str): graphic.world_object.geometry.set_text(value) self._value = value @@ -31,6 +32,7 @@ def __init__(self, value: float | int): def value(self) -> float | int: return self._value + @block_reentrance def set_value(self, graphic, value: float | int): graphic.world_object.geometry.font_size = value self._value = graphic.world_object.geometry.font_size @@ -48,6 +50,7 @@ def __init__(self, value: str | np.ndarray | list[float] | tuple[float]): def value(self) -> pygfx.Color: return self._value + @block_reentrance def set_value(self, graphic, value: str | np.ndarray | list[float] | tuple[float]): value = pygfx.Color(value) graphic.world_object.material.color = value @@ -66,6 +69,7 @@ def __init__(self, value: str | np.ndarray | list[float] | tuple[float]): def value(self) -> pygfx.Color: return self._value + @block_reentrance def set_value(self, graphic, value: str | np.ndarray | list[float] | tuple[float]): value = pygfx.Color(value) graphic.world_object.material.outline_color = value @@ -84,6 +88,7 @@ def __init__(self, value: float): def value(self) -> float: return self._value + @block_reentrance def set_value(self, graphic, value: float): graphic.world_object.material.outline_thickness = value self._value = graphic.world_object.material.outline_thickness From 12ef40db215cf51961721336e22e46b7b40cef04 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Fri, 14 Mar 2025 11:46:40 -0400 Subject: [PATCH 160/185] docs via ssh (#751) * Update docs-deploy.yml * Update _axes.py * Update docs-deploy.yml * Update docs-deploy.yml --- .github/workflows/docs-deploy.yml | 45 ++++++------ fastplotlib/graphics/_axes.py | 114 ++---------------------------- 2 files changed, 29 insertions(+), 130 deletions(-) diff --git a/.github/workflows/docs-deploy.yml b/.github/workflows/docs-deploy.yml index f854ed70d..a0cb54357 100644 --- a/.github/workflows/docs-deploy.yml +++ b/.github/workflows/docs-deploy.yml @@ -68,18 +68,20 @@ jobs: if: ${{ github.ref == 'refs/heads/main' }} # any push to main goes to fastplotlib.org/ver/dev run: echo "DOCS_VERSION_DIR=dev" >> "$GITHUB_ENV" - - # upload docs via FTP + + # upload docs via SCP - name: Deploy docs - uses: SamKirkland/FTP-Deploy-Action@v4.3.5 + uses: appleboy/scp-action@v0.1.7 with: - server: ${{ secrets.DOCS_SERVER }} + host: ${{ secrets.DOCS_SERVER }} username: ${{ secrets.DOCS_USERNAME }} - password: ${{ secrets.DOCS_PASSWORD }} - # built docs - local-dir: docs/build/html/ - # output subdir based on the previous if statements - server-dir: ./ver/${{ env.DOCS_VERSION_DIR }}/ + port: ${{ secrets.DOCS_PORT }} + key: ${{ secrets.DOCS_KEY }} + passphrase: ${{ secrets.DOCS_PASS }} + source: "docs/build/html/*" + # without strip_components it creates dirs docs/build/html within /ver on the server + strip_components: 3 + target: /home/${{ secrets.DOCS_USERNAME }}/public_html/ver/${{ env.DOCS_VERSION_DIR }}/ # comment on PR to provide link to built docs - name: Add PR link in comment @@ -88,19 +90,18 @@ jobs: with: message: | 📚 Docs preview built and uploaded! https://www.fastplotlib.org/ver/${{ env.DOCS_VERSION_DIR }} - - # also deploy to root if this is a new release - # i.e., fastplotlib.org/ points to docs for the latest release - - name: Deploy docs + + # upload docs via SCP + - name: Deploy docs release if: ${{ github.ref_type == 'tag' }} - uses: SamKirkland/FTP-Deploy-Action@v4.3.5 + uses: appleboy/scp-action@v0.1.7 with: - server: ${{ secrets.DOCS_SERVER }} + host: ${{ secrets.DOCS_SERVER }} username: ${{ secrets.DOCS_USERNAME }} - password: ${{ secrets.DOCS_PASSWORD }} - log-level: verbose - timeout: 60000 - local-dir: docs/build/html/ - server-dir: ./ # deploy to the root dir - exclude: | # don't delete the /ver/ dir - **/ver/** + port: ${{ secrets.DOCS_PORT }} + key: ${{ secrets.DOCS_KEY }} + passphrase: ${{ secrets.DOCS_PASS }} + source: "docs/build/html/*" + # without strip_components it creates dirs docs/build/html within /ver on the server + strip_components: 3 + target: /home/${{ secrets.DOCS_USERNAME }}/public_html/ diff --git a/fastplotlib/graphics/_axes.py b/fastplotlib/graphics/_axes.py index 4938b1a97..10774fc2a 100644 --- a/fastplotlib/graphics/_axes.py +++ b/fastplotlib/graphics/_axes.py @@ -141,108 +141,6 @@ def yz(self) -> Grid: return self._yz -class Ruler(pygfx.Ruler): - def __init__(self, **kwargs): - super().__init__(**kwargs) - self.tick_text_mapper = None - self.font_size = 14 - - def _update_sub_objects(self, ticks, tick_auto_step): - """Update the sub-objects to show the given ticks.""" - assert isinstance(ticks, dict) - - tick_size = 5 - min_n_slots = 8 # todo: can be (much) higher when we use a single text object! - - # Load config - start_pos = self._start_pos - end_pos = self._end_pos - start_value = self._start_value - end_value = self.end_value - - # Derive some more variables - length = end_value - start_value - vec = end_pos - start_pos - if length: - vec /= length - - # Get array to store positions - n_slots = self.points.geometry.positions.nitems - n_positions = len(ticks) + 2 - if n_positions <= n_slots <= max(min_n_slots, 2 * n_positions): - # Re-use existing buffers - positions = self.points.geometry.positions.data - sizes = self.points.geometry.sizes.data - self.points.geometry.positions.update_range() - self.points.geometry.sizes.update_range() - else: - # Allocate new buffers - new_n_slots = max(min_n_slots, int(n_positions * 1.2)) - positions = np.zeros((new_n_slots, 3), np.float32) - sizes = np.zeros((new_n_slots,), np.float32) - self.points.geometry.positions = pygfx.Buffer(positions) - self.points.geometry.sizes = pygfx.Buffer(sizes) - # Allocate text objects - while len(self._text_object_pool) < new_n_slots: - ob = pygfx.Text( - pygfx.TextGeometry("", screen_space=True, font_size=self.font_size), - pygfx.TextMaterial(aa=False), - ) - self._text_object_pool.append(ob) - self._text_object_pool[new_n_slots:] = [] - # Reset children - self.clear() - self.add(self._line, self._points, *self._text_object_pool) - - def define_text(pos, text): - if self.tick_text_mapper is not None and text != "": - text = self.tick_text_mapper(text) - - ob = self._text_object_pool[index] - ob.geometry.anchor = self._text_anchor - ob.geometry.anchor_offset = self._text_anchor_offset - ob.geometry.set_text(text) - ob.local.position = pos - - # Apply start point - index = 0 - positions[0] = start_pos - if self._ticks_at_end_points: - sizes[0] = tick_size - define_text(start_pos, f"{self._start_value:0.4g}") - else: - sizes[0] = 0 - define_text(start_pos, f"") - - # Collect ticks - index += 1 - for value, text in ticks.items(): - pos = start_pos + vec * (value - start_value) - positions[index] = pos - sizes[index] = tick_size - define_text(pos, text) - index += 1 - - # Handle end point, and nullify remaining slots - positions[index:] = end_pos - sizes[index:] = 0 - for ob in self._text_object_pool[index:]: - ob.geometry.set_text("") - - # Show last tick? - if self._ticks_at_end_points: - sizes[index] = tick_size - define_text(end_pos, f"{end_value:0.4g}") - - # Hide the ticks close to the ends? - if self._ticks_at_end_points and ticks: - tick_values = list(ticks.keys()) - if abs(tick_values[0] - start_value) < 0.5 * tick_auto_step: - self._text_object_pool[1].geometry.set_text("") - if abs(tick_values[-1] - end_value) < 0.5 * tick_auto_step: - self._text_object_pool[index - 1].geometry.set_text("") - - class Axes: def __init__( self, @@ -283,9 +181,9 @@ def __init__( } # create ruler for each dim - self._x = Ruler(**x_kwargs) - self._y = Ruler(**y_kwargs) - self._z = Ruler(**z_kwargs) + self._x = pygfx.Ruler(**x_kwargs) + self._y = pygfx.Ruler(**y_kwargs) + self._z = pygfx.Ruler(**z_kwargs) self._offset = offset @@ -400,17 +298,17 @@ def offset(self, value: np.ndarray): self._offset = value @property - def x(self) -> Ruler: + def x(self) -> pygfx.Ruler: """x axis ruler""" return self._x @property - def y(self) -> Ruler: + def y(self) -> pygfx.Ruler: """y axis ruler""" return self._y @property - def z(self) -> Ruler: + def z(self) -> pygfx.Ruler: """z axis ruler""" return self._z From 20c9421cb8a6608c2f71751938a6f52957e77613 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Sat, 15 Mar 2025 16:08:55 -0400 Subject: [PATCH 161/185] Rectangle/bbox layouts in a Figure (#740) * start basic mesh and camera stuff * progress * resizing canvas auto-resizes bboxes using internal fractional bbox * resizing works well * ranges as array, comments * layout management logic works! :D git status * handler color, size * cleanup * resize handler highlight * subplot title works, rename to Frame * start generalizing layout manager * cleaner * start rect and extent class for organization * start moving rect logic to a dedicated class * organization * better extent validation * progress * progress * progress * almost there * formatting, subplot toolbar tweaks * cleanup * docks * more stuff * grid works * add or remove subplot, not tested * better highlight behavior * increase size of example fig * repr * sdf for resize handle, better resize handle, overlap stuff with distance * cleanup * more space * spacing tweaks * add utils._types * unit circle using extents * black * refactor, better organization * modified: scripts/generate_add_graphic_methods.py * more stuff * more * add examples * black * rename * update test utils * update nb test utils * update ground truths * update nb ground truths * flex layouts examples 'as screenshot tests * accidentaly added screenshot * comments, cleanup * docs api * black * fix * cleanup * add README.rst for flex layouts examples dir * add flex layouts to test utils list * add spinning spiral scatter example * modify docs conf * forgot a comma * add rect extent ground truths * fix text * fix text features * types * comments, docstrings * update w.r.t. text changes * small typos # Conflicts: # fastplotlib/layouts/_engine.py * small typos * Update fastplotlib/layouts/_engine.py Co-authored-by: Almar Klein * rename FlexLayout -> WindowLayout * better check for imgui * imports * comments * example tests files moved * smaller canvas initial size for abs pixels until rendercanvs fix for glfw * better error messages * update screenshots * update screenshots * black * newer black really was an extra comma for some reason * update example * underline * docstring, better exception messages --------- Co-authored-by: clewis7 Co-authored-by: Almar Klein --- docs/source/api/layouts/figure.rst | 5 +- docs/source/api/layouts/imgui_figure.rst | 5 +- docs/source/api/layouts/subplot.rst | 4 +- docs/source/conf.py | 1 + examples/image_widget/image_widget_videos.py | 2 +- examples/machine_learning/kmeans.py | 26 +- examples/notebooks/nb_test_utils.py | 2 +- .../notebooks/screenshots/nb-astronaut.png | 4 +- .../screenshots/nb-astronaut_RGB.png | 4 +- examples/notebooks/screenshots/nb-camera.png | 4 +- .../nb-image-widget-movie-set_data.png | 4 +- .../nb-image-widget-movie-single-0-reset.png | 4 +- .../nb-image-widget-movie-single-0.png | 4 +- .../nb-image-widget-movie-single-279.png | 4 +- ...e-widget-movie-single-50-window-max-33.png | 4 +- ...-widget-movie-single-50-window-mean-13.png | 4 +- ...-widget-movie-single-50-window-mean-33.png | 4 +- ...ge-widget-movie-single-50-window-reset.png | 4 +- .../nb-image-widget-movie-single-50.png | 4 +- .../nb-image-widget-single-gnuplot2.png | 4 +- .../screenshots/nb-image-widget-single.png | 4 +- ...et-zfish-frame-50-frame-apply-gaussian.png | 4 +- ...idget-zfish-frame-50-frame-apply-reset.png | 4 +- ...ge-widget-zfish-frame-50-max-window-13.png | 4 +- ...e-widget-zfish-frame-50-mean-window-13.png | 4 +- ...ge-widget-zfish-frame-50-mean-window-5.png | 4 +- .../nb-image-widget-zfish-frame-50.png | 4 +- .../nb-image-widget-zfish-frame-99.png | 4 +- ...ish-grid-frame-50-frame-apply-gaussian.png | 4 +- ...-zfish-grid-frame-50-frame-apply-reset.png | 4 +- ...dget-zfish-grid-frame-50-max-window-13.png | 4 +- ...get-zfish-grid-frame-50-mean-window-13.png | 4 +- ...dget-zfish-grid-frame-50-mean-window-5.png | 4 +- .../nb-image-widget-zfish-grid-frame-50.png | 4 +- .../nb-image-widget-zfish-grid-frame-99.png | 4 +- ...e-widget-zfish-grid-init-mean-window-5.png | 4 +- ...fish-grid-set_data-reset-indices-false.png | 4 +- ...zfish-grid-set_data-reset-indices-true.png | 4 +- ...-image-widget-zfish-init-mean-window-5.png | 4 +- ...dget-zfish-mixed-rgb-cockatoo-frame-50.png | 4 +- ...dget-zfish-mixed-rgb-cockatoo-set-data.png | 4 +- ...get-zfish-mixed-rgb-cockatoo-windowrgb.png | 4 +- .../notebooks/screenshots/nb-lines-3d.png | 4 +- .../notebooks/screenshots/nb-lines-colors.png | 4 +- .../notebooks/screenshots/nb-lines-data.png | 4 +- .../screenshots/nb-lines-underlay.png | 4 +- examples/notebooks/screenshots/nb-lines.png | 4 +- .../screenshots/no-imgui-nb-astronaut.png | 4 +- .../screenshots/no-imgui-nb-astronaut_RGB.png | 4 +- .../screenshots/no-imgui-nb-camera.png | 4 +- .../screenshots/no-imgui-nb-lines-3d.png | 4 +- .../screenshots/no-imgui-nb-lines-colors.png | 4 +- .../screenshots/no-imgui-nb-lines-data.png | 4 +- .../no-imgui-nb-lines-underlay.png | 4 +- .../screenshots/no-imgui-nb-lines.png | 4 +- examples/scatter/spinning_spiral.py | 62 +++ examples/screenshots/extent_frac_layout.png | 3 + examples/screenshots/extent_layout.png | 3 + examples/screenshots/gridplot.png | 4 +- examples/screenshots/gridplot_non_square.png | 4 +- .../screenshots/gridplot_viewports_check.png | 4 +- examples/screenshots/heatmap.png | 4 +- examples/screenshots/image_cmap.png | 4 +- examples/screenshots/image_rgb.png | 4 +- examples/screenshots/image_rgbvminvmax.png | 4 +- examples/screenshots/image_simple.png | 4 +- examples/screenshots/image_small.png | 4 +- examples/screenshots/image_vminvmax.png | 4 +- examples/screenshots/image_widget.png | 4 +- examples/screenshots/image_widget_grid.png | 4 +- examples/screenshots/image_widget_imgui.png | 4 +- .../screenshots/image_widget_single_video.png | 4 +- examples/screenshots/image_widget_videos.png | 4 +- .../image_widget_viewports_check.png | 4 +- examples/screenshots/imgui_basic.png | 4 +- examples/screenshots/line.png | 4 +- examples/screenshots/line_cmap.png | 4 +- examples/screenshots/line_cmap_more.png | 4 +- examples/screenshots/line_collection.png | 4 +- .../line_collection_cmap_values.png | 4 +- ...ine_collection_cmap_values_qualitative.png | 4 +- .../screenshots/line_collection_colors.png | 4 +- .../screenshots/line_collection_slicing.png | 4 +- examples/screenshots/line_colorslice.png | 4 +- examples/screenshots/line_dataslice.png | 4 +- examples/screenshots/line_stack.png | 4 +- .../linear_region_selectors_match_offsets.png | 4 +- .../no-imgui-extent_frac_layout.png | 3 + .../screenshots/no-imgui-extent_layout.png | 3 + examples/screenshots/no-imgui-gridplot.png | 4 +- .../no-imgui-gridplot_non_square.png | 4 +- .../no-imgui-gridplot_viewports_check.png | 4 +- examples/screenshots/no-imgui-heatmap.png | 4 +- examples/screenshots/no-imgui-image_cmap.png | 4 +- examples/screenshots/no-imgui-image_rgb.png | 4 +- .../no-imgui-image_rgbvminvmax.png | 4 +- .../screenshots/no-imgui-image_simple.png | 4 +- examples/screenshots/no-imgui-image_small.png | 4 +- .../screenshots/no-imgui-image_vminvmax.png | 4 +- examples/screenshots/no-imgui-line.png | 4 +- examples/screenshots/no-imgui-line_cmap.png | 4 +- .../screenshots/no-imgui-line_cmap_more.png | 4 +- .../screenshots/no-imgui-line_collection.png | 4 +- .../no-imgui-line_collection_cmap_values.png | 4 +- ...ine_collection_cmap_values_qualitative.png | 4 +- .../no-imgui-line_collection_colors.png | 4 +- .../no-imgui-line_collection_slicing.png | 4 +- .../screenshots/no-imgui-line_colorslice.png | 4 +- .../screenshots/no-imgui-line_dataslice.png | 4 +- examples/screenshots/no-imgui-line_stack.png | 4 +- ...-linear_region_selectors_match_offsets.png | 4 +- .../screenshots/no-imgui-rect_frac_layout.png | 3 + examples/screenshots/no-imgui-rect_layout.png | 3 + .../no-imgui-scatter_cmap_iris.png | 4 +- .../no-imgui-scatter_colorslice_iris.png | 4 +- .../no-imgui-scatter_dataslice_iris.png | 4 +- .../screenshots/no-imgui-scatter_iris.png | 4 +- .../screenshots/no-imgui-scatter_size.png | 4 +- examples/screenshots/rect_frac_layout.png | 3 + examples/screenshots/rect_layout.png | 3 + examples/screenshots/scatter_cmap_iris.png | 4 +- .../screenshots/scatter_colorslice_iris.png | 4 +- .../screenshots/scatter_dataslice_iris.png | 4 +- examples/screenshots/scatter_iris.png | 4 +- examples/screenshots/scatter_size.png | 4 +- examples/selection_tools/unit_circle.py | 30 +- examples/tests/test_examples.py | 2 +- examples/tests/testutils.py | 1 + examples/window_layouts/README.rst | 2 + examples/window_layouts/extent_frac_layout.py | 74 +++ examples/window_layouts/extent_layout.py | 74 +++ examples/window_layouts/rect_frac_layout.py | 74 +++ examples/window_layouts/rect_layout.py | 74 +++ fastplotlib/graphics/_base.py | 2 +- fastplotlib/graphics/_features/_text.py | 6 +- fastplotlib/graphics/text.py | 17 +- fastplotlib/layouts/__init__.py | 8 +- fastplotlib/layouts/_engine.py | 390 ++++++++++++++ fastplotlib/layouts/_figure.py | 496 ++++++++---------- fastplotlib/layouts/_frame.py | 371 +++++++++++++ fastplotlib/layouts/_graphic_methods_mixin.py | 3 - fastplotlib/layouts/_imgui_figure.py | 12 +- fastplotlib/layouts/_plot_area.py | 5 +- fastplotlib/layouts/_rect.py | 239 +++++++++ fastplotlib/layouts/_subplot.py | 108 ++-- fastplotlib/layouts/_utils.py | 31 ++ fastplotlib/ui/_subplot_toolbar.py | 14 +- .../ui/right_click_menus/_standard_menu.py | 4 +- fastplotlib/utils/types.py | 4 + scripts/generate_add_graphic_methods.py | 2 - tests/test_text_graphic.py | 4 +- 151 files changed, 1999 insertions(+), 615 deletions(-) create mode 100644 examples/scatter/spinning_spiral.py create mode 100644 examples/screenshots/extent_frac_layout.png create mode 100644 examples/screenshots/extent_layout.png create mode 100644 examples/screenshots/no-imgui-extent_frac_layout.png create mode 100644 examples/screenshots/no-imgui-extent_layout.png create mode 100644 examples/screenshots/no-imgui-rect_frac_layout.png create mode 100644 examples/screenshots/no-imgui-rect_layout.png create mode 100644 examples/screenshots/rect_frac_layout.png create mode 100644 examples/screenshots/rect_layout.png create mode 100644 examples/window_layouts/README.rst create mode 100644 examples/window_layouts/extent_frac_layout.py create mode 100644 examples/window_layouts/extent_layout.py create mode 100644 examples/window_layouts/rect_frac_layout.py create mode 100644 examples/window_layouts/rect_layout.py create mode 100644 fastplotlib/layouts/_engine.py create mode 100644 fastplotlib/layouts/_frame.py create mode 100644 fastplotlib/layouts/_rect.py create mode 100644 fastplotlib/utils/types.py diff --git a/docs/source/api/layouts/figure.rst b/docs/source/api/layouts/figure.rst index 3d6c745e9..b5cbbd2bb 100644 --- a/docs/source/api/layouts/figure.rst +++ b/docs/source/api/layouts/figure.rst @@ -23,11 +23,10 @@ Properties Figure.cameras Figure.canvas Figure.controllers - Figure.mode + Figure.layout Figure.names Figure.renderer Figure.shape - Figure.spacing Methods ~~~~~~~ @@ -35,6 +34,7 @@ Methods :toctree: Figure_api Figure.add_animations + Figure.add_subplot Figure.clear Figure.close Figure.export @@ -42,5 +42,6 @@ Methods Figure.get_pygfx_render_area Figure.open_popup Figure.remove_animation + Figure.remove_subplot Figure.show diff --git a/docs/source/api/layouts/imgui_figure.rst b/docs/source/api/layouts/imgui_figure.rst index 6d6bb2dd4..a338afe96 100644 --- a/docs/source/api/layouts/imgui_figure.rst +++ b/docs/source/api/layouts/imgui_figure.rst @@ -25,11 +25,10 @@ Properties ImguiFigure.controllers ImguiFigure.guis ImguiFigure.imgui_renderer - ImguiFigure.mode + ImguiFigure.layout ImguiFigure.names ImguiFigure.renderer ImguiFigure.shape - ImguiFigure.spacing Methods ~~~~~~~ @@ -38,6 +37,7 @@ Methods ImguiFigure.add_animations ImguiFigure.add_gui + ImguiFigure.add_subplot ImguiFigure.clear ImguiFigure.close ImguiFigure.export @@ -46,5 +46,6 @@ Methods ImguiFigure.open_popup ImguiFigure.register_popup ImguiFigure.remove_animation + ImguiFigure.remove_subplot ImguiFigure.show diff --git a/docs/source/api/layouts/subplot.rst b/docs/source/api/layouts/subplot.rst index 1cf9be31c..e1c55514d 100644 --- a/docs/source/api/layouts/subplot.rst +++ b/docs/source/api/layouts/subplot.rst @@ -26,6 +26,7 @@ Properties Subplot.canvas Subplot.controller Subplot.docks + Subplot.frame Subplot.graphics Subplot.legends Subplot.name @@ -34,6 +35,7 @@ Properties Subplot.renderer Subplot.scene Subplot.selectors + Subplot.title Subplot.toolbar Subplot.viewport @@ -53,7 +55,6 @@ Methods Subplot.auto_scale Subplot.center_graphic Subplot.center_scene - Subplot.center_title Subplot.clear Subplot.delete_graphic Subplot.get_figure @@ -61,5 +62,4 @@ Methods Subplot.map_screen_to_world Subplot.remove_animation Subplot.remove_graphic - Subplot.set_title diff --git a/docs/source/conf.py b/docs/source/conf.py index 76298d4ff..865c462a6 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -59,6 +59,7 @@ "../../examples/heatmap", "../../examples/image_widget", "../../examples/gridplot", + "../../examples/window_layouts", "../../examples/line", "../../examples/line_collection", "../../examples/scatter", diff --git a/examples/image_widget/image_widget_videos.py b/examples/image_widget/image_widget_videos.py index 1e367f0ad..7de4a9c04 100644 --- a/examples/image_widget/image_widget_videos.py +++ b/examples/image_widget/image_widget_videos.py @@ -29,7 +29,7 @@ [random_data, cockatoo_sub], rgb=[False, True], figure_shape=(2, 1), # 2 rows, 1 column - figure_kwargs={"size": (700, 560)} + figure_kwargs={"size": (700, 940)} ) iw.show() diff --git a/examples/machine_learning/kmeans.py b/examples/machine_learning/kmeans.py index 620fa15fb..0aae8fdae 100644 --- a/examples/machine_learning/kmeans.py +++ b/examples/machine_learning/kmeans.py @@ -3,6 +3,9 @@ =================================== Example showing how you can perform K-Means clustering on the MNIST dataset. + +Use WASD keys on your keyboard to fly through the data in PCA space. +Use the mouse pointer to select points. """ # test_example = false @@ -29,17 +32,17 @@ # iterate through each subplot for i, subplot in enumerate(fig_data): # reshape each image to (8, 8) - subplot.add_image(data[i].reshape(8,8), cmap="gray", interpolation="linear") + subplot.add_image(data[i].reshape(8, 8), cmap="gray", interpolation="linear") # add the label as a title - subplot.set_title(f"Label: {labels[i]}") + subplot.title = f"Label: {labels[i]}" # turn off the axes and toolbar subplot.axes.visible = False - subplot.toolbar = False + subplot.toolbar = False fig_data.show() # project the data from 64 dimensions down to the number of unique digits -n_digits = len(np.unique(labels)) # 10 +n_digits = len(np.unique(labels)) # 10 reduced_data = PCA(n_components=n_digits).fit_transform(data) # (1797, 10) @@ -53,17 +56,17 @@ # plot the kmeans result and corresponding original image figure = fpl.Figure( - shape=(1,2), - size=(700, 400), + shape=(1, 2), + size=(700, 560), cameras=["3d", "2d"], - controller_types=[["fly", "panzoom"]] + controller_types=["fly", "panzoom"] ) -# set the axes to False -figure[0, 0].axes.visible = False +# set the axes to False in the image subplot figure[0, 1].axes.visible = False -figure[0, 0].set_title(f"K-means clustering of PCA-reduced data") +figure[0, 0].title = "k-means clustering of PCA-reduced data" +figure[0, 1].title = "handwritten digit" # plot the centroids figure[0, 0].add_scatter( @@ -94,6 +97,7 @@ digit_scatter.colors[ix] = "magenta" digit_scatter.sizes[ix] = 10 + # define event handler to update the selected data point @digit_scatter.add_event_handler("pointer_enter") def update(ev): @@ -110,8 +114,10 @@ def update(ev): # update digit fig figure[0, 1]["digit"].data = data[ix].reshape(8, 8) + figure.show() + # NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": diff --git a/examples/notebooks/nb_test_utils.py b/examples/notebooks/nb_test_utils.py index f1505f98a..9d99e3be3 100644 --- a/examples/notebooks/nb_test_utils.py +++ b/examples/notebooks/nb_test_utils.py @@ -102,7 +102,7 @@ def plot_test(name, fig: fpl.Figure): # hacky but it works for now fig.imgui_renderer.render() - fig._set_viewport_rects() + fig._fpl_reset_layout() # render each subplot for subplot in fig: subplot.viewport.render(subplot.scene, subplot.camera) diff --git a/examples/notebooks/screenshots/nb-astronaut.png b/examples/notebooks/screenshots/nb-astronaut.png index 32b09caf9..2370c5988 100644 --- a/examples/notebooks/screenshots/nb-astronaut.png +++ b/examples/notebooks/screenshots/nb-astronaut.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8d9e2b0479d3de1c12764b984679dba83a1876ea6a88c072789a0e06f957ca2a -size 70655 +oid sha256:0a6e8bb3c72f1be6915e8e78c9a4f269419cfb4faded16e39b5cb11d70bec247 +size 64185 diff --git a/examples/notebooks/screenshots/nb-astronaut_RGB.png b/examples/notebooks/screenshots/nb-astronaut_RGB.png index be498bb6d..2a7eac585 100644 --- a/examples/notebooks/screenshots/nb-astronaut_RGB.png +++ b/examples/notebooks/screenshots/nb-astronaut_RGB.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e2d02877510191e951d38d03a6fe9d31f5c0c335913876c65b175c4bb1a9c0e1 -size 69942 +oid sha256:9f9f32e86018f87057435f7121b02bbe98823444babb330645bab618e1d586b7 +size 63838 diff --git a/examples/notebooks/screenshots/nb-camera.png b/examples/notebooks/screenshots/nb-camera.png index 3e9a518f9..bfe226ca4 100644 --- a/examples/notebooks/screenshots/nb-camera.png +++ b/examples/notebooks/screenshots/nb-camera.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5271c2204a928185b287c73c852ffa06b708d8d6a33de09acda8d2ea734e78c5 -size 51445 +oid sha256:2964d0150b38f990a7b804e9057f99505e8c99bb04538a13137989d540704593 +size 47456 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-set_data.png b/examples/notebooks/screenshots/nb-image-widget-movie-set_data.png index 8c353442a..2578ad028 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-set_data.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-set_data.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4d8563587c4f642d5e4edb34f41b569673d7ea71bcbafdb734369272776baeef -size 62316 +oid sha256:78e7e99fafc15cc6edf53cfb2e5b679623ad14e0d594e0ad615088e623be22e1 +size 60988 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-0-reset.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-0-reset.png index 22c7ad73a..bb2e1ee37 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-0-reset.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-0-reset.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9b122f0ba9bfff0b0868778f09744870238bf7b4945e57410b6aa36341eaaf4a -size 116781 +oid sha256:6c9b898259fc965452ef0b6ff53ac7fa41196826c6e27b6b5d417d33fb352051 +size 112399 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-0.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-0.png index 22c7ad73a..bb2e1ee37 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-0.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9b122f0ba9bfff0b0868778f09744870238bf7b4945e57410b6aa36341eaaf4a -size 116781 +oid sha256:6c9b898259fc965452ef0b6ff53ac7fa41196826c6e27b6b5d417d33fb352051 +size 112399 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-279.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-279.png index 84e2514d0..1841cd237 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-279.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-279.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fcc5092f35c881da4a9b9f3c216fb608b8dfc27a791b83e0d5184ef3973746cf -size 139375 +oid sha256:b9cbc2a6916c7518d40812a13276270eb1acfc596f3e6e02e98a6a5185da03a4 +size 132971 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-max-33.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-max-33.png index 075116ff4..6cc1821fa 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-max-33.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-max-33.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3fabd9d52ae2815ae883a4c8c8a8b1385c0824e0212347896a09eb3600c29430 -size 124238 +oid sha256:070748e90bd230a01d3ae7c6d6487815926b0158888a52db272356dc8b0a89d7 +size 119453 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-13.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-13.png index 216ae2b9e..3865aef93 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:86ad31cab3aefa24a1c4c0adc2033cbc9fa594e9cf8ab8e4a6ff0a3630bb7896 -size 109041 +oid sha256:b24450ccf1f8cf902b8e37e73907186f37a6495f227dcbd5ec53f75c52125f56 +size 105213 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-33.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-33.png index 99302d4e6..025086930 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-33.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-33.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3ebf4e875199c7e682dc15aa03a36ea9f111618853a94076064b055bf6ce788e -size 101209 +oid sha256:3dfc8e978eddf08d1ed32e16fbf93c037ccdf5f7349180dcda54578a8c9e1a18 +size 97359 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-reset.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-reset.png index 3bb5081f0..5ff5052b0 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-reset.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-reset.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d8dbf6b76818315e40d9d4cc97807c4276c27e7a9a09d2643f74adf701ef1cdc -size 123136 +oid sha256:00130242d3f199926604df16dda70a062071f002566a8056e4794805f29adfde +size 118044 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50.png index 3bb5081f0..5ff5052b0 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d8dbf6b76818315e40d9d4cc97807c4276c27e7a9a09d2643f74adf701ef1cdc -size 123136 +oid sha256:00130242d3f199926604df16dda70a062071f002566a8056e4794805f29adfde +size 118044 diff --git a/examples/notebooks/screenshots/nb-image-widget-single-gnuplot2.png b/examples/notebooks/screenshots/nb-image-widget-single-gnuplot2.png index 48ab5d6fe..e8c02adfe 100644 --- a/examples/notebooks/screenshots/nb-image-widget-single-gnuplot2.png +++ b/examples/notebooks/screenshots/nb-image-widget-single-gnuplot2.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c65e2dc4276393278ab769706f22172fd71e38eeb3c9f4d70fa51de31820f1d1 -size 234012 +oid sha256:8c8562f8e1178cf21af98af635006c64010f3c5fc615533d1df8c49479232843 +size 217693 diff --git a/examples/notebooks/screenshots/nb-image-widget-single.png b/examples/notebooks/screenshots/nb-image-widget-single.png index 5e1cb8cc1..8de4099fb 100644 --- a/examples/notebooks/screenshots/nb-image-widget-single.png +++ b/examples/notebooks/screenshots/nb-image-widget-single.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7d4e4edf1429a135bafb7c1c927ea87f78a93fb5f3e0cbee2fb5c156af88d5a0 -size 220490 +oid sha256:5c9bae3c9c5521a4054288be7ae548204fc7b0eafbc3e99cb6b649e0be797169 +size 207176 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-gaussian.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-gaussian.png index ec2911374..13297e09f 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-gaussian.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-gaussian.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:39adce1898e5b00ccf9d8792bd4e76f2da2591a8c3f6e201a5c2af1f814d37cb -size 58692 +oid sha256:70c7738ed303f5a3e19271e8dfc12ab857a6f3aff767bdbecb485b763a09913e +size 55584 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-reset.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-reset.png index ae72c8175..b8307bc44 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-reset.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-reset.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d50b960c66acc6672aaeb6a97f6a69aad14f9e54060c3702679d6a5bf2b70e78 -size 70582 +oid sha256:66a435e45dc4643135633115af2eeaf70761e408a94d70d94d80c14141574528 +size 69343 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-max-window-13.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-max-window-13.png index 66f9136dc..d6237dc9f 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-max-window-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-max-window-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d244a8a91d04380f2ebe255b2b56b3be5249c0a382821877710cae6bdaa2d414 -size 128643 +oid sha256:731f225fa2de3457956b2095d1cc539734983d041b13d6ad1a1f9d8e7ebfa4bc +size 115239 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-13.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-13.png index 230e71c0f..ecf63a369 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:24c991457b8b081ee271cbdb03821ea1024c8340db92340e0e445bf3c70aba40 -size 97903 +oid sha256:7e2d70159ac47c004acb022b3a669e7bd307299ddd590b83c08854b0dba27b70 +size 93885 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-5.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-5.png index a355670a0..e7106fae9 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-5.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bdd62a9bd1ca4f1ff110a30fb4064d778f02120295a3e3d30552e06892146e40 -size 93658 +oid sha256:1756783ab90435b46ded650033cf29ac36d2b4380744bf312caa2813267f7f38 +size 89813 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50.png index c47545ccb..ddd4f85ca 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:db7e2cf15ff3ce61b169b114c630e2339c1c6b5c687c580e1ee6964785df6790 -size 74844 +oid sha256:a35e2e4b892b55f5d2500f895951f6a0289a2df3b69cf12f59409bbc091d1caf +size 72810 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-99.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-99.png index 69ef49149..d9971c3fd 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-99.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-99.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:64d2d3fd91ac8e10736add5a82a312927ae6f976119cfa2aaa1fc1e008bcf6f1 -size 66038 +oid sha256:3bdb0ed864c8a6f2118cfe0d29476f61c54576f7b8e041f3c3a895ba0a440c05 +size 65039 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-gaussian.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-gaussian.png index bb04d1800..6736e108c 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-gaussian.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-gaussian.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8d2a805c85e1cdf5bd2d995600b033019680ac645d7634efeaf1db7d0d00d4aa -size 79403 +oid sha256:7ae7c86bee3a30bde6cfa44e1e583e6dfd8de6bb29e7c86cea9141ae30637b4a +size 80627 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-reset.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-reset.png index 5b1a4a8da..dce99223b 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-reset.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-reset.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:440623bb4588994c4f52f17e027af29d1f2d5d330c5691630fd2acb9e38f8a25 -size 99033 +oid sha256:b51a5d26f2408748e59e3ee481735694f8f376539b50deb2b5c5a864b7de1079 +size 105581 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-max-window-13.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-max-window-13.png index bd72160dd..cdea3673d 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-max-window-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-max-window-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9ee56adf8f2a516ef74a799e9e503f980c36c4dfb41f9ff6d8168cfcf65ad092 -size 132745 +oid sha256:e854f7f2fdaeeac6c8358f94a33698b5794c0f6c55b240d384e8c6d51fbfb0ff +size 143301 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-13.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-13.png index 438d1e2d4..25a2fa53e 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:de4733b82c2e77baa659582401eff0c70ec583c50462b33bcbfd2bb00ceaa517 -size 102959 +oid sha256:c8c8d3c59c145a4096deceabc71775a03e5e121e82509590787c768944d155bd +size 110744 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-5.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-5.png index ee081c6df..00a4a1fd2 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-5.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6107f108b5a86ba376c53f5e207841c01a85b686100efb46e5df3565127201d2 -size 106765 +oid sha256:c4b4af7b99cad95ea3f688af8633de24b6602bd700cb244f93c28718af2e1e85 +size 114982 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50.png index c2071c850..3b5594c64 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:caa15f6bc21a3be0f480b442414ec4b96b21cc1de2cdcb891b366692962d4ef8 -size 100753 +oid sha256:6d28a4be4c76d5c0da5f5767b169acf7048a268b010f33f96829a5de7f06fd7d +size 107477 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-99.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-99.png index 3d90fd77a..239237b45 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-99.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-99.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4e23288d695a5a91188b285f6a0a2c9f0643dd19f3d6dedb56f4389f44ed1f44 -size 98621 +oid sha256:30dba982c9a605a7a3c0f2fa6d8cdf0df4160b2913a95b26ffdb6b04ead12add +size 104603 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-init-mean-window-5.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-init-mean-window-5.png index 3fd5688d9..0745a4d4a 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-init-mean-window-5.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-init-mean-window-5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8b4e1bb60466d7553b4d1afc14015b7c4edc6e79c724c0afb5acd123a10093d0 -size 105541 +oid sha256:e431229806ee32a78fb9313a09af20829c27799798232193feab1723b66b1bca +size 112646 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-false.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-false.png index 048078520..498b19cb7 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-false.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-false.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:33ce1260b4715b3d28ba28d0ad4c5eb94c9997bdc1676ff6208121e789e168a5 -size 99287 +oid sha256:a8e899b48881e3eb9200cc4e776db1f865b0911c340c06d4009b3ae12aa1fc85 +size 105421 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-true.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-true.png index ade8fb483..369168141 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-true.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-true.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5e08f4e4cb3330fbbbf827af56c02039af3b293036c7676f2a87c309ad07f2f6 -size 99759 +oid sha256:93933e7ba5f791072df2934c94a782e39ed97f7db5b55c5d71c8c5bbfc69d800 +size 106360 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-init-mean-window-5.png b/examples/notebooks/screenshots/nb-image-widget-zfish-init-mean-window-5.png index 14d9e8448..b62721be2 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-init-mean-window-5.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-init-mean-window-5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3aad82db14f8100f669d2ad36b5bc3973b7c12457adfdd73adbc81c759338f7b -size 80964 +oid sha256:bf38b2af1ceb372cd0949d42c027acb5fcc4c6b9a8f38c5aacdce1cd14e290fe +size 78533 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-frame-50.png b/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-frame-50.png index af04a6f73..76ed01a7c 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-frame-50.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-frame-50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e40559eea03790315718c55b4ec4976aacb97a2f07bcdc49d917c044745687c2 -size 117144 +oid sha256:ff462d24820f0bdd509e58267071fa956b5c863b8b8d66fea061c5253b7557cf +size 113926 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-set-data.png b/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-set-data.png index 7f530e554..d9a593ee7 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-set-data.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-set-data.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:414ebe9a0b2bc4eb1caa4b4aeef070955c662bb691899c4b2046be3e2ca821e3 -size 113649 +oid sha256:2b8fd14f8e8a90c3cd3fbb84a00d50b1b826b596d64dfae4a5ea1bab0687d906 +size 110829 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-windowrgb.png b/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-windowrgb.png index e2f6b8318..cf10c6d42 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-windowrgb.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-windowrgb.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ea6d0c4756db434af6e257b7cd809f1d49089eca6b9eae9e347801e20b175686 -size 113631 +oid sha256:d88c64b716d19a3978bd60f8d75ffe09e022183381898fa1c48b77598be8fb7c +size 111193 diff --git a/examples/notebooks/screenshots/nb-lines-3d.png b/examples/notebooks/screenshots/nb-lines-3d.png index 2e26a8cd7..fb84ef21a 100644 --- a/examples/notebooks/screenshots/nb-lines-3d.png +++ b/examples/notebooks/screenshots/nb-lines-3d.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:857eb528b02fd7dd4b9f46ce1e65942066736f1bdf5271db141d73a0abab82b0 -size 19457 +oid sha256:c70c01b3ade199864df227a44fb28a53626df3beecee722a7b782c9a9f4658d8 +size 19907 diff --git a/examples/notebooks/screenshots/nb-lines-colors.png b/examples/notebooks/screenshots/nb-lines-colors.png index 1e13983f3..ab221d83f 100644 --- a/examples/notebooks/screenshots/nb-lines-colors.png +++ b/examples/notebooks/screenshots/nb-lines-colors.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6681a1e5658c1f2214217dcb7321cad89c7a0a3fd7919296a1069f27f1a7ee92 -size 35381 +oid sha256:3b238b085eddb664ff56bd265423d85b35fc70769ebec050b27fefa8fe6380de +size 35055 diff --git a/examples/notebooks/screenshots/nb-lines-data.png b/examples/notebooks/screenshots/nb-lines-data.png index a7e8287ef..44b142f55 100644 --- a/examples/notebooks/screenshots/nb-lines-data.png +++ b/examples/notebooks/screenshots/nb-lines-data.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:043d8d9cd6dfc7627a6ccdb5810efd4b1a15e8880a4e30c0f558ae4d67c2f470 -size 42410 +oid sha256:4df736ec3ea90478930a77437949977f8e30f7d9272f65ef9f4908f2103dd11e +size 40679 diff --git a/examples/notebooks/screenshots/nb-lines-underlay.png b/examples/notebooks/screenshots/nb-lines-underlay.png index c2908d479..f4a5b4e76 100644 --- a/examples/notebooks/screenshots/nb-lines-underlay.png +++ b/examples/notebooks/screenshots/nb-lines-underlay.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c52ac60ffc08005d1f1fcad1b29339a89a0f31b58c9ca692f9d93400e7c8ac9e -size 48540 +oid sha256:3a8b59386015b4c1eaa85c33c7b041d566ac1ac76fbba829075e9a3af021bedf +size 46228 diff --git a/examples/notebooks/screenshots/nb-lines.png b/examples/notebooks/screenshots/nb-lines.png index f4a4d58b1..8c86b48d0 100644 --- a/examples/notebooks/screenshots/nb-lines.png +++ b/examples/notebooks/screenshots/nb-lines.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2cef0e2fb84e985f8d9c18f77817fb3eba31bd30b8fa4c54bb71432587909458 -size 30075 +oid sha256:823558e877830b816cc87df0776a92d5316d98a4f40e475cbf997b597c5eb8de +size 30338 diff --git a/examples/notebooks/screenshots/no-imgui-nb-astronaut.png b/examples/notebooks/screenshots/no-imgui-nb-astronaut.png index a1e524e2a..9f9e2013a 100644 --- a/examples/notebooks/screenshots/no-imgui-nb-astronaut.png +++ b/examples/notebooks/screenshots/no-imgui-nb-astronaut.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:915f6c4695c932dc2aa467be750e58a0435fe86fe0e0fa5a52c6065e05ec3193 -size 85456 +oid sha256:4758a94e6c066d95569515c0bff8e4c9ec383c65c5928a827550c142214df085 +size 72372 diff --git a/examples/notebooks/screenshots/no-imgui-nb-astronaut_RGB.png b/examples/notebooks/screenshots/no-imgui-nb-astronaut_RGB.png index ec3208e01..23d1bd906 100644 --- a/examples/notebooks/screenshots/no-imgui-nb-astronaut_RGB.png +++ b/examples/notebooks/screenshots/no-imgui-nb-astronaut_RGB.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:31cfa60229a4e297be507a8888e08d6950c2a7d4b323d34774c9462419272ada -size 84284 +oid sha256:fb3c72edc6f41d6f77e44bc68e7f5277525d2548d369925827c14d855dc33bbd +size 71588 diff --git a/examples/notebooks/screenshots/no-imgui-nb-camera.png b/examples/notebooks/screenshots/no-imgui-nb-camera.png index 31b60d9c0..22c70a760 100644 --- a/examples/notebooks/screenshots/no-imgui-nb-camera.png +++ b/examples/notebooks/screenshots/no-imgui-nb-camera.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:800845fae18093945ed921237c8756b1afa31ee391fe679b03c57a67929e4ba9 -size 60087 +oid sha256:6de3880cc22a8f6cdb77305e4d5be520fe92fd54a9a107bdbddf1e6f72c19262 +size 52157 diff --git a/examples/notebooks/screenshots/no-imgui-nb-lines-3d.png b/examples/notebooks/screenshots/no-imgui-nb-lines-3d.png index 35c777e6a..1a5a7b548 100644 --- a/examples/notebooks/screenshots/no-imgui-nb-lines-3d.png +++ b/examples/notebooks/screenshots/no-imgui-nb-lines-3d.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f4253362c0908e0d983542be3691a3d94f27a0319fb9e7183315c77891dac140 -size 23232 +oid sha256:f0e63c918aac713af2015cb85289c9451be181400834b0f60bcbb50564551f08 +size 20546 diff --git a/examples/notebooks/screenshots/no-imgui-nb-lines-colors.png b/examples/notebooks/screenshots/no-imgui-nb-lines-colors.png index b8e34aab3..cdce4bf46 100644 --- a/examples/notebooks/screenshots/no-imgui-nb-lines-colors.png +++ b/examples/notebooks/screenshots/no-imgui-nb-lines-colors.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dc95d6291d06ab64d142ba0048318caefa28b404bb4b31635df075dc651eaa08 -size 37276 +oid sha256:2bd481f558907ac1af97bd7ee08d58951bada758cc32467c73483fa66e4602f8 +size 36206 diff --git a/examples/notebooks/screenshots/no-imgui-nb-lines-data.png b/examples/notebooks/screenshots/no-imgui-nb-lines-data.png index 8f58dbc6d..8923be766 100644 --- a/examples/notebooks/screenshots/no-imgui-nb-lines-data.png +++ b/examples/notebooks/screenshots/no-imgui-nb-lines-data.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b8aa0b8303f0a69609198ea312800fc0eb98007c18d0ebc37672a9cf4f1cbaff -size 46780 +oid sha256:ea39e2651408431ad5e49af378828a41b7b377f7f0098adc8ce2c7b5e10d0234 +size 43681 diff --git a/examples/notebooks/screenshots/no-imgui-nb-lines-underlay.png b/examples/notebooks/screenshots/no-imgui-nb-lines-underlay.png index b33cde5a6..b6b4cf340 100644 --- a/examples/notebooks/screenshots/no-imgui-nb-lines-underlay.png +++ b/examples/notebooks/screenshots/no-imgui-nb-lines-underlay.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:822410f43d48d12e70930b5b581bafe624ea72475d53ca0d98cdaa5649338c63 -size 51849 +oid sha256:6a8d4aba2411598ecae1b7f202fbb1a1fa7416a814b7b4c5fdd1e0e584cdb06a +size 49343 diff --git a/examples/notebooks/screenshots/no-imgui-nb-lines.png b/examples/notebooks/screenshots/no-imgui-nb-lines.png index 5d7e704ca..5d03421a4 100644 --- a/examples/notebooks/screenshots/no-imgui-nb-lines.png +++ b/examples/notebooks/screenshots/no-imgui-nb-lines.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8e3ba744fcfa43df839fddce88f79fb8d7c5eafdd22f271e6b885e09b8891072 -size 31222 +oid sha256:b2fdaf79703c475521184ab9dc948d3e817160b0162e9d88fcb20207225d0233 +size 31153 diff --git a/examples/scatter/spinning_spiral.py b/examples/scatter/spinning_spiral.py new file mode 100644 index 000000000..c032fc1c8 --- /dev/null +++ b/examples/scatter/spinning_spiral.py @@ -0,0 +1,62 @@ +""" +Spinning spiral scatter +======================= + +Example of a spinning spiral scatter +""" + +# test_example = false +# sphinx_gallery_pygfx_docs = 'animate 10s' + +import numpy as np +import fastplotlib as fpl + +# number of points +n = 100_000 + +# create data in the shape of a spiral +phi = np.linspace(0, 30, n) + +xs = phi * np.cos(phi) + np.random.normal(scale=1.5, size=n) +ys = np.random.normal(scale=1, size=n) +zs = phi * np.sin(phi) + np.random.normal(scale=1.5, size=n) + +data = np.column_stack([xs, ys, zs]) + +figure = fpl.Figure(cameras="3d", size=(700, 560)) + +spiral = figure[0, 0].add_scatter(data, cmap="viridis_r", alpha=0.8) + + +def update(): + # rotate around y axis + spiral.rotate(0.005, axis="y") + # add small jitter + spiral.data[:] += np.random.normal(scale=0.01, size=n * 3).reshape((n, 3)) + + +figure.add_animations(update) +figure.show() + +# pre-saved camera state +camera_state = { + 'position': np.array([-0.13046005, 20.09142224, 29.03347696]), + 'rotation': np.array([-0.44485092, 0.05335406, 0.11586037, 0.88647469]), + 'scale': np.array([1., 1., 1.]), + 'reference_up': np.array([0., 1., 0.]), + 'fov': 50.0, + 'width': 62.725074768066406, + 'height': 8.856056690216064, + 'zoom': 0.75, + 'maintain_aspect': True, + 'depth_range': None +} +figure[0, 0].camera.set_state(camera_state) +figure[0, 0].axes.visible = False + + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.loop.run() diff --git a/examples/screenshots/extent_frac_layout.png b/examples/screenshots/extent_frac_layout.png new file mode 100644 index 000000000..7fe6d3d37 --- /dev/null +++ b/examples/screenshots/extent_frac_layout.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5991b755432318310cfc2b4826bd9639cc234883aa06f1895817f710714cb58f +size 156297 diff --git a/examples/screenshots/extent_layout.png b/examples/screenshots/extent_layout.png new file mode 100644 index 000000000..dec391ac2 --- /dev/null +++ b/examples/screenshots/extent_layout.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0cf23f845932023789e0823a105910e9f701d0f03c04e3c18488f0da62420921 +size 123409 diff --git a/examples/screenshots/gridplot.png b/examples/screenshots/gridplot.png index 1a222affd..08e6d6b78 100644 --- a/examples/screenshots/gridplot.png +++ b/examples/screenshots/gridplot.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8de769538bb435b71b33e038998b2bafa340c635211c0dfc388c7a5bf55fd36d -size 286794 +oid sha256:6f424ec68dbc0761566cd147f3bf5b8f15e4126c3b30b2ff47b6fb48f04d512a +size 252269 diff --git a/examples/screenshots/gridplot_non_square.png b/examples/screenshots/gridplot_non_square.png index 45d71abb2..781de8749 100644 --- a/examples/screenshots/gridplot_non_square.png +++ b/examples/screenshots/gridplot_non_square.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:92f55da7e2912a68e69e212b31df760f27e72253ec234fe1dd5b5463b60061b3 -size 212647 +oid sha256:9ac9ee6fd1118a06a1f0de4eee73e7b6bee188c533da872c5cbaf7119114414f +size 194385 diff --git a/examples/screenshots/gridplot_viewports_check.png b/examples/screenshots/gridplot_viewports_check.png index 050067e22..b1faf9b69 100644 --- a/examples/screenshots/gridplot_viewports_check.png +++ b/examples/screenshots/gridplot_viewports_check.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:250959179b0f998e1c586951864e9cbce3ac63bf6d2e12a680a47b9b1be061a1 -size 46456 +oid sha256:67dd50d61a0caaf563d95110f99fa24c567ddd778a697715247d697a1b5bb1ac +size 46667 diff --git a/examples/screenshots/heatmap.png b/examples/screenshots/heatmap.png index a63eb5ec8..defcca301 100644 --- a/examples/screenshots/heatmap.png +++ b/examples/screenshots/heatmap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1f2f0699e01eb12c44a2dbefd1d8371b86b3b3456b28cb5f1850aed44c13f412 -size 94505 +oid sha256:0789d249cb4cfad21c9f1629721ade26ed734e05b1b13c3a5871793f6271362b +size 91831 diff --git a/examples/screenshots/image_cmap.png b/examples/screenshots/image_cmap.png index 6f7081b03..0301d2ed4 100644 --- a/examples/screenshots/image_cmap.png +++ b/examples/screenshots/image_cmap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e1482ce72511bc4f815825c29fabac5dd0f2586ac4c827a220a5cecb1162be4d -size 210019 +oid sha256:d2bbb79716fecce08479fbe7977565daccadf4688c8a99e155db297ecce4c484 +size 199979 diff --git a/examples/screenshots/image_rgb.png b/examples/screenshots/image_rgb.png index 88beb7df3..11129ceaa 100644 --- a/examples/screenshots/image_rgb.png +++ b/examples/screenshots/image_rgb.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8210ad8d1755f7819814bdaaf236738cdf1e9a0c4f77120aca4968fcd8aa8a7a -size 239431 +oid sha256:23024936931651cdf4761f2cafcd8002bb12ab86e9efb13ddc99a9bf659c3935 +size 226879 diff --git a/examples/screenshots/image_rgbvminvmax.png b/examples/screenshots/image_rgbvminvmax.png index f3ef59d84..afe4de6f7 100644 --- a/examples/screenshots/image_rgbvminvmax.png +++ b/examples/screenshots/image_rgbvminvmax.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8ebbcc4a2e83e9733eb438fe2341f77c86579421f3fa96b6a49e94073c0ffd32 -size 48270 +oid sha256:2fb9cd6d32813df6a9e3bf183f73cb69fdb61d290d7f2a4cc223ab34301351a1 +size 50231 diff --git a/examples/screenshots/image_simple.png b/examples/screenshots/image_simple.png index 0c7e011f4..702a1ac5c 100644 --- a/examples/screenshots/image_simple.png +++ b/examples/screenshots/image_simple.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:44bc2d1fd97921fef0be45424f21513d5d978b807db8cf148dfc59c07f6e292f -size 211333 +oid sha256:b3eb6f03364226e9f1aae72f6414ad05b0239a15c2a0fbcd71d3718fee477e2c +size 199468 diff --git a/examples/screenshots/image_small.png b/examples/screenshots/image_small.png index 41a4a240e..d17cb7ab2 100644 --- a/examples/screenshots/image_small.png +++ b/examples/screenshots/image_small.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:079ee6254dc995cc5fc8c20ff1c00cb0899f21ba2d5d1a4dc0d020c3a71902c4 -size 13022 +oid sha256:2dcfc7b8a964db9a950bf4d3217fb171d081251b107977f9acd612fcd5fb0be1 +size 14453 diff --git a/examples/screenshots/image_vminvmax.png b/examples/screenshots/image_vminvmax.png index f3ef59d84..afe4de6f7 100644 --- a/examples/screenshots/image_vminvmax.png +++ b/examples/screenshots/image_vminvmax.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8ebbcc4a2e83e9733eb438fe2341f77c86579421f3fa96b6a49e94073c0ffd32 -size 48270 +oid sha256:2fb9cd6d32813df6a9e3bf183f73cb69fdb61d290d7f2a4cc223ab34301351a1 +size 50231 diff --git a/examples/screenshots/image_widget.png b/examples/screenshots/image_widget.png index af248dd3e..23d34ae50 100644 --- a/examples/screenshots/image_widget.png +++ b/examples/screenshots/image_widget.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e2ae1938c5e7b742fb2dac0336877028f6ece26cd80e84f309195a55601025cb -size 197495 +oid sha256:220ebb5286b48426f9457b62d6e7f9fe61b5a62b8874c7e010e07e146ae205a5 +size 184633 diff --git a/examples/screenshots/image_widget_grid.png b/examples/screenshots/image_widget_grid.png index e0f0ff5c8..45bc70726 100644 --- a/examples/screenshots/image_widget_grid.png +++ b/examples/screenshots/image_widget_grid.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:eeb5b86e7c15dfe2e71267453426930200223026f72156f34ff1ccc2f9389b6e -size 253769 +oid sha256:306977f7eebdb652828ba425d73b6018e97c100f3cf8f3cbaa0244ffb6c040a3 +size 249103 diff --git a/examples/screenshots/image_widget_imgui.png b/examples/screenshots/image_widget_imgui.png index 135a0d4c4..cb165cc86 100644 --- a/examples/screenshots/image_widget_imgui.png +++ b/examples/screenshots/image_widget_imgui.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7e2cd0e3892377e6e2d552199391fc64aac6a02413168a5b4c5c4848f3390dec -size 166265 +oid sha256:7522a35768d013a257e3cf3b00cce626b023b169484e035f46c635efc553b0bf +size 165747 diff --git a/examples/screenshots/image_widget_single_video.png b/examples/screenshots/image_widget_single_video.png index 5d10d91a6..aa757a950 100644 --- a/examples/screenshots/image_widget_single_video.png +++ b/examples/screenshots/image_widget_single_video.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:de1750c9c1c3cd28c356fb51687f4a8f00afb3cc7e365502342168fce8459d3a -size 90307 +oid sha256:5f0843f4693460ae985c1f33d84936fbcc943d0405e0893186cbee7a5765dbc0 +size 90283 diff --git a/examples/screenshots/image_widget_videos.png b/examples/screenshots/image_widget_videos.png index f0e262e24..2e289ae3c 100644 --- a/examples/screenshots/image_widget_videos.png +++ b/examples/screenshots/image_widget_videos.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:23d993e0b5b6bcfe67da7aa4ceab3f06e99358b00f287b9703c4c3bff19648ba -size 169541 +oid sha256:eec22392f85db1fd375d7ffa995a2719cf86821fe3fe85913f4ab66084eccbf9 +size 290587 diff --git a/examples/screenshots/image_widget_viewports_check.png b/examples/screenshots/image_widget_viewports_check.png index 6bfbc0153..662432e59 100644 --- a/examples/screenshots/image_widget_viewports_check.png +++ b/examples/screenshots/image_widget_viewports_check.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:27e8aaab0085d15965649f0a4b367e313bab382c13b39de0354d321398565a46 -size 99567 +oid sha256:1c4449f7e97375aa9d7fe1d00364945fc86b568303022157621de21a20d1d13e +size 93914 diff --git a/examples/screenshots/imgui_basic.png b/examples/screenshots/imgui_basic.png index 27288e38f..1ff9952a9 100644 --- a/examples/screenshots/imgui_basic.png +++ b/examples/screenshots/imgui_basic.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3391b7cf02fc7bd2c73dc57214b21ceaca9a1513556b3a4725639f21588824e4 -size 36261 +oid sha256:09cc7b0680e53ae1a2689b63f9b0ed641535fcffc99443cd455cc8d9b6923229 +size 36218 diff --git a/examples/screenshots/line.png b/examples/screenshots/line.png index 492ea2ada..02603b692 100644 --- a/examples/screenshots/line.png +++ b/examples/screenshots/line.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1458d472362f8d5bcef599fd64f931997a246f9e7649c80cc95f465cbd858850 -size 170243 +oid sha256:9bfaa54bde0967463413ecd2defa8ca18169d534163cc8b297879900e812fee8 +size 167012 diff --git a/examples/screenshots/line_cmap.png b/examples/screenshots/line_cmap.png index 10779fcd5..1ecc930e4 100644 --- a/examples/screenshots/line_cmap.png +++ b/examples/screenshots/line_cmap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:66e64835f824d80dd7606d90530517dbc320bcc11a68393ab92c08fef3d23f5a -size 48828 +oid sha256:d0503c008f8869dcf83793c21b15169a93558988c1a5c4edfd2aa93c549d25e1 +size 49343 diff --git a/examples/screenshots/line_cmap_more.png b/examples/screenshots/line_cmap_more.png index 56e3fe8cc..4bf597e8b 100644 --- a/examples/screenshots/line_cmap_more.png +++ b/examples/screenshots/line_cmap_more.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:de08452e47799d9afcadfc583e63da1c02513cf73000bd5c2649236e61ed6b34 -size 126725 +oid sha256:ab4d759dd679a2959c0fda724e7b7a1b7593d6f67ce797f08a5292dd0eb74fb1 +size 125023 diff --git a/examples/screenshots/line_collection.png b/examples/screenshots/line_collection.png index d9124daf1..382132770 100644 --- a/examples/screenshots/line_collection.png +++ b/examples/screenshots/line_collection.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:50920f4bc21bb5beffe317777a20d8d09f90f3631a14df51c219814d3507c602 -size 100758 +oid sha256:b3b6b973a52f7088536a4f437be2a7f6ebb2787756f9170145a945c53e90093c +size 98950 diff --git a/examples/screenshots/line_collection_cmap_values.png b/examples/screenshots/line_collection_cmap_values.png index e04289699..c00bffdb6 100644 --- a/examples/screenshots/line_collection_cmap_values.png +++ b/examples/screenshots/line_collection_cmap_values.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:850e3deb2220d44f01e6366ee7cffb83085cad933a137b9838ce8c2231e7786a -size 64152 +oid sha256:45bb6652f477ab0165bf59e504c1935e5781bceea9a891fcfa9975dec92eef4b +size 64720 diff --git a/examples/screenshots/line_collection_cmap_values_qualitative.png b/examples/screenshots/line_collection_cmap_values_qualitative.png index 710cee119..662d3254d 100644 --- a/examples/screenshots/line_collection_cmap_values_qualitative.png +++ b/examples/screenshots/line_collection_cmap_values_qualitative.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ba5fefc8e1043fe0ebd926a6b8e6ab19e724205a4c13e4d7740122cfe464e38b -size 67017 +oid sha256:4e5b5cb45e78ae24d72f3cb84e482fac7bf0a98cd9b9b934444d2e67c9910d57 +size 66565 diff --git a/examples/screenshots/line_collection_colors.png b/examples/screenshots/line_collection_colors.png index 6c1d05f04..3b90e5b4c 100644 --- a/examples/screenshots/line_collection_colors.png +++ b/examples/screenshots/line_collection_colors.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:17d48f07310090b835e5cd2e6fa9c178db9af8954f4b0a9d52d21997ec229abd -size 57778 +oid sha256:4edf84af27535e4a30b48906ab3cacaeb38d073290828df3c5707620e222b4d3 +size 58635 diff --git a/examples/screenshots/line_collection_slicing.png b/examples/screenshots/line_collection_slicing.png index abb63760f..e0537a261 100644 --- a/examples/screenshots/line_collection_slicing.png +++ b/examples/screenshots/line_collection_slicing.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ed0d4fdb729409d07ec9ec9e05d915a04ebb237087d266591e7f46b0838e05b3 -size 130192 +oid sha256:66933c1fa349ebb4dd69b9bf396acb8f0aeeabbf17a3b7054d1f1e038a6e04be +size 129484 diff --git a/examples/screenshots/line_colorslice.png b/examples/screenshots/line_colorslice.png index 1f100d89e..f3374e221 100644 --- a/examples/screenshots/line_colorslice.png +++ b/examples/screenshots/line_colorslice.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1b2c5562f4150ec69029a4a139469b0a2524a14078b78055df40d9b487946ce5 -size 57037 +oid sha256:d654aa666ac1f4cfbf228fc4c5fbd2f68eed841c7cc6265637d5b836b918314c +size 57989 diff --git a/examples/screenshots/line_dataslice.png b/examples/screenshots/line_dataslice.png index b2f963195..6ecf63b26 100644 --- a/examples/screenshots/line_dataslice.png +++ b/examples/screenshots/line_dataslice.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c31a12afa3e66c442e370e6157ad9a5aad225b21f0f95fb6a115066b1b4f2e73 -size 68811 +oid sha256:a9b93af2028eb0186dd75d74c079d5effdb284a8677e6eec1a7fd2c8de4c8498 +size 70489 diff --git a/examples/screenshots/line_stack.png b/examples/screenshots/line_stack.png index 786f434be..9a9ad4fd6 100644 --- a/examples/screenshots/line_stack.png +++ b/examples/screenshots/line_stack.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fcfa7c49d465ff9cfe472ee885bcc9d9a44106b82adfc151544847b95035d760 -size 121640 +oid sha256:4b6c2d1ee4c49ff5b193b5105b2794c6b5bd7a089a8a2c6fa03e09e02352aa65 +size 121462 diff --git a/examples/screenshots/linear_region_selectors_match_offsets.png b/examples/screenshots/linear_region_selectors_match_offsets.png index 9d2371403..327f14e72 100644 --- a/examples/screenshots/linear_region_selectors_match_offsets.png +++ b/examples/screenshots/linear_region_selectors_match_offsets.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1f12310c09c4e84ea2c6f8245d1aa0ce9389a3d9637d7d4f9dc233bea173a0e3 -size 95366 +oid sha256:8fac4f439b34a5464792588b77856f08c127c0ee06fa77722818f8d6b48dd64c +size 95433 diff --git a/examples/screenshots/no-imgui-extent_frac_layout.png b/examples/screenshots/no-imgui-extent_frac_layout.png new file mode 100644 index 000000000..4dc3b2aa6 --- /dev/null +++ b/examples/screenshots/no-imgui-extent_frac_layout.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5923e8b9f687f97d488b282b35f16234898ed1038b0737b7b57fb9cbd72ebf34 +size 157321 diff --git a/examples/screenshots/no-imgui-extent_layout.png b/examples/screenshots/no-imgui-extent_layout.png new file mode 100644 index 000000000..16d1ff446 --- /dev/null +++ b/examples/screenshots/no-imgui-extent_layout.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c2ffe0a8d625322cc22d2abdde80a3f179f01552dde974bbbd49f9e371ab39aa +size 138936 diff --git a/examples/screenshots/no-imgui-gridplot.png b/examples/screenshots/no-imgui-gridplot.png index 45571161d..7f870cf76 100644 --- a/examples/screenshots/no-imgui-gridplot.png +++ b/examples/screenshots/no-imgui-gridplot.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a27ccf2230628980d16ab22a17df64504268da35a27cd1adb44102e64df033af -size 329247 +oid sha256:b31f2002053b5934ae78393214e67717d10bd567e590212eaff4062440657acd +size 292558 diff --git a/examples/screenshots/no-imgui-gridplot_non_square.png b/examples/screenshots/no-imgui-gridplot_non_square.png index f8c307c22..e08d64805 100644 --- a/examples/screenshots/no-imgui-gridplot_non_square.png +++ b/examples/screenshots/no-imgui-gridplot_non_square.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:58f50c4fc1b00c9e78c840193d1e15d008b9fe1e7f2a3d8b90065be91e2178f5 -size 236474 +oid sha256:c9ef00db82a3559b4d7c77b68838f5876f98a2b9e80ef9ecb257f32c62161b5e +size 216512 diff --git a/examples/screenshots/no-imgui-gridplot_viewports_check.png b/examples/screenshots/no-imgui-gridplot_viewports_check.png index 8dea071d0..2a8c0dc6f 100644 --- a/examples/screenshots/no-imgui-gridplot_viewports_check.png +++ b/examples/screenshots/no-imgui-gridplot_viewports_check.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0cda256658e84b14b48bf5151990c828092ff461f394fb9e54341ab601918aa1 -size 45113 +oid sha256:6818a7c8bdb29567bb09cfe00acaa6872a046d4d35a87ef2be7afa06c2a8a089 +size 44869 diff --git a/examples/screenshots/no-imgui-heatmap.png b/examples/screenshots/no-imgui-heatmap.png index 3d1cf5ef2..e91d06c4f 100644 --- a/examples/screenshots/no-imgui-heatmap.png +++ b/examples/screenshots/no-imgui-heatmap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4fac55efd9339b180b9e34d5cf244c473d6439e57e34f272c1a7e59183f1afa2 -size 98573 +oid sha256:875c15e74e7ea2eaa6b00ddbdd80b4775ecb1fe0002a5122371d49f975369cce +size 95553 diff --git a/examples/screenshots/no-imgui-image_cmap.png b/examples/screenshots/no-imgui-image_cmap.png index 6c565ca2b..2d42899fc 100644 --- a/examples/screenshots/no-imgui-image_cmap.png +++ b/examples/screenshots/no-imgui-image_cmap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:82f7176a61e2c6953c22171bea561845bb79cb8179d76b20eef2b2cc475bbb23 -size 237327 +oid sha256:2b43bd64ceec8c5c1287a2df57abf7bd148955d6ba97a425b32ae53bad03a051 +size 216050 diff --git a/examples/screenshots/no-imgui-image_rgb.png b/examples/screenshots/no-imgui-image_rgb.png index 355238724..6be5205ac 100644 --- a/examples/screenshots/no-imgui-image_rgb.png +++ b/examples/screenshots/no-imgui-image_rgb.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fce532d713d2c664eb3b676e0128060ebf17241387134812b490d3ad398d42c2 -size 269508 +oid sha256:42516cd0719d5b33ec32523dd2efe7874398bac6d0aecb5163ff1cb5c105135f +size 244717 diff --git a/examples/screenshots/no-imgui-image_rgbvminvmax.png b/examples/screenshots/no-imgui-image_rgbvminvmax.png index 6282f2438..48d8fff95 100644 --- a/examples/screenshots/no-imgui-image_rgbvminvmax.png +++ b/examples/screenshots/no-imgui-image_rgbvminvmax.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:42e01469f0f7da37d3c1c90225bf7c03c44badd1f3612ac9bf88eaed5eeb6850 -size 50145 +oid sha256:7f8a99a9172ae5edf98f0d189455fad2074a99f2280c9352675bab8d4c0e3491 +size 50751 diff --git a/examples/screenshots/no-imgui-image_simple.png b/examples/screenshots/no-imgui-image_simple.png index d00a166ce..1e4487757 100644 --- a/examples/screenshots/no-imgui-image_simple.png +++ b/examples/screenshots/no-imgui-image_simple.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c8bb29f192617b9dde2490ce36c69bd8352b6ba5d068434bc53edaad91871356 -size 237960 +oid sha256:3cfa6469803f44a682c9ce7337ae265a8d60749070991e6f3a723eb37c5a9a23 +size 215410 diff --git a/examples/screenshots/no-imgui-image_small.png b/examples/screenshots/no-imgui-image_small.png index aca14cd69..3613a8139 100644 --- a/examples/screenshots/no-imgui-image_small.png +++ b/examples/screenshots/no-imgui-image_small.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e1ea4bcf76158169bc06973457ea09997c13ecd4a91e6e634566beb31348ef68 -size 13194 +oid sha256:17ccf0014c7ba7054440e3daf8d4e2a397e9013d1aea804c40dc7302dad4171e +size 13327 diff --git a/examples/screenshots/no-imgui-image_vminvmax.png b/examples/screenshots/no-imgui-image_vminvmax.png index 6282f2438..48d8fff95 100644 --- a/examples/screenshots/no-imgui-image_vminvmax.png +++ b/examples/screenshots/no-imgui-image_vminvmax.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:42e01469f0f7da37d3c1c90225bf7c03c44badd1f3612ac9bf88eaed5eeb6850 -size 50145 +oid sha256:7f8a99a9172ae5edf98f0d189455fad2074a99f2280c9352675bab8d4c0e3491 +size 50751 diff --git a/examples/screenshots/no-imgui-line.png b/examples/screenshots/no-imgui-line.png index 29610c612..cdc24e382 100644 --- a/examples/screenshots/no-imgui-line.png +++ b/examples/screenshots/no-imgui-line.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:709458b03d535bcf407fdae1720ccdcd11a5f79ccf673e85c7e64c5748f6d25e -size 173422 +oid sha256:d3952cf9b0c9d008a885dc4abb3aeaaed6fd94a5db05ba83c6f4c4c76fe6e925 +size 171519 diff --git a/examples/screenshots/no-imgui-line_cmap.png b/examples/screenshots/no-imgui-line_cmap.png index 9340e191e..4f2bbba43 100644 --- a/examples/screenshots/no-imgui-line_cmap.png +++ b/examples/screenshots/no-imgui-line_cmap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:69426f5aac61e59a08764626b2aded602e576479e652d76b6b3bf646e3218cc1 -size 48028 +oid sha256:d3c9ac8d2b8157ffd575e5ad2b2bb23b684b52403c2f4f021c52d100cfb28a83 +size 49048 diff --git a/examples/screenshots/no-imgui-line_cmap_more.png b/examples/screenshots/no-imgui-line_cmap_more.png index f0cea4ec1..8125be49f 100644 --- a/examples/screenshots/no-imgui-line_cmap_more.png +++ b/examples/screenshots/no-imgui-line_cmap_more.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:df9a2ef9d54b417e0387116eb6e6215c54b7c939867d0d62c768768baae27e5f -size 129510 +oid sha256:5ddd88200aa824d4e05ba3f94fdb4216a1e7c7137b202cd8fb47997453dfd5a6 +size 126830 diff --git a/examples/screenshots/no-imgui-line_collection.png b/examples/screenshots/no-imgui-line_collection.png index ca74d3362..a31cf55fe 100644 --- a/examples/screenshots/no-imgui-line_collection.png +++ b/examples/screenshots/no-imgui-line_collection.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:90f281301e8b23a22a5333e7b34316475907ac25ffc9a23b7395b7431c965343 -size 106518 +oid sha256:7d807f770c118e668c6bda1919856d7804f716a2bf95a5ae060345df1cd2b3c7 +size 102703 diff --git a/examples/screenshots/no-imgui-line_collection_cmap_values.png b/examples/screenshots/no-imgui-line_collection_cmap_values.png index df237aa1b..c909c766f 100644 --- a/examples/screenshots/no-imgui-line_collection_cmap_values.png +++ b/examples/screenshots/no-imgui-line_collection_cmap_values.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3f5a7257d121a15a8a35ca6e9c70de9d6fbb4977221c840dd34e25e67136f4ea -size 67209 +oid sha256:2e8612de5c3ee252ce9c8cc8afd5bd6075d5e242e8a93cd025e28ec82526120f +size 64698 diff --git a/examples/screenshots/no-imgui-line_collection_cmap_values_qualitative.png b/examples/screenshots/no-imgui-line_collection_cmap_values_qualitative.png index 0347f7361..61d5a21d0 100644 --- a/examples/screenshots/no-imgui-line_collection_cmap_values_qualitative.png +++ b/examples/screenshots/no-imgui-line_collection_cmap_values_qualitative.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:89a7bc62495e6454ee008e15f1504211777cc01e52f303c18f6068fd38ab3c12 -size 70090 +oid sha256:7847cd4399ce5b43bda9985eb72467ad292744aaeb9e8d210dd6c86c4eb1a090 +size 67959 diff --git a/examples/screenshots/no-imgui-line_collection_colors.png b/examples/screenshots/no-imgui-line_collection_colors.png index dff4f83db..567bb4d06 100644 --- a/examples/screenshots/no-imgui-line_collection_colors.png +++ b/examples/screenshots/no-imgui-line_collection_colors.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:78b14e90e5ae1e185abb51d94ac9d99c1a4318b0ddf79c26a55e6061f22c0ed9 -size 60447 +oid sha256:15216a0900bcaef492e5d9e3380db9f28d7b7e4bd11b26eb87ce956666dcd2b1 +size 58414 diff --git a/examples/screenshots/no-imgui-line_collection_slicing.png b/examples/screenshots/no-imgui-line_collection_slicing.png index 70c343361..c9bc6d931 100644 --- a/examples/screenshots/no-imgui-line_collection_slicing.png +++ b/examples/screenshots/no-imgui-line_collection_slicing.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d6b4090d3ae9e38256c9f04e17bf2499f0a35348552f62e9c8d8dc97c9e760a7 -size 132125 +oid sha256:e8d3d7813580be188766c2d0200bcbff28122758d36d0faa846b0bb4dceac654 +size 130453 diff --git a/examples/screenshots/no-imgui-line_colorslice.png b/examples/screenshots/no-imgui-line_colorslice.png index 3befac6da..fe54de5d6 100644 --- a/examples/screenshots/no-imgui-line_colorslice.png +++ b/examples/screenshots/no-imgui-line_colorslice.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9f161ad7f351b56c988e1b27155e3963be5191dc09cbaa55615026d07df07334 -size 56338 +oid sha256:be429bf910979cf4c9483b8ae1f7aa877fde64fb6ec8a4cf32be143f282c9103 +size 57353 diff --git a/examples/screenshots/no-imgui-line_dataslice.png b/examples/screenshots/no-imgui-line_dataslice.png index 957462d09..649a9df59 100644 --- a/examples/screenshots/no-imgui-line_dataslice.png +++ b/examples/screenshots/no-imgui-line_dataslice.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d2f737e0afd8f57c7d621197d37fcf30199086f6c083ec0d3d8e5497965e6d12 -size 67938 +oid sha256:cf873f1479cec065f0062ce58ce78ddfbd5673654aacf0ecdbd559747ae741cb +size 69381 diff --git a/examples/screenshots/no-imgui-line_stack.png b/examples/screenshots/no-imgui-line_stack.png index 26f4a3af8..3ef24e73a 100644 --- a/examples/screenshots/no-imgui-line_stack.png +++ b/examples/screenshots/no-imgui-line_stack.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4dd69dc4be7a2283ec11a8427a75a2ddfe4be0cdbbdaedef3dcbf5f567c11ea7 -size 130519 +oid sha256:4b9d02719e7051c2a0e848cc828f21be52ac108c6f9be16795d1150a1e215371 +size 123674 diff --git a/examples/screenshots/no-imgui-linear_region_selectors_match_offsets.png b/examples/screenshots/no-imgui-linear_region_selectors_match_offsets.png index 9871d65c1..809908432 100644 --- a/examples/screenshots/no-imgui-linear_region_selectors_match_offsets.png +++ b/examples/screenshots/no-imgui-linear_region_selectors_match_offsets.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:747b0915eeaf5985346e3b6807a550da53b516769d2517d7c2e0f189baefef91 -size 100604 +oid sha256:303d562f1a16f6a704415072d43ca08a51e12a702292b522e0f17f397b1aee60 +size 96668 diff --git a/examples/screenshots/no-imgui-rect_frac_layout.png b/examples/screenshots/no-imgui-rect_frac_layout.png new file mode 100644 index 000000000..4dc3b2aa6 --- /dev/null +++ b/examples/screenshots/no-imgui-rect_frac_layout.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5923e8b9f687f97d488b282b35f16234898ed1038b0737b7b57fb9cbd72ebf34 +size 157321 diff --git a/examples/screenshots/no-imgui-rect_layout.png b/examples/screenshots/no-imgui-rect_layout.png new file mode 100644 index 000000000..16d1ff446 --- /dev/null +++ b/examples/screenshots/no-imgui-rect_layout.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c2ffe0a8d625322cc22d2abdde80a3f179f01552dde974bbbd49f9e371ab39aa +size 138936 diff --git a/examples/screenshots/no-imgui-scatter_cmap_iris.png b/examples/screenshots/no-imgui-scatter_cmap_iris.png index 35812357a..0d1f8dbb0 100644 --- a/examples/screenshots/no-imgui-scatter_cmap_iris.png +++ b/examples/screenshots/no-imgui-scatter_cmap_iris.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:74438dc47ff3fc1391b6952a52c66160fece0545de4ad40c13d3d56b2e093257 -size 59951 +oid sha256:7e197c84911cf7711d09653d6c54d7a756fbe4fe80daa84f0cf1a1d516217423 +size 60341 diff --git a/examples/screenshots/no-imgui-scatter_colorslice_iris.png b/examples/screenshots/no-imgui-scatter_colorslice_iris.png index 61812c8d7..84447c70f 100644 --- a/examples/screenshots/no-imgui-scatter_colorslice_iris.png +++ b/examples/screenshots/no-imgui-scatter_colorslice_iris.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a02a21459deeca379a69b30054bebcc3739553b9d377d25b953315094e714d1a -size 35763 +oid sha256:780b680de7d3a22d2cb73a6829cad1e1066163e084b8daa9e8362f2543ba62eb +size 36881 diff --git a/examples/screenshots/no-imgui-scatter_dataslice_iris.png b/examples/screenshots/no-imgui-scatter_dataslice_iris.png index 9ef39785c..a19d66270 100644 --- a/examples/screenshots/no-imgui-scatter_dataslice_iris.png +++ b/examples/screenshots/no-imgui-scatter_dataslice_iris.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:21ccf85a9242f6d7a724c38797688abd804d9a565e818b81ea0c8931aa05ca4e -size 38337 +oid sha256:6b4f6635f48e047944c923ac46a9bd5b77e736f26421978ff74cd37a9677c622 +size 39457 diff --git a/examples/screenshots/no-imgui-scatter_iris.png b/examples/screenshots/no-imgui-scatter_iris.png index 91dc29397..631672504 100644 --- a/examples/screenshots/no-imgui-scatter_iris.png +++ b/examples/screenshots/no-imgui-scatter_iris.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7ec960574580af159f3502da09f1f34e841267985edb52b89baf034c1d49125e -size 37410 +oid sha256:80cc8c1ed5276b0b8cbd5aeb3151182a73984829f889195b57442a58c3124a43 +size 38488 diff --git a/examples/screenshots/no-imgui-scatter_size.png b/examples/screenshots/no-imgui-scatter_size.png index 6fadfec4d..241e38ad5 100644 --- a/examples/screenshots/no-imgui-scatter_size.png +++ b/examples/screenshots/no-imgui-scatter_size.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:94b4b9d39f3d4ef2c46b6b4dd7f712ca612f31a7fc94ab5fad8015e48c637e91 -size 70290 +oid sha256:71f3db93ea28e773c708093319985fb0fe04fae9a8a78d4f4f764f0417979b72 +size 68596 diff --git a/examples/screenshots/rect_frac_layout.png b/examples/screenshots/rect_frac_layout.png new file mode 100644 index 000000000..7fe6d3d37 --- /dev/null +++ b/examples/screenshots/rect_frac_layout.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5991b755432318310cfc2b4826bd9639cc234883aa06f1895817f710714cb58f +size 156297 diff --git a/examples/screenshots/rect_layout.png b/examples/screenshots/rect_layout.png new file mode 100644 index 000000000..dec391ac2 --- /dev/null +++ b/examples/screenshots/rect_layout.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0cf23f845932023789e0823a105910e9f701d0f03c04e3c18488f0da62420921 +size 123409 diff --git a/examples/screenshots/scatter_cmap_iris.png b/examples/screenshots/scatter_cmap_iris.png index a887d1f99..c069d6b11 100644 --- a/examples/screenshots/scatter_cmap_iris.png +++ b/examples/screenshots/scatter_cmap_iris.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6d6bfba80eb737099040eebce9b70e1b261720f26cc895ec4b81ca21af60471c -size 60550 +oid sha256:fad40cf8004e31f7d30f4bb552ee1c7f79a499d3bad310c0eac83396f0aabd62 +size 61193 diff --git a/examples/screenshots/scatter_colorslice_iris.png b/examples/screenshots/scatter_colorslice_iris.png index e260df642..58c2b61fe 100644 --- a/examples/screenshots/scatter_colorslice_iris.png +++ b/examples/screenshots/scatter_colorslice_iris.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:febd4aa7240eea70b2759337cf98be31cacc1b147859bf628e929ead0153ef9c -size 36791 +oid sha256:427587ef9a73bf9c3ea6e739b61d5af7380a5488c454a9d3653019b40d569292 +size 37589 diff --git a/examples/screenshots/scatter_dataslice_iris.png b/examples/screenshots/scatter_dataslice_iris.png index e5f05bb74..ab61f0405 100644 --- a/examples/screenshots/scatter_dataslice_iris.png +++ b/examples/screenshots/scatter_dataslice_iris.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6cfbc717281c15c6d1d8fe2989770bc9c46f42052c897c2270294ad1b4b40d66 -size 39296 +oid sha256:e3dd9ad854f41386d353ca0dae689a263eff942817727e328690427e2e62e2f3 +size 40112 diff --git a/examples/screenshots/scatter_iris.png b/examples/screenshots/scatter_iris.png index 9c452d448..01bd5cacd 100644 --- a/examples/screenshots/scatter_iris.png +++ b/examples/screenshots/scatter_iris.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:98eab41312eb42cbffdf8add0651b55e63b5c2fb5f4523e32dc51ed28a1be369 -size 38452 +oid sha256:c7978b93f7eac8176c54ed0e39178424d9cb6474c73e9013d5164d3e88d54c95 +size 39147 diff --git a/examples/screenshots/scatter_size.png b/examples/screenshots/scatter_size.png index f2f036ea4..2f6c045f3 100644 --- a/examples/screenshots/scatter_size.png +++ b/examples/screenshots/scatter_size.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e3522468f99c030cb27c225f009ecb4c7aafbd97cfc743cf1d07fb8d7ff8e0d4 -size 71336 +oid sha256:eb05b8378d94e16094738850dca6328caf7477c641bf474b9deae426344bc7a4 +size 70898 diff --git a/examples/selection_tools/unit_circle.py b/examples/selection_tools/unit_circle.py index 76f6a207c..2850b1bc1 100644 --- a/examples/selection_tools/unit_circle.py +++ b/examples/selection_tools/unit_circle.py @@ -28,12 +28,39 @@ def make_circle(center, radius: float, n_points: int) -> np.ndarray: return np.column_stack([xs, ys]) + center +# We will have 3 subplots in a layout like this: +""" +|========|========| +| | | +| | sine | +| | | +| circle |========| +| | | +| | cosine | +| | | +|========|========| +""" + +# we can define this layout using "extents", i.e. min and max ranges on the canvas +# (x_min, x_max, y_min, y_max) +# extents can be defined as fractions as shown here +extents = [ + (0, 0.5, 0, 1), # circle subplot + (0.5, 1, 0, 0.5), # sine subplot + (0.5, 1, 0.5, 1), # cosine subplot +] + # create a figure with 3 subplots -figure = fpl.Figure((3, 1), names=["unit circle", "sin(x)", "cos(x)"], size=(700, 1024)) +figure = fpl.Figure( + extents=extents, + names=["unit circle", "sin(x)", "cos(x)"], + size=(700, 560) +) # set the axes to intersect at (0, 0, 0) to better illustrate the unit circle for subplot in figure: subplot.axes.intersection = (0, 0, 0) + subplot.toolbar = False # reduce clutter figure["sin(x)"].camera.maintain_aspect = False figure["cos(x)"].camera.maintain_aspect = False @@ -73,6 +100,7 @@ def make_circle(center, radius: float, n_points: int) -> np.ndarray: sine_selector = sine_graphic.add_linear_selector() cosine_selector = cosine_graphic.add_linear_selector() + def set_circle_cmap(ev): # sets the cmap transforms diff --git a/examples/tests/test_examples.py b/examples/tests/test_examples.py index d5f3e8ab9..7fbd32e2f 100644 --- a/examples/tests/test_examples.py +++ b/examples/tests/test_examples.py @@ -103,7 +103,7 @@ def test_example_screenshots(module, force_offscreen): # hacky but it works for now example.figure.imgui_renderer.render() - example.figure._set_viewport_rects() + example.figure._fpl_reset_layout() # render each subplot for subplot in example.figure: subplot.viewport.render(subplot.scene, subplot.camera) diff --git a/examples/tests/testutils.py b/examples/tests/testutils.py index f72a87123..d6fce52fe 100644 --- a/examples/tests/testutils.py +++ b/examples/tests/testutils.py @@ -24,6 +24,7 @@ "line/*.py", "line_collection/*.py", "gridplot/*.py", + "window_layouts/*.py", "misc/*.py", "selection_tools/*.py", "guis/*.py", diff --git a/examples/window_layouts/README.rst b/examples/window_layouts/README.rst new file mode 100644 index 000000000..3c7df2366 --- /dev/null +++ b/examples/window_layouts/README.rst @@ -0,0 +1,2 @@ +WindowLayout Examples +===================== diff --git a/examples/window_layouts/extent_frac_layout.py b/examples/window_layouts/extent_frac_layout.py new file mode 100644 index 000000000..0c5293e09 --- /dev/null +++ b/examples/window_layouts/extent_frac_layout.py @@ -0,0 +1,74 @@ +""" +Fractional Extent Layout +======================== + +Create subplots using extents given as fractions of the canvas. +This example plots two images and their histograms in separate subplots + +""" + +# test_example = true +# sphinx_gallery_pygfx_docs = 'screenshot' + +import numpy as np +import imageio.v3 as iio +import fastplotlib as fpl + +# load images +img1 = iio.imread("imageio:astronaut.png") +img2 = iio.imread("imageio:wikkie.png") + +# calculate histograms +hist_1, edges_1 = np.histogram(img1) +centers_1 = edges_1[:-1] + np.diff(edges_1) / 2 + +hist_2, edges_2 = np.histogram(img2) +centers_2 = edges_2[:-1] + np.diff(edges_2) / 2 + +# figure size in pixels +size = (700, 560) + +# extent is (xmin, xmax, ymin, ymax) +# here it is defined as fractions of the canvas +extents = [ + (0, 0.3, 0, 0.5), # for image1 + (0, 0.3, 0.5, 1), # for image2 + (0.3, 1, 0, 0.5), # for image1 histogram + (0.3, 1, 0.5, 1), # for image2 histogram +] + +# create a figure using the rects and size +# also give each subplot a name +figure = fpl.Figure( + extents=extents, + names=["astronaut image", "wikkie image", "astronaut histogram", "wikkie histogram"], + size=size +) + +# add image to the corresponding subplots +figure["astronaut image"].add_image(img1) +figure["wikkie image"].add_image(img2) + +# add histogram to the corresponding subplots +figure["astronaut histogram"].add_line(np.column_stack([centers_1, hist_1])) +figure["wikkie histogram"].add_line(np.column_stack([centers_2, hist_2])) + + +for subplot in figure: + if "image" in subplot.name: + # remove axes from image subplots to reduce clutter + subplot.axes.visible = False + continue + + # don't maintain aspect ratio for the histogram subplots + subplot.camera.maintain_aspect = False + + +figure.show() + + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.loop.run() diff --git a/examples/window_layouts/extent_layout.py b/examples/window_layouts/extent_layout.py new file mode 100644 index 000000000..e6facaaa2 --- /dev/null +++ b/examples/window_layouts/extent_layout.py @@ -0,0 +1,74 @@ +""" +Extent Layout +============= + +Create subplots using given extents in absolute pixels. +This example plots two images and their histograms in separate subplots + +""" + +# test_example = true +# sphinx_gallery_pygfx_docs = 'screenshot' + +import numpy as np +import imageio.v3 as iio +import fastplotlib as fpl + +# load images +img1 = iio.imread("imageio:astronaut.png") +img2 = iio.imread("imageio:wikkie.png") + +# calculate histograms +hist_1, edges_1 = np.histogram(img1) +centers_1 = edges_1[:-1] + np.diff(edges_1) / 2 + +hist_2, edges_2 = np.histogram(img2) +centers_2 = edges_2[:-1] + np.diff(edges_2) / 2 + +# figure size in pixels +size = (640, 480) + +# extent is (xmin, xmax, ymin, ymax) +# here it is defined in absolute pixels +extents = [ + (0, 200, 0, 240), # for image1 + (0, 200, 240, 480), # for image2 + (200, 640, 0, 240), # for image1 histogram + (200, 640, 240, 480), # for image2 histogram +] + +# create a figure using the rects and size +# also give each subplot a name +figure = fpl.Figure( + extents=extents, + names=["astronaut image", "wikkie image", "astronaut histogram", "wikkie histogram"], + size=size +) + +# add image to the corresponding subplots +figure["astronaut image"].add_image(img1) +figure["wikkie image"].add_image(img2) + +# add histogram to the corresponding subplots +figure["astronaut histogram"].add_line(np.column_stack([centers_1, hist_1])) +figure["wikkie histogram"].add_line(np.column_stack([centers_2, hist_2])) + + +for subplot in figure: + if "image" in subplot.name: + # remove axes from image subplots to reduce clutter + subplot.axes.visible = False + continue + + # don't maintain aspect ratio for the histogram subplots + subplot.camera.maintain_aspect = False + + +figure.show() + + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.loop.run() diff --git a/examples/window_layouts/rect_frac_layout.py b/examples/window_layouts/rect_frac_layout.py new file mode 100644 index 000000000..072fa1107 --- /dev/null +++ b/examples/window_layouts/rect_frac_layout.py @@ -0,0 +1,74 @@ +""" +Rect Fractional Layout +====================== + +Create subplots using rects given as fractions of the canvas. +This example plots two images and their histograms in separate subplots + +""" + +# test_example = true +# sphinx_gallery_pygfx_docs = 'screenshot' + +import numpy as np +import imageio.v3 as iio +import fastplotlib as fpl + +# load images +img1 = iio.imread("imageio:astronaut.png") +img2 = iio.imread("imageio:wikkie.png") + +# calculate histograms +hist_1, edges_1 = np.histogram(img1) +centers_1 = edges_1[:-1] + np.diff(edges_1) / 2 + +hist_2, edges_2 = np.histogram(img2) +centers_2 = edges_2[:-1] + np.diff(edges_2) / 2 + +# figure size in pixels +size = (700, 560) + +# rect is (x, y, width, height) +# here it is defined as fractions of the canvas +rects = [ + (0, 0, 0.3, 0.5), # for image1 + (0, 0.5, 0.3, 0.5), # for image2 + (0.3, 0, 0.7, 0.5), # for image1 histogram + (0.3, 0.5, 0.7, 0.5), # for image2 histogram +] + +# create a figure using the rects and size +# also give each subplot a name +figure = fpl.Figure( + rects=rects, + names=["astronaut image", "wikkie image", "astronaut histogram", "wikkie histogram"], + size=size +) + +# add image to the corresponding subplots +figure["astronaut image"].add_image(img1) +figure["wikkie image"].add_image(img2) + +# add histogram to the corresponding subplots +figure["astronaut histogram"].add_line(np.column_stack([centers_1, hist_1])) +figure["wikkie histogram"].add_line(np.column_stack([centers_2, hist_2])) + + +for subplot in figure: + if "image" in subplot.name: + # remove axes from image subplots to reduce clutter + subplot.axes.visible = False + continue + + # don't maintain aspect ratio for the histogram subplots + subplot.camera.maintain_aspect = False + + +figure.show() + + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.loop.run() diff --git a/examples/window_layouts/rect_layout.py b/examples/window_layouts/rect_layout.py new file mode 100644 index 000000000..962b8a4f1 --- /dev/null +++ b/examples/window_layouts/rect_layout.py @@ -0,0 +1,74 @@ +""" +Rect Layout +=========== + +Create subplots using given rects in absolute pixels. +This example plots two images and their histograms in separate subplots + +""" + +# test_example = true +# sphinx_gallery_pygfx_docs = 'screenshot' + +import numpy as np +import imageio.v3 as iio +import fastplotlib as fpl + +# load images +img1 = iio.imread("imageio:astronaut.png") +img2 = iio.imread("imageio:wikkie.png") + +# calculate histograms +hist_1, edges_1 = np.histogram(img1) +centers_1 = edges_1[:-1] + np.diff(edges_1) / 2 + +hist_2, edges_2 = np.histogram(img2) +centers_2 = edges_2[:-1] + np.diff(edges_2) / 2 + +# figure size in pixels +size = (640, 480) + +# a rect is (x, y, width, height) +# here it is defined in absolute pixels +rects = [ + (0, 0, 200, 240), # for image1 + (0, 240, 200, 240), # for image2 + (200, 0, 440, 240), # for image1 histogram + (200, 240, 440, 240), # for image2 histogram +] + +# create a figure using the rects and size +# also give each subplot a name +figure = fpl.Figure( + rects=rects, + names=["astronaut image", "wikkie image", "astronaut histogram", "wikkie histogram"], + size=size +) + +# add image to the corresponding subplots +figure["astronaut image"].add_image(img1) +figure["wikkie image"].add_image(img2) + +# add histogram to the corresponding subplots +figure["astronaut histogram"].add_line(np.column_stack([centers_1, hist_1])) +figure["wikkie histogram"].add_line(np.column_stack([centers_2, hist_2])) + + +for subplot in figure: + if "image" in subplot.name: + # remove axes from image subplots to reduce clutter + subplot.axes.visible = False + continue + + # don't maintain aspect ratio for the histogram subplots + subplot.camera.maintain_aspect = False + + +figure.show() + + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.loop.run() diff --git a/fastplotlib/graphics/_base.py b/fastplotlib/graphics/_base.py index a25bc7176..61ad291ee 100644 --- a/fastplotlib/graphics/_base.py +++ b/fastplotlib/graphics/_base.py @@ -365,7 +365,7 @@ def _fpl_add_plot_area_hook(self, plot_area): self._plot_area = plot_area def __repr__(self): - rval = f"{self.__class__.__name__} @ {hex(id(self))}" + rval = f"{self.__class__.__name__}" if self.name is not None: return f"'{self.name}': {rval}" else: diff --git a/fastplotlib/graphics/_features/_text.py b/fastplotlib/graphics/_features/_text.py index 90af7c719..a95fe256c 100644 --- a/fastplotlib/graphics/_features/_text.py +++ b/fastplotlib/graphics/_features/_text.py @@ -16,7 +16,7 @@ def value(self) -> str: @block_reentrance def set_value(self, graphic, value: str): - graphic.world_object.geometry.set_text(value) + graphic.world_object.set_text(value) self._value = value event = FeatureEvent(type="text", info={"value": value}) @@ -34,8 +34,8 @@ def value(self) -> float | int: @block_reentrance def set_value(self, graphic, value: float | int): - graphic.world_object.geometry.font_size = value - self._value = graphic.world_object.geometry.font_size + graphic.world_object.font_size = value + self._value = graphic.world_object.font_size event = FeatureEvent(type="font_size", info={"value": value}) self._call_event_handlers(event) diff --git a/fastplotlib/graphics/text.py b/fastplotlib/graphics/text.py index fcee6129b..e3794743a 100644 --- a/fastplotlib/graphics/text.py +++ b/fastplotlib/graphics/text.py @@ -79,13 +79,11 @@ def __init__( self._outline_thickness = TextOutlineThickness(outline_thickness) world_object = pygfx.Text( - pygfx.TextGeometry( - text=self.text, - font_size=self.font_size, - screen_space=screen_space, - anchor=anchor, - ), - pygfx.TextMaterial( + text=self.text, + font_size=self.font_size, + screen_space=screen_space, + anchor=anchor, + material=pygfx.TextMaterial( color=self.face_color, outline_color=self.outline_color, outline_thickness=self.outline_thickness, @@ -97,6 +95,11 @@ def __init__( self.offset = offset + @property + def world_object(self) -> pygfx.Text: + """Text world object""" + return super(TextGraphic, self).world_object + @property def text(self) -> str: """the text displayed""" diff --git a/fastplotlib/layouts/__init__.py b/fastplotlib/layouts/__init__.py index 4a4f45174..8fb1d54d8 100644 --- a/fastplotlib/layouts/__init__.py +++ b/fastplotlib/layouts/__init__.py @@ -1,11 +1,5 @@ from ._figure import Figure - -try: - import imgui_bundle -except ImportError: - IMGUI = False -else: - IMGUI = True +from ._utils import IMGUI if IMGUI: from ._imgui_figure import ImguiFigure diff --git a/fastplotlib/layouts/_engine.py b/fastplotlib/layouts/_engine.py new file mode 100644 index 000000000..877a7fbab --- /dev/null +++ b/fastplotlib/layouts/_engine.py @@ -0,0 +1,390 @@ +from functools import partial + +import numpy as np +import pygfx + +from ._subplot import Subplot +from ._rect import RectManager + + +class UnderlayCamera(pygfx.Camera): + """ + Same as pygfx.ScreenCoordsCamera but y-axis is inverted. + + So top left corner is (0, 0). This is easier to manage because we + often resize using the bottom right corner. + + """ + + def _update_projection_matrix(self): + width, height = self._view_size + sx, sy, sz = 2 / width, 2 / height, 1 + dx, dy, dz = -1, 1, 0 # pygfx is -1, -1, 0 + m = sx, 0, 0, dx, 0, sy, 0, dy, 0, 0, sz, dz, 0, 0, 0, 1 + proj_matrix = np.array(m, dtype=float).reshape(4, 4) + proj_matrix.flags.writeable = False + return proj_matrix + + +class BaseLayout: + def __init__( + self, + renderer: pygfx.WgpuRenderer, + subplots: np.ndarray[Subplot], + canvas_rect: tuple[float, float], + moveable: bool, + resizeable: bool, + ): + """ + Base layout engine, subclass to create a usable layout engine. + """ + self._renderer = renderer + self._subplots: np.ndarray[Subplot] = subplots.ravel() + self._canvas_rect = canvas_rect + + self._last_pointer_pos: np.ndarray[np.float64, np.float64] = np.array( + [np.nan, np.nan] + ) + + # the current user action, move or resize + self._active_action: str | None = None + # subplot that is currently in action, i.e. currently being moved or resized + self._active_subplot: Subplot | None = None + # subplot that is in focus, i.e. being hovered by the pointer + self._subplot_focus: Subplot | None = None + + for subplot in self._subplots: + # highlight plane when pointer enters it + subplot.frame.plane.add_event_handler( + partial(self._highlight_plane, subplot), "pointer_enter" + ) + + if resizeable: + # highlight/unhighlight resize handler when pointer enters/leaves + subplot.frame.resize_handle.add_event_handler( + partial(self._highlight_resize_handler, subplot), "pointer_enter" + ) + subplot.frame.resize_handle.add_event_handler( + partial(self._unhighlight_resize_handler, subplot), "pointer_leave" + ) + + def _inside_render_rect(self, subplot: Subplot, pos: tuple[int, int]) -> bool: + """whether the pos is within the render area, used for filtering out pointer events""" + rect = subplot.frame.get_render_rect() + + x0, y0 = rect[:2] + + x1 = x0 + rect[2] + y1 = y0 + rect[3] + + if (x0 < pos[0] < x1) and (y0 < pos[1] < y1): + return True + + return False + + def canvas_resized(self, canvas_rect: tuple): + """ + called by figure when canvas is resized + + Parameters + ---------- + canvas_rect: (x, y, w, h) + the rect that pygfx can render to, excludes any areas used by imgui. + + """ + + self._canvas_rect = canvas_rect + for subplot in self._subplots: + subplot.frame.canvas_resized(canvas_rect) + + def _highlight_resize_handler(self, subplot: Subplot, ev): + if self._active_action == "resize": + return + + ev.target.material.color = subplot.frame.resize_handle_color.highlight + + def _unhighlight_resize_handler(self, subplot: Subplot, ev): + if self._active_action == "resize": + return + + ev.target.material.color = subplot.frame.resize_handle_color.idle + + def _highlight_plane(self, subplot: Subplot, ev): + if self._active_action is not None: + return + + # reset color of previous focus + if self._subplot_focus is not None: + self._subplot_focus.frame.plane.material.color = ( + subplot.frame.plane_color.idle + ) + + self._subplot_focus = subplot + ev.target.material.color = subplot.frame.plane_color.highlight + + def __len__(self): + return len(self._subplots) + + +class WindowLayout(BaseLayout): + def __init__( + self, + renderer, + subplots: np.ndarray[Subplot], + canvas_rect: tuple, + moveable=True, + resizeable=True, + ): + """ + Flexible layout engine that allows freely moving and resizing subplots. + Subplots are not allowed to overlap. + + We use a screenspace camera to perform an underlay render pass to draw the + subplot frames, there is no depth rendering so we do not allow overlaps. + + """ + + super().__init__(renderer, subplots, canvas_rect, moveable, resizeable) + + self._last_pointer_pos: np.ndarray[np.float64, np.float64] = np.array( + [np.nan, np.nan] + ) + + for subplot in self._subplots: + if moveable: + # start a move action + subplot.frame.plane.add_event_handler( + partial(self._action_start, subplot, "move"), "pointer_down" + ) + # start a resize action + subplot.frame.resize_handle.add_event_handler( + partial(self._action_start, subplot, "resize"), "pointer_down" + ) + + if moveable or resizeable: + # when pointer moves, do an iteration of move or resize action + self._renderer.add_event_handler(self._action_iter, "pointer_move") + + # end the action when pointer button goes up + self._renderer.add_event_handler(self._action_end, "pointer_up") + + def _new_extent_from_delta(self, delta: tuple[int, int]) -> np.ndarray: + delta_x, delta_y = delta + if self._active_action == "resize": + # subtract only from x1, y1 + new_extent = self._active_subplot.frame.extent - np.asarray( + [0, delta_x, 0, delta_y] + ) + else: + # moving + new_extent = self._active_subplot.frame.extent - np.asarray( + [delta_x, delta_x, delta_y, delta_y] + ) + + x0, x1, y0, y1 = new_extent + w = x1 - x0 + h = y1 - y0 + + # make sure width and height are valid + # min width, height is 50px + if w <= 50: # width > 0 + new_extent[:2] = self._active_subplot.frame.extent[:2] + + if h <= 50: # height > 0 + new_extent[2:] = self._active_subplot.frame.extent[2:] + + # ignore movement if this would cause an overlap + for subplot in self._subplots: + if subplot is self._active_subplot: + continue + + if subplot.frame.rect_manager.overlaps(new_extent): + # we have an overlap, need to ignore one or more deltas + # ignore x + if not subplot.frame.rect_manager.is_left_of( + x0 + ) or not subplot.frame.rect_manager.is_right_of(x1): + new_extent[:2] = self._active_subplot.frame.extent[:2] + + # ignore y + if not subplot.frame.rect_manager.is_above( + y0 + ) or not subplot.frame.rect_manager.is_below(y1): + new_extent[2:] = self._active_subplot.frame.extent[2:] + + # make sure all vals are non-negative + if (new_extent[:2] < 0).any(): + # ignore delta_x + new_extent[:2] = self._active_subplot.frame.extent[:2] + + if (new_extent[2:] < 0).any(): + # ignore delta_y + new_extent[2:] = self._active_subplot.frame.extent[2:] + + # canvas extent + cx0, cy0, cw, ch = self._canvas_rect + + # check if new x-range is beyond canvas x-max + if (new_extent[:2] > cx0 + cw).any(): + new_extent[:2] = self._active_subplot.frame.extent[:2] + + # check if new y-range is beyond canvas y-max + if (new_extent[2:] > cy0 + ch).any(): + new_extent[2:] = self._active_subplot.frame.extent[2:] + + return new_extent + + def _action_start(self, subplot: Subplot, action: str, ev): + if self._inside_render_rect(subplot, pos=(ev.x, ev.y)): + return + + if ev.button == 1: # left mouse button + self._active_action = action + if action == "resize": + subplot.frame.resize_handle.material.color = ( + subplot.frame.resize_handle_color.action + ) + elif action == "move": + subplot.frame.plane.material.color = subplot.frame.plane_color.action + else: + raise ValueError + + self._active_subplot = subplot + self._last_pointer_pos[:] = ev.x, ev.y + + def _action_iter(self, ev): + if self._active_action is None: + return + + delta_x, delta_y = self._last_pointer_pos - (ev.x, ev.y) + new_extent = self._new_extent_from_delta((delta_x, delta_y)) + self._active_subplot.frame.extent = new_extent + self._last_pointer_pos[:] = ev.x, ev.y + + def _action_end(self, ev): + self._active_action = None + if self._active_subplot is not None: + self._active_subplot.frame.resize_handle.material.color = ( + self._active_subplot.frame.resize_handle_color.idle + ) + self._active_subplot.frame.plane.material.color = ( + self._active_subplot.frame.plane_color.idle + ) + self._active_subplot = None + + self._last_pointer_pos[:] = np.nan + + def set_rect(self, subplot: Subplot, rect: tuple | list | np.ndarray): + """ + Set the rect of a Subplot + + Parameters + ---------- + subplot: Subplot + the subplot to set the rect of + + rect: (x, y, w, h) + as absolute pixels or fractional. + If width & height <= 1 the rect is assumed to be fractional. + Conversely, if width & height > 1 the rect is assumed to be in absolute pixels. + width & height must be > 0. Negative values are not allowed. + + """ + + new_rect = RectManager(*rect, self._canvas_rect) + extent = new_rect.extent + # check for overlaps + for s in self._subplots: + if s is subplot: + continue + + if s.frame.rect_manager.overlaps(extent): + raise ValueError(f"Given rect: {rect} overlaps with another subplot.") + + def set_extent(self, subplot: Subplot, extent: tuple | list | np.ndarray): + """ + Set the extent of a Subplot + + Parameters + ---------- + subplot: Subplot + the subplot to set the extent of + + extent: (xmin, xmax, ymin, ymax) + as absolute pixels or fractional. + If xmax & ymax <= 1 the extent is assumed to be fractional. + Conversely, if xmax & ymax > 1 the extent is assumed to be in absolute pixels. + Negative values are not allowed. xmax - xmin & ymax - ymin must be > 0. + + """ + + new_rect = RectManager.from_extent(extent, self._canvas_rect) + extent = new_rect.extent + # check for overlaps + for s in self._subplots: + if s is subplot: + continue + + if s.frame.rect_manager.overlaps(extent): + raise ValueError( + f"Given extent: {extent} overlaps with another subplot." + ) + + +class GridLayout(WindowLayout): + def __init__( + self, + renderer, + subplots: np.ndarray[Subplot], + canvas_rect: tuple[float, float, float, float], + shape: tuple[int, int], + ): + """ + Grid layout engine that auto-sets Frame and Subplot rects such that they maintain + a fixed grid layout. Does not allow freely moving or resizing subplots. + + """ + + super().__init__( + renderer, subplots, canvas_rect, moveable=False, resizeable=False + ) + + # {Subplot: (row_ix, col_ix)}, dict mapping subplots to their row and col index in the grid layout + self._subplot_grid_position: dict[Subplot, tuple[int, int]] + self._shape = shape + + @property + def shape(self) -> tuple[int, int]: + return self._shape + + def set_rect(self, subplot, rect: np.ndarray | list | tuple): + raise NotImplementedError( + "set_rect() not implemented for GridLayout which is an auto layout manager" + ) + + def set_extent(self, subplot, extent: np.ndarray | list | tuple): + raise NotImplementedError( + "set_extent() not implemented for GridLayout which is an auto layout manager" + ) + + def add_row(self): + raise NotImplementedError("Not yet implemented") + + def add_column(self): + raise NotImplementedError("Not yet implemented") + + def remove_row(self): + raise NotImplementedError("Not yet implemented") + + def remove_column(self): + raise NotImplementedError("Not yet implemented") + + def add_subplot(self): + raise NotImplementedError( + "Not implemented for GridLayout which is an auto layout manager" + ) + + def remove_subplot(self, subplot): + raise NotImplementedError( + "Not implemented for GridLayout which is an auto layout manager" + ) diff --git a/fastplotlib/layouts/_figure.py b/fastplotlib/layouts/_figure.py index e09005a4c..e1822eb64 100644 --- a/fastplotlib/layouts/_figure.py +++ b/fastplotlib/layouts/_figure.py @@ -11,20 +11,24 @@ from rendercanvas import BaseRenderCanvas -from ._utils import make_canvas_and_renderer, create_controller, create_camera +from ._utils import ( + make_canvas_and_renderer, + create_controller, + create_camera, + get_extents_from_grid, +) from ._utils import controller_types as valid_controller_types from ._subplot import Subplot +from ._engine import GridLayout, WindowLayout, UnderlayCamera from .. import ImageGraphic -# number of pixels taken by the imgui toolbar when present -IMGUI_TOOLBAR_HEIGHT = 39 - - class Figure: def __init__( self, - shape: list[tuple[int, int, int, int]] | tuple[int, int] = (1, 1), + shape: tuple[int, int] = (1, 1), + rects: list[tuple | np.ndarray] = None, + extents: list[tuple | np.ndarray] = None, cameras: ( Literal["2d", "3d"] | Iterable[Iterable[Literal["2d", "3d"]]] @@ -48,14 +52,31 @@ def __init__( names: list | np.ndarray = None, ): """ - A grid of subplots. + Create a Figure containing Subplots. Parameters ---------- - shape: list[tuple[int, int, int, int]] | tuple[int, int], default (1, 1) - grid of shape [n_rows, n_cols] or list of bounding boxes: [x, y, width, height] (NOT YET IMPLEMENTED) - - cameras: "2d", "3", list of "2d" | "3d", Iterable of camera instances, or Iterable of "2d" | "3d", optional + shape: tuple[int, int], default (1, 1) + shape [n_rows, n_cols] that defines a grid of subplots + + rects: list of tuples or arrays + list of rects (x, y, width, height) that define the subplots. + rects can be defined in absolute pixels or as a fraction of the canvas. + If width & height <= 1 the rect is assumed to be fractional. + Conversely, if width & height > 1 the rect is assumed to be in absolute pixels. + width & height must be > 0. Negative values are not allowed. + + extents: list of tuples or arrays + list of extents (xmin, xmax, ymin, ymax) that define the subplots. + extents can be defined in absolute pixels or as a fraction of the canvas. + If xmax & ymax <= 1 the extent is assumed to be fractional. + Conversely, if xmax & ymax > 1 the extent is assumed to be in absolute pixels. + Negative values are not allowed. xmax - xmin & ymax - ymin must be > 0. + + If both ``rects`` and ``extents`` are provided, then ``rects`` takes precedence over ``extents``, i.e. + ``extents`` is ignored when ``rects`` are also provided. + + cameras: "2d", "3d", list of "2d" | "3d", Iterable of camera instances, or Iterable of "2d" | "3d", optional | if str, one of ``"2d"`` or ``"3d"`` indicating 2D or 3D cameras for all subplots | Iterable/list/array of ``2d`` and/or ``3d`` that specifies the camera type for each subplot | Iterable/list/array of pygfx.PerspectiveCamera instances @@ -91,84 +112,85 @@ def __init__( pygfx renderer instance size: (int, int), optional - starting size of canvas, default (500, 300) + starting size of canvas in absolute pixels, default (500, 300) names: list or array of str, optional subplot names + """ - if isinstance(shape, list): - raise NotImplementedError("bounding boxes for shape not yet implemented") - if not all(isinstance(v, (tuple, list)) for v in shape): + if rects is not None: + if not all(isinstance(v, (np.ndarray, tuple, list)) for v in rects): raise TypeError( - "shape argument must be a list of bounding boxes or a tuple[n_rows, n_cols]" + f"rects must a list of arrays, tuples, or lists of rects (x, y, w, h), you have passed: {rects}" ) - for item in shape: - if not all(isinstance(v, (int, np.integer)) for v in item): - raise TypeError( - "shape argument must be a list of bounding boxes or a tuple[n_rows, n_cols]" - ) - # constant that sets the Figure to be in "rect" mode - self._mode: str = "rect" + n_subplots = len(rects) + layout_mode = "rect" + extents = [None] * n_subplots - elif isinstance(shape, tuple): - if not all(isinstance(v, (int, np.integer)) for v in shape): + elif extents is not None: + if not all(isinstance(v, (np.ndarray, tuple, list)) for v in extents): raise TypeError( - "shape argument must be a list of bounding boxes or a tuple[n_rows, n_cols]" + f"extents must a list of arrays, tuples, or lists of extents (xmin, xmax, ymin, ymax), " + f"you have passed: {extents}" ) - # constant that sets the Figure to be in "grid" mode - self._mode: str = "grid" - - # shape is [n_subplots, row_col_index] - self._subplot_grid_positions: dict[Subplot, tuple[int, int]] = dict() + n_subplots = len(extents) + layout_mode = "extent" + rects = [None] * n_subplots else: - raise TypeError( - "shape argument must be a list of bounding boxes or a tuple[n_rows, n_cols]" - ) - - self._shape = shape + if not all(isinstance(v, (int, np.integer)) for v in shape): + raise TypeError("shape argument must be a tuple[n_rows, n_cols]") + n_subplots = shape[0] * shape[1] + layout_mode = "grid" - # default spacing of 2 pixels between subplots - self._spacing = 2 + # create fractional extents from the grid + extents = get_extents_from_grid(shape) + # empty rects + rects = [None] * n_subplots if names is not None: subplot_names = np.asarray(names).flatten() - if subplot_names.size != len(self): + if subplot_names.size != n_subplots: raise ValueError( - "must provide same number of subplot `names` as specified by Figure `shape`" + f"must provide same number of subplot `names` as specified by shape, extents, or rects: {n_subplots}" ) else: - subplot_names = None + if layout_mode == "grid": + subplot_names = np.asarray( + list(map(str, product(range(shape[0]), range(shape[1])))) + ) + else: + subplot_names = None canvas, renderer = make_canvas_and_renderer( canvas, renderer, canvas_kwargs={"size": size} ) - canvas.add_event_handler(self._set_viewport_rects, "resize") + canvas.add_event_handler(self._fpl_reset_layout, "resize") if isinstance(cameras, str): # create the array representing the views for each subplot in the grid - cameras = np.array([cameras] * len(self)) + cameras = np.array([cameras] * n_subplots) # list/tuple -> array if necessary cameras = np.asarray(cameras).flatten() - if cameras.size != len(self): + if cameras.size != n_subplots: raise ValueError( - f"Number of cameras: {cameras.size} does not match the number of subplots: {len(self)}" + f"Number of cameras: {cameras.size} does not match the number of subplots: {n_subplots}" ) # create the cameras - subplot_cameras = np.empty(len(self), dtype=object) - for index in range(len(self)): + subplot_cameras = np.empty(n_subplots, dtype=object) + for index in range(n_subplots): subplot_cameras[index] = create_camera(camera_type=cameras[index]) # if controller instances have been specified for each subplot if controllers is not None: # one controller for all subplots if isinstance(controllers, pygfx.Controller): - controllers = [controllers] * len(self) + controllers = [controllers] * n_subplots # individual controller instance specified for each subplot else: @@ -188,25 +210,25 @@ def __init__( subplot_controllers: np.ndarray[pygfx.Controller] = np.asarray( controllers ).flatten() - if not subplot_controllers.size == len(self): + if not subplot_controllers.size == n_subplots: raise ValueError( f"number of controllers passed must be the same as the number of subplots specified " - f"by shape: {len(self)}. You have passed: {subplot_controllers.size} controllers" + f"by shape, extents, or rects: {n_subplots}. You have passed: {subplot_controllers.size} controllers" ) from None - for index in range(len(self)): + for index in range(n_subplots): subplot_controllers[index].add_camera(subplot_cameras[index]) # parse controller_ids and controller_types to make desired controller for each subplot else: if controller_ids is None: # individual controller for each subplot - controller_ids = np.arange(len(self)) + controller_ids = np.arange(n_subplots) elif isinstance(controller_ids, str): if controller_ids == "sync": # this will end up creating one controller to control the camera of every subplot - controller_ids = np.zeros(len(self), dtype=int) + controller_ids = np.zeros(n_subplots, dtype=int) else: raise ValueError( f"`controller_ids` must be one of 'sync', an array/list of subplot names, or an array/list of " @@ -236,7 +258,7 @@ def __init__( ) # initialize controller_ids array - ids_init = np.arange(len(self)) + ids_init = np.arange(n_subplots) # set id based on subplot position for each synced sublist for row_ix, sublist in enumerate(controller_ids): @@ -261,18 +283,18 @@ def __init__( f"you have passed: {controller_ids}" ) - if controller_ids.size != len(self): + if controller_ids.size != n_subplots: raise ValueError( - "Number of controller_ids does not match the number of subplots" + f"Number of controller_ids does not match the number of subplots: {n_subplots}" ) if controller_types is None: # `create_controller()` will auto-determine controller for each subplot based on defaults - controller_types = np.array(["default"] * len(self)) + controller_types = np.array(["default"] * n_subplots) # valid controller types if isinstance(controller_types, str): - controller_types = np.array([controller_types] * len(self)) + controller_types = np.array([controller_types] * n_subplots) controller_types: np.ndarray[pygfx.Controller] = np.asarray( controller_types @@ -292,7 +314,7 @@ def __init__( ) # make the real controllers for each subplot - subplot_controllers = np.empty(shape=len(self), dtype=object) + subplot_controllers = np.empty(shape=n_subplots, dtype=object) for cid in np.unique(controller_ids): cont_type = controller_types[controller_ids == cid] if np.unique(cont_type).size > 1: @@ -323,34 +345,66 @@ def __init__( self._canvas = canvas self._renderer = renderer - if self.mode == "grid": - nrows, ncols = self.shape + if layout_mode == "grid": + n_rows, n_cols = shape + grid_index_iterator = list(product(range(n_rows), range(n_cols))) + self._subplots: np.ndarray[Subplot] = np.empty(shape=shape, dtype=object) + resizeable = False - self._subplots: np.ndarray[Subplot] = np.ndarray( - shape=(nrows, ncols), dtype=object + else: + self._subplots: np.ndarray[Subplot] = np.empty( + shape=n_subplots, dtype=object ) + resizeable = True - for i, (row_ix, col_ix) in enumerate(product(range(nrows), range(ncols))): - camera = subplot_cameras[i] - controller = subplot_controllers[i] + for i in range(n_subplots): + camera = subplot_cameras[i] + controller = subplot_controllers[i] - if subplot_names is not None: - name = subplot_names[i] - else: - name = None - - subplot = Subplot( - parent=self, - camera=camera, - controller=controller, - canvas=canvas, - renderer=renderer, - name=name, - ) + if subplot_names is not None: + name = subplot_names[i] + else: + name = None + + subplot = Subplot( + parent=self, + camera=camera, + controller=controller, + canvas=canvas, + renderer=renderer, + name=name, + rect=rects[i], + extent=extents[i], # figure created extents for grid layout + resizeable=resizeable, + ) + if layout_mode == "grid": + row_ix, col_ix = grid_index_iterator[i] self._subplots[row_ix, col_ix] = subplot + else: + self._subplots[i] = subplot + + if layout_mode == "grid": + self._layout = GridLayout( + self.renderer, + subplots=self._subplots, + canvas_rect=self.get_pygfx_render_area(), + shape=shape, + ) + + elif layout_mode == "rect" or layout_mode == "extent": + self._layout = WindowLayout( + self.renderer, + subplots=self._subplots, + canvas_rect=self.get_pygfx_render_area(), + ) + + self._underlay_camera = UnderlayCamera() + + self._underlay_scene = pygfx.Scene() - self._subplot_grid_positions[subplot] = (row_ix, col_ix) + for subplot in self._subplots.ravel(): + self._underlay_scene.add(subplot.frame._world_object) self._animate_funcs_pre: list[callable] = list() self._animate_funcs_post: list[callable] = list() @@ -366,31 +420,15 @@ def __init__( @property def shape(self) -> list[tuple[int, int, int, int]] | tuple[int, int]: """[n_rows, n_cols]""" - return self._shape + if isinstance(self.layout, GridLayout): + return self.layout.shape @property - def mode(self) -> str: + def layout(self) -> WindowLayout | GridLayout: """ - one of 'grid' or 'rect' - - Used by Figure to determine certain aspects, such as how to calculate - rects and shapes of properties for cameras, controllers, and subplots arrays + Layout engine """ - return self._mode - - @property - def spacing(self) -> int: - """spacing between subplots, in pixels""" - return self._spacing - - @spacing.setter - def spacing(self, value: int): - """set the spacing between subplots, in pixels""" - if not isinstance(value, (int, np.integer)): - raise TypeError("spacing must be of type ") - - self._spacing = value - self._set_viewport_rects() + return self._layout @property def canvas(self) -> BaseRenderCanvas: @@ -407,7 +445,7 @@ def controllers(self) -> np.ndarray[pygfx.Controller]: """controllers, read-only array, access individual subplots to change a controller""" controllers = np.asarray([subplot.controller for subplot in self], dtype=object) - if self.mode == "grid": + if isinstance(self.layout, GridLayout): controllers = controllers.reshape(self.shape) controllers.flags.writeable = False @@ -418,7 +456,7 @@ def cameras(self) -> np.ndarray[pygfx.Camera]: """cameras, read-only array, access individual subplots to change a camera""" cameras = np.asarray([subplot.camera for subplot in self], dtype=object) - if self.mode == "grid": + if isinstance(self.layout, GridLayout): cameras = cameras.reshape(self.shape) cameras.flags.writeable = False @@ -429,25 +467,16 @@ def names(self) -> np.ndarray[str]: """subplot names, read-only array, access individual subplots to change a name""" names = np.asarray([subplot.name for subplot in self]) - if self.mode == "grid": + if isinstance(self.layout, GridLayout): names = names.reshape(self.shape) names.flags.writeable = False return names - def __getitem__(self, index: str | int | tuple[int, int]) -> Subplot: - if isinstance(index, str): - for subplot in self._subplots.ravel(): - if subplot.name == index: - return subplot - raise IndexError(f"no subplot with given name: {index}") - - if self.mode == "grid": - return self._subplots[index[0], index[1]] - - return self._subplots[index] - def _render(self, draw=True): + # draw the underlay planes + self.renderer.render(self._underlay_scene, self._underlay_camera, flush=False) + # call the animation functions before render self._call_animate_functions(self._animate_funcs_pre) for subplot in self: @@ -458,6 +487,10 @@ def _render(self, draw=True): # call post-render animate functions self._call_animate_functions(self._animate_funcs_post) + if draw: + # needs to be here else events don't get processed + self.canvas.request_draw() + def _start_render(self): """start render cycle""" self.canvas.request_draw(self._render) @@ -538,7 +571,7 @@ def show( elif self.canvas.__class__.__name__ == "OffscreenRenderCanvas": # for test and docs gallery screenshots - self._set_viewport_rects() + self._fpl_reset_layout() for subplot in self: subplot.axes.update_using_camera() @@ -709,176 +742,92 @@ def export(self, uri: str | Path | bytes, **kwargs): def open_popup(self, *args, **kwargs): warn("popups only supported by ImguiFigure") - def _fpl_set_subplot_viewport_rect(self, subplot: Subplot): - """ - Sets the viewport rect for the given subplot - """ - - if self.mode == "grid": - # row, col position of this subplot within the grid - row_ix, col_ix = self._subplot_grid_positions[subplot] - - # number of rows, cols in the grid - nrows, ncols = self.shape - - # get starting positions and dimensions for the pygfx portion of the canvas - # anything outside the pygfx portion of the canvas is for imgui - x0_canvas, y0_canvas, width_canvas, height_canvas = ( - self.get_pygfx_render_area() - ) - - # width of an individual subplot - width_subplot = width_canvas / ncols - # height of an individual subplot - height_subplot = height_canvas / nrows - - # x position of this subplot - x_pos = ( - ((col_ix - 1) * width_subplot) - + width_subplot - + x0_canvas - + self.spacing - ) - # y position of this subplot - y_pos = ( - ((row_ix - 1) * height_subplot) - + height_subplot - + y0_canvas - + self.spacing - ) - - if self.__class__.__name__ == "ImguiFigure" and subplot.toolbar: - # leave space for imgui toolbar - height_subplot -= IMGUI_TOOLBAR_HEIGHT - - # clip so that min (w, h) is always 1, otherwise JupyterRenderCanvas causes issues because it - # initializes with a width, height of (0, 0) - rect = np.array( - [ - x_pos, - y_pos, - width_subplot - self.spacing, - height_subplot - self.spacing, - ] - ).clip(min=[0, 0, 1, 1]) - - # adjust if a subplot dock is present - adjust = np.array( - [ - # add left dock size to x_pos - subplot.docks["left"].size, - # add top dock size to y_pos - subplot.docks["top"].size, - # remove left and right dock sizes from width - -subplot.docks["right"].size - subplot.docks["left"].size, - # remove top and bottom dock sizes from height - -subplot.docks["top"].size - subplot.docks["bottom"].size, - ] - ) - - subplot.viewport.rect = rect + adjust + def _fpl_reset_layout(self, *ev): + """set the viewport rects for all subplots, *ev argument is not used, exists because of renderer resize event""" + self.layout.canvas_resized(self.get_pygfx_render_area()) - def _fpl_set_subplot_dock_viewport_rect(self, subplot, position): - """ - Sets the viewport rect for the given subplot dock + def get_pygfx_render_area(self, *args) -> tuple[float, float, float, float]: """ + Get rect for the portion of the canvas that the pygfx renderer draws to, + i.e. non-imgui, part of canvas - dock = subplot.docks[position] + Returns + ------- + tuple[float, float, float, float] + x_pos, y_pos, width, height - if dock.size == 0: - dock.viewport.rect = None - return + """ - if self.mode == "grid": - # row, col position of this subplot within the grid - row_ix, col_ix = self._subplot_grid_positions[subplot] + width, height = self.canvas.get_logical_size() - # number of rows, cols in the grid - nrows, ncols = self.shape + return 0, 0, width, height - x0_canvas, y0_canvas, width_canvas, height_canvas = ( - self.get_pygfx_render_area() + def add_subplot( + self, + rect=None, + extent=None, + camera: str | pygfx.PerspectiveCamera = "2d", + controller: str | pygfx.Controller = None, + name: str = None, + ) -> Subplot: + if isinstance(self.layout, GridLayout): + raise NotImplementedError( + "`add_subplot()` is not implemented for Figures using a GridLayout" ) - # width of an individual subplot - width_subplot = width_canvas / ncols - # height of an individual subplot - height_subplot = height_canvas / nrows - - # calculate the rect based on the dock position - match position: - case "right": - x_pos = ( - ((col_ix - 1) * width_subplot) + (width_subplot * 2) - dock.size - ) - y_pos = ( - ((row_ix - 1) * height_subplot) + height_subplot + self.spacing - ) - width_viewport = dock.size - height_viewport = height_subplot - self.spacing - - case "left": - x_pos = ((col_ix - 1) * width_subplot) + width_subplot - y_pos = ( - ((row_ix - 1) * height_subplot) + height_subplot + self.spacing - ) - width_viewport = dock.size - height_viewport = height_subplot - self.spacing - - case "top": - x_pos = ( - ((col_ix - 1) * width_subplot) + width_subplot + self.spacing - ) - y_pos = ( - ((row_ix - 1) * height_subplot) + height_subplot + self.spacing - ) - width_viewport = width_subplot - self.spacing - height_viewport = dock.size + raise NotImplementedError("Not yet implemented") + + camera = create_camera(camera) + controller = create_controller(controller, camera) + + subplot = Subplot( + parent=self, + camera=camera, + controller=controller, + canvas=self.canvas, + renderer=self.renderer, + name=name, + rect=rect, + extent=extent, # figure created extents for grid layout + resizeable=True, + ) - case "bottom": - x_pos = ( - ((col_ix - 1) * width_subplot) + width_subplot + self.spacing - ) - y_pos = ( - ((row_ix - 1) * height_subplot) - + (height_subplot * 2) - - dock.size - ) - width_viewport = width_subplot - self.spacing - height_viewport = dock.size + return subplot - case _: - raise ValueError("invalid position") + def remove_subplot(self, subplot: Subplot): + raise NotImplementedError("Not yet implemented") - dock.viewport.rect = [ - x_pos + x0_canvas, - y_pos + y0_canvas, - width_viewport, - height_viewport, - ] + if isinstance(self.layout, GridLayout): + raise NotImplementedError( + "`remove_subplot()` is not implemented for Figures using a GridLayout" + ) - def _set_viewport_rects(self, *ev): - """set the viewport rects for all subplots, *ev argument is not used, exists because of renderer resize event""" - for subplot in self: - self._fpl_set_subplot_viewport_rect(subplot) - for dock_pos in subplot.docks.keys(): - self._fpl_set_subplot_dock_viewport_rect(subplot, dock_pos) + if subplot not in self._subplots.tolist(): + raise KeyError(f"given subplot: {subplot} not found in the layout.") - def get_pygfx_render_area(self, *args) -> tuple[int, int, int, int]: - """ - Fet rect for the portion of the canvas that the pygfx renderer draws to, - i.e. non-imgui, part of canvas + subplot.clear() + self._underlay_scene.remove(subplot.frame._world_object) + subplot.frame._world_object.clear() + self.layout._subplots = None + subplots = self._subplots.tolist() + subplots.remove(subplot) + self.layout.remove_subplot(subplot) + del subplot - Returns - ------- - tuple[int, int, int, int] - x_pos, y_pos, width, height + self._subplots = np.asarray(subplots) + self.layout._subplots = self._subplots.ravel() - """ + def __getitem__(self, index: str | int | tuple[int, int]) -> Subplot: + if isinstance(index, str): + for subplot in self._subplots.ravel(): + if subplot.name == index: + return subplot + raise IndexError(f"no subplot with given name: {index}") - width, height = self.canvas.get_logical_size() + if isinstance(self.layout, GridLayout): + return self._subplots[index[0], index[1]] - return 0, 0, width, height + return self._subplots[index] def __iter__(self): self._current_iter = iter(range(len(self))) @@ -890,19 +839,16 @@ def __next__(self) -> Subplot: def __len__(self): """number of subplots""" - if isinstance(self._shape, tuple): - return self.shape[0] * self.shape[1] - if isinstance(self._shape, list): - return len(self._shape) + return len(self._layout) def __str__(self): - return f"{self.__class__.__name__} @ {hex(id(self))}" + return f"{self.__class__.__name__}" def __repr__(self): newline = "\n\t" return ( - f"fastplotlib.{self.__class__.__name__} @ {hex(id(self))}\n" + f"fastplotlib.{self.__class__.__name__}" f" Subplots:\n" f"\t{newline.join(subplot.__str__() for subplot in self)}" f"\n" diff --git a/fastplotlib/layouts/_frame.py b/fastplotlib/layouts/_frame.py new file mode 100644 index 000000000..cd2a1cbc2 --- /dev/null +++ b/fastplotlib/layouts/_frame.py @@ -0,0 +1,371 @@ +import numpy as np +import pygfx + +from ._rect import RectManager +from ._utils import IMGUI_TOOLBAR_HEIGHT +from ..utils.types import SelectorColorStates +from ..graphics import TextGraphic + + +""" +Each Subplot is framed by a 2D plane mesh, a rectangle. +The rectangles are viewed using the UnderlayCamera where (0, 0) is the top left corner. +We can control the bbox of this rectangle by changing the x and y boundaries of the rectangle. + +Note how the y values of the plane mesh are negative, this is because of the UnderlayCamera. +We always just keep the positive y value, and make it negative only when setting the plane mesh. + +Illustration: + +(0, 0) --------------------------------------------------- +---------------------------------------------------------- +---------------------------------------------------------- +--------------(x0, -y0) --------------- (x1, -y0) -------- +------------------------|||||||||||||||------------------- +------------------------|||||||||||||||------------------- +------------------------|||||||||||||||------------------- +------------------------|||rectangle|||------------------- +------------------------|||||||||||||||------------------- +------------------------|||||||||||||||------------------- +------------------------|||||||||||||||------------------- +--------------(x0, -y1) --------------- (x1, -y1)--------- +---------------------------------------------------------- +------------------------------------------- (canvas_width, canvas_height) + +""" + + +# wgsl shader snippet for SDF function that defines the resize handler, a lower right triangle. +sdf_wgsl_resize_handle = """ +// hardcode square root of 2 +let m_sqrt_2 = 1.4142135; + +// given a distance from an origin point, this defines the hypotenuse of a lower right triangle +let distance = (-coord.x + coord.y) / m_sqrt_2; + +// return distance for this position +return distance * size; +""" + + +class MeshMasks: + """Used set the x0, x1, y0, y1 positions of the plane mesh""" + + x0 = np.array( + [ + [False, False, False], + [True, False, False], + [False, False, False], + [True, False, False], + ] + ) + + x1 = np.array( + [ + [True, False, False], + [False, False, False], + [True, False, False], + [False, False, False], + ] + ) + + y0 = np.array( + [ + [False, True, False], + [False, True, False], + [False, False, False], + [False, False, False], + ] + ) + + y1 = np.array( + [ + [False, False, False], + [False, False, False], + [False, True, False], + [False, True, False], + ] + ) + + +masks = MeshMasks + + +class Frame: + # resize handle color states + resize_handle_color = SelectorColorStates( + idle=(0.6, 0.6, 0.6, 1), # gray + highlight=(1, 1, 1, 1), # white + action=(1, 0, 1, 1), # magenta + ) + + # plane color states + plane_color = SelectorColorStates( + idle=(0.1, 0.1, 0.1), # dark grey + highlight=(0.2, 0.2, 0.2), # less dark grey + action=(0.1, 0.1, 0.2), # dark gray-blue + ) + + def __init__( + self, + viewport, + rect, + extent, + resizeable, + title, + docks, + toolbar_visible, + canvas_rect, + ): + """ + Manages the plane mesh, resize handle point, and subplot title. + It also sets the viewport rects for the subplot rect and the rects of the docks. + + Note: This is a backend class not meant to be user-facing. + + Parameters + ---------- + viewport: pygfx.Viewport + Subplot viewport + + rect: tuple | np.ndarray + rect of this subplot + + extent: tuple | np.ndarray + extent of this subplot + + resizeable: bool + if the Frame is resizeable or not + + title: str + subplot title + + docks: dict[str, PlotArea] + subplot dock + + toolbar_visible: bool + toolbar visibility + + canvas_rect: tuple + figure canvas rect, the render area excluding any areas taken by imgui edge windows + + """ + + self.viewport = viewport + self.docks = docks + self._toolbar_visible = toolbar_visible + + # create rect manager to handle all the backend rect calculations + if rect is not None: + self._rect_manager = RectManager(*rect, canvas_rect) + elif extent is not None: + self._rect_manager = RectManager.from_extent(extent, canvas_rect) + else: + raise ValueError("Must provide `rect` or `extent`") + + wobjects = list() + + # make title graphic + if title is None: + title_text = "" + else: + title_text = title + self._title_graphic = TextGraphic(title_text, font_size=16, face_color="white") + wobjects.append(self._title_graphic.world_object) + + # init mesh of size 1 to graphically represent rect + geometry = pygfx.plane_geometry(1, 1) + material = pygfx.MeshBasicMaterial(color=self.plane_color.idle, pick_write=True) + self._plane = pygfx.Mesh(geometry, material) + wobjects.append(self._plane) + + # otherwise text isn't visible + self._plane.world.z = 0.5 + + # create resize handler at point (x1, y1) + x1, y1 = self.extent[[1, 3]] + self._resize_handle = pygfx.Points( + # note negative y since y is inverted in UnderlayCamera + # subtract 7 so that the bottom right corner of the triangle is at the center + pygfx.Geometry(positions=[[x1 - 7, -y1 + 7, 0]]), + pygfx.PointsMarkerMaterial( + color=self.resize_handle_color.idle, + marker="custom", + custom_sdf=sdf_wgsl_resize_handle, + size=12, + size_space="screen", + pick_write=True, + ), + ) + + if not resizeable: + # set all color states to transparent if Frame isn't resizeable + c = (0, 0, 0, 0) + self._resize_handle.material.color = c + self._resize_handle.material.edge_width = 0 + self.resize_handle_color = SelectorColorStates(c, c, c) + + wobjects.append(self._resize_handle) + + self._world_object = pygfx.Group() + self._world_object.add(*wobjects) + + self._reset() + self.reset_viewport() + + @property + def rect_manager(self) -> RectManager: + return self._rect_manager + + @property + def extent(self) -> np.ndarray: + """extent, (xmin, xmax, ymin, ymax)""" + # not actually stored, computed when needed + return self._rect_manager.extent + + @extent.setter + def extent(self, extent): + self._rect_manager.extent = extent + self._reset() + self.reset_viewport() + + @property + def rect(self) -> np.ndarray[int]: + """rect in absolute screen space, (x, y, w, h)""" + return self._rect_manager.rect + + @rect.setter + def rect(self, rect: np.ndarray): + self._rect_manager.rect = rect + self._reset() + self.reset_viewport() + + def reset_viewport(self): + """reset the viewport rect for the subplot and docks""" + + # get rect of the render area + x, y, w, h = self.get_render_rect() + + # dock sizes + s_left = self.docks["left"].size + s_top = self.docks["top"].size + s_right = self.docks["right"].size + s_bottom = self.docks["bottom"].size + + # top and bottom have same width + # subtract left and right dock sizes + w_top_bottom = w - s_left - s_right + # top and bottom have same x pos + x_top_bottom = x + s_left + + # set dock rects + self.docks["left"].viewport.rect = x, y, s_left, h + self.docks["top"].viewport.rect = x_top_bottom, y, w_top_bottom, s_top + self.docks["bottom"].viewport.rect = ( + x_top_bottom, + y + h - s_bottom, + w_top_bottom, + s_bottom, + ) + self.docks["right"].viewport.rect = x + w - s_right, y, s_right, h + + # calc subplot rect by adjusting for dock sizes + x += s_left + y += s_top + w -= s_left + s_right + h -= s_top + s_bottom + + # set subplot rect + self.viewport.rect = x, y, w, h + + def get_render_rect(self) -> tuple[float, float, float, float]: + """ + Get the actual render area of the subplot, including the docks. + + Excludes area taken by the subplot title and toolbar. Also adds a small amount of spacing around the subplot. + """ + # the rect of the entire Frame + x, y, w, h = self.rect + + x += 1 # add 1 so a 1 pixel edge is visible + w -= 2 # subtract 2, so we get a 1 pixel edge on both sides + + # add 4 pixels above and below title for better spacing + y = y + 4 + self._title_graphic.font_size + 4 + + # spacing on the bottom if imgui toolbar is visible + if self.toolbar_visible: + toolbar_space = IMGUI_TOOLBAR_HEIGHT + resize_handle_space = 0 + else: + toolbar_space = 0 + # need some space for resize handler if imgui toolbar isn't present + resize_handle_space = 13 + + # adjust for the 4 pixels from the line above + # also give space for resize handler if imgui toolbar is not present + h = ( + h + - 4 + - self._title_graphic.font_size + - toolbar_space + - 4 + - resize_handle_space + ) + + return x, y, w, h + + def _reset(self): + """reset the plane mesh using the current rect state""" + + x0, x1, y0, y1 = self._rect_manager.extent + w = self._rect_manager.w + + self._plane.geometry.positions.data[masks.x0] = x0 + self._plane.geometry.positions.data[masks.x1] = x1 + + # negative y because UnderlayCamera y is inverted + self._plane.geometry.positions.data[masks.y0] = -y0 + self._plane.geometry.positions.data[masks.y1] = -y1 + + self._plane.geometry.positions.update_full() + + # note negative y since y is inverted in UnderlayCamera + # subtract 7 so that the bottom right corner of the triangle is at the center + self._resize_handle.geometry.positions.data[0] = [x1 - 7, -y1 + 7, 0] + self._resize_handle.geometry.positions.update_full() + + # set subplot title position + x = x0 + (w / 2) + y = y0 + (self._title_graphic.font_size / 2) + self._title_graphic.world_object.world.x = x + self._title_graphic.world_object.world.y = -y - 4 # add 4 pixels for spacing + + @property + def toolbar_visible(self) -> bool: + return self._toolbar_visible + + @toolbar_visible.setter + def toolbar_visible(self, visible: bool): + self._toolbar_visible = visible + self.reset_viewport() + + @property + def title_graphic(self) -> TextGraphic: + return self._title_graphic + + @property + def plane(self) -> pygfx.Mesh: + """the plane mesh""" + return self._plane + + @property + def resize_handle(self) -> pygfx.Points: + """resize handler point""" + return self._resize_handle + + def canvas_resized(self, canvas_rect): + """called by layout is resized""" + self._rect_manager.canvas_resized(canvas_rect) + self._reset() + self.reset_viewport() diff --git a/fastplotlib/layouts/_graphic_methods_mixin.py b/fastplotlib/layouts/_graphic_methods_mixin.py index a08e9b110..a04b681f5 100644 --- a/fastplotlib/layouts/_graphic_methods_mixin.py +++ b/fastplotlib/layouts/_graphic_methods_mixin.py @@ -9,9 +9,6 @@ class GraphicMethodsMixin: - def __init__(self): - pass - def _create_graphic(self, graphic_class, *args, **kwargs) -> Graphic: if "center" in kwargs.keys(): center = kwargs.pop("center") diff --git a/fastplotlib/layouts/_imgui_figure.py b/fastplotlib/layouts/_imgui_figure.py index 2e77f350d..f6d3da20f 100644 --- a/fastplotlib/layouts/_imgui_figure.py +++ b/fastplotlib/layouts/_imgui_figure.py @@ -20,7 +20,9 @@ class ImguiFigure(Figure): def __init__( self, - shape: list[tuple[int, int, int, int]] | tuple[int, int] = (1, 1), + shape: tuple[int, int] = (1, 1), + rects=None, + extents=None, cameras: ( Literal["2d", "3d"] | Iterable[Iterable[Literal["2d", "3d"]]] @@ -52,6 +54,8 @@ def __init__( super().__init__( shape=shape, + rects=rects, + extents=extents, cameras=cameras, controller_types=controller_types, controller_ids=controller_ids, @@ -109,6 +113,8 @@ def _render(self, draw=False): super()._render(draw) self.imgui_renderer.render() + + # needs to be here else events don't get processed self.canvas.request_draw() def _draw_imgui(self) -> imgui.ImDrawData: @@ -164,11 +170,11 @@ def add_gui(self, gui: EdgeWindow): self.guis[location] = gui - self._set_viewport_rects() + self._fpl_reset_layout() def get_pygfx_render_area(self, *args) -> tuple[int, int, int, int]: """ - Fet rect for the portion of the canvas that the pygfx renderer draws to, + Get rect for the portion of the canvas that the pygfx renderer draws to, i.e. non-imgui, part of canvas Returns diff --git a/fastplotlib/layouts/_plot_area.py b/fastplotlib/layouts/_plot_area.py index c4e6a9d70..2e69af100 100644 --- a/fastplotlib/layouts/_plot_area.py +++ b/fastplotlib/layouts/_plot_area.py @@ -11,6 +11,7 @@ from ._utils import create_controller from ..graphics._base import Graphic from ..graphics.selectors._base_selector import BaseSelector +from ._graphic_methods_mixin import GraphicMethodsMixin from ..legends import Legend @@ -24,7 +25,7 @@ IPYTHON = get_ipython() -class PlotArea: +class PlotArea(GraphicMethodsMixin): def __init__( self, parent: Union["PlotArea", "Figure"], @@ -712,7 +713,7 @@ def __str__(self): else: name = self.name - return f"{name}: {self.__class__.__name__} @ {hex(id(self))}" + return f"{name}: {self.__class__.__name__}" def __repr__(self): newline = "\n\t" diff --git a/fastplotlib/layouts/_rect.py b/fastplotlib/layouts/_rect.py new file mode 100644 index 000000000..aa84ee8a2 --- /dev/null +++ b/fastplotlib/layouts/_rect.py @@ -0,0 +1,239 @@ +import numpy as np + + +class RectManager: + """ + Backend management of a rect. Allows converting between rects and extents, also works with fractional inputs. + """ + + def __init__(self, x: float, y: float, w: float, h: float, canvas_rect: tuple): + # initialize rect state arrays + # used to store internal state of the rect in both fractional screen space and absolute screen space + # the purpose of storing the fractional rect is that it remains constant when the canvas resizes + self._rect_frac = np.zeros(4, dtype=np.float64) + self._rect_screen_space = np.zeros(4, dtype=np.float64) + self._canvas_rect = np.asarray(canvas_rect) + + self._set((x, y, w, h)) + + def _set(self, rect): + """ + Using the passed rect which is either absolute screen space or fractional, + set the internal fractional and absolute screen space rects + """ + rect = np.asarray(rect) + for val, name in zip(rect, ["x-position", "y-position", "width", "height"]): + if val < 0: + raise ValueError( + f"Invalid rect value < 0: {rect}\n All values must be non-negative." + ) + + if (rect[2:] <= 1).all(): # fractional bbox + self._set_from_fract(rect) + + elif (rect[2:] > 1).all(): # bbox in already in screen coords coordinates + self._set_from_screen_space(rect) + + else: + raise ValueError(f"Invalid rect: {rect}") + + def _set_from_fract(self, rect): + """set rect from fractional representation""" + _, _, cw, ch = self._canvas_rect + mult = np.array([cw, ch, cw, ch]) + + # check that widths, heights are valid: + if rect[0] + rect[2] > 1: + raise ValueError( + f"invalid fractional rect: {rect}\n x + width > 1: {rect[0]} + {rect[2]} > 1" + ) + if rect[1] + rect[3] > 1: + raise ValueError( + f"invalid fractional rect: {rect}\n y + height > 1: {rect[1]} + {rect[3]} > 1" + ) + + # assign values to the arrays, don't just change the reference + self._rect_frac[:] = rect + self._rect_screen_space[:] = self._rect_frac * mult + + def _set_from_screen_space(self, rect): + """set rect from screen space representation""" + _, _, cw, ch = self._canvas_rect + mult = np.array([cw, ch, cw, ch]) + # for screen coords allow (x, y) = 1 or 0, but w, h must be > 1 + # check that widths, heights are valid + if rect[0] + rect[2] > cw: + raise ValueError( + f"invalid rect: {rect}\n x + width > canvas width: {rect[0]} + {rect[2]} > {cw}" + ) + if rect[1] + rect[3] > ch: + raise ValueError( + f"invalid rect: {rect}\n y + height > canvas height: {rect[1]} + {rect[3]} >{ch}" + ) + + self._rect_frac[:] = rect / mult + self._rect_screen_space[:] = rect + + @property + def x(self) -> np.float64: + """x position""" + return self._rect_screen_space[0] + + @property + def y(self) -> np.float64: + """y position""" + return self._rect_screen_space[1] + + @property + def w(self) -> np.float64: + """width""" + return self._rect_screen_space[2] + + @property + def h(self) -> np.float64: + """height""" + return self._rect_screen_space[3] + + @property + def rect(self) -> np.ndarray: + """rect, (x, y, w, h)""" + return self._rect_screen_space + + @rect.setter + def rect(self, rect: np.ndarray | tuple): + self._set(rect) + + def canvas_resized(self, canvas_rect: tuple): + # called by Frame when canvas is resized + self._canvas_rect[:] = canvas_rect + # set new rect using existing rect_frac since this remains constant regardless of resize + self._set(self._rect_frac) + + @property + def x0(self) -> np.float64: + """x0 position""" + return self.x + + @property + def x1(self) -> np.float64: + """x1 position""" + return self.x + self.w + + @property + def y0(self) -> np.float64: + """y0 position""" + return self.y + + @property + def y1(self) -> np.float64: + """y1 position""" + return self.y + self.h + + @classmethod + def from_extent(cls, extent, canvas_rect): + """create a RectManager from an extent""" + rect = cls.extent_to_rect(extent, canvas_rect) + return cls(*rect, canvas_rect) + + @property + def extent(self) -> np.ndarray: + """extent, (xmin, xmax, ymin, ymax)""" + # not actually stored, computed when needed + return np.asarray([self.x0, self.x1, self.y0, self.y1]) + + @extent.setter + def extent(self, extent): + rect = RectManager.extent_to_rect(extent, canvas_rect=self._canvas_rect) + + self._set(rect) + + @staticmethod + def extent_to_rect(extent, canvas_rect): + """convert an extent to a rect""" + RectManager.validate_extent(extent, canvas_rect) + x0, x1, y0, y1 = extent + + # width and height + w = x1 - x0 + h = y1 - y0 + + return x0, y0, w, h + + @staticmethod + def validate_extent(extent: np.ndarray | tuple, canvas_rect: tuple): + extent = np.asarray(extent) + cx0, cy0, cw, ch = canvas_rect + + # make sure extent is valid + if (extent < 0).any(): + raise ValueError(f"extent must be non-negative, you have passed: {extent}") + + if extent[1] <= 1 or extent[3] <= 1: # if x1 <= 1, or y1 <= 1 + # if fractional rect, convert to full + if not (extent <= 1).all(): # if x1 and y1 <= 1, then all vals must be <= 1 + raise ValueError( + f"if passing a fractional extent, all values must be fractional, you have passed: {extent}" + ) + extent *= np.asarray([cw, cw, ch, ch]) + + x0, x1, y0, y1 = extent + + # width and height + w = x1 - x0 + h = y1 - y0 + + # check if x1 - x0 <= 0 + if w <= 0: + raise ValueError(f"extent x-range must be non-negative: {extent}") + + # check if y1 - y0 <= 0 + if h <= 0: + raise ValueError(f"extent y-range must be non-negative: {extent}") + + # calc canvas extent + cx1 = cx0 + cw + cy1 = cy0 + ch + canvas_extent = np.asarray([cx0, cx1, cy0, cy1]) + + if x0 < cx0 or x1 < cx0 or x0 > cx1 or x1 > cx1: + raise ValueError( + f"extent: {extent} x-range is beyond the bounds of the canvas: {canvas_extent}" + ) + if y0 < cy0 or y1 < cy0 or y0 > cy1 or y1 > cy1: + raise ValueError( + f"extent: {extent} y-range is beyond the bounds of the canvas: {canvas_extent}" + ) + + def is_above(self, y0, dist: int = 1) -> bool: + # our bottom < other top within given distance + return self.y1 < y0 + dist + + def is_below(self, y1, dist: int = 1) -> bool: + # our top > other bottom + return self.y0 > y1 - dist + + def is_left_of(self, x0, dist: int = 1) -> bool: + # our right_edge < other left_edge + # self.x1 < other.x0 + return self.x1 < x0 + dist + + def is_right_of(self, x1, dist: int = 1) -> bool: + # self.x0 > other.x1 + return self.x0 > x1 - dist + + def overlaps(self, extent: np.ndarray) -> bool: + """returns whether this rect overlaps with the given extent""" + x0, x1, y0, y1 = extent + return not any( + [ + self.is_above(y0), + self.is_below(y1), + self.is_left_of(x0), + self.is_right_of(x1), + ] + ) + + def __repr__(self): + s = f"{self._rect_frac}\n{self.rect}" + + return s diff --git a/fastplotlib/layouts/_subplot.py b/fastplotlib/layouts/_subplot.py index a97e89b0d..73f669fe5 100644 --- a/fastplotlib/layouts/_subplot.py +++ b/fastplotlib/layouts/_subplot.py @@ -1,29 +1,32 @@ from typing import Literal, Union -import pygfx +import numpy as np +import pygfx from rendercanvas import BaseRenderCanvas from ..graphics import TextGraphic from ._utils import create_camera, create_controller from ._plot_area import PlotArea -from ._graphic_methods_mixin import GraphicMethodsMixin +from ._frame import Frame from ..graphics._axes import Axes -class Subplot(PlotArea, GraphicMethodsMixin): +class Subplot(PlotArea): def __init__( self, parent: Union["Figure"], camera: Literal["2d", "3d"] | pygfx.PerspectiveCamera, - controller: pygfx.Controller, + controller: pygfx.Controller | str, canvas: BaseRenderCanvas | pygfx.Texture, + rect: np.ndarray = None, + extent: np.ndarray = None, + resizeable: bool = True, renderer: pygfx.WgpuRenderer = None, name: str = None, ): """ - General plot object is found within a ``Figure``. Each ``Figure`` instance will have [n rows, n columns] - of subplots. + Subplot class. .. important:: ``Subplot`` is not meant to be constructed directly, it only exists as part of a ``Figure`` @@ -33,9 +36,6 @@ def __init__( parent: 'Figure' | None parent Figure instance - position: (int, int), optional - corresponds to the [row, column] position of the subplot within a ``Figure`` - camera: str or pygfx.PerspectiveCamera, default '2d' indicates the FOV for the camera, '2d' sets ``fov = 0``, '3d' sets ``fov = 50``. ``fov`` can be changed at any time. @@ -56,19 +56,18 @@ def __init__( """ - super(GraphicMethodsMixin, self).__init__() - camera = create_camera(camera) controller = create_controller(controller_type=controller, camera=camera) self._docks = dict() - self._title_graphic: TextGraphic = None - - self._toolbar = True + if "Imgui" in parent.__class__.__name__: + toolbar_visible = True + else: + toolbar_visible = False - super(Subplot, self).__init__( + super().__init__( parent=parent, camera=camera, controller=controller, @@ -79,23 +78,33 @@ def __init__( ) for pos in ["left", "top", "right", "bottom"]: - dv = Dock(self, pos, size=0) + dv = Dock(self, size=0) dv.name = pos self.docks[pos] = dv self.children.append(dv) - if self.name is not None: - self.set_title(self.name) - self._axes = Axes(self) self.scene.add(self.axes.world_object) + self._frame = Frame( + viewport=self.viewport, + rect=rect, + extent=extent, + resizeable=resizeable, + title=name, + docks=self.docks, + toolbar_visible=toolbar_visible, + canvas_rect=parent.get_pygfx_render_area(), + ) + @property def axes(self) -> Axes: + """Axes object""" return self._axes @property def name(self) -> str: + """Subplot name""" return self._name @name.setter @@ -130,60 +139,40 @@ def docks(self) -> dict: @property def toolbar(self) -> bool: """show/hide toolbar""" - return self._toolbar + return self.frame.toolbar_visible @toolbar.setter def toolbar(self, visible: bool): - self._toolbar = bool(visible) - self.get_figure()._fpl_set_subplot_viewport_rect(self) + self.frame.toolbar_visible = visible + self.frame.reset_viewport() def _render(self): self.axes.update_using_camera() super()._render() - def set_title(self, text: str): - """Sets the plot title, stored as a ``TextGraphic`` in the "top" dock area""" - if text is None: - return + @property + def title(self) -> TextGraphic: + """subplot title""" + return self._frame.title_graphic + @title.setter + def title(self, text: str): text = str(text) - if self._title_graphic is not None: - self._title_graphic.text = text - else: - tg = TextGraphic(text=text, font_size=18) - self._title_graphic = tg - - self.docks["top"].size = 35 - self.docks["top"].add_graphic(tg) - - self.center_title() + self.title.text = text - def center_title(self): - """Centers name of subplot.""" - if self._title_graphic is None: - raise AttributeError("No title graphic is set") - - self._title_graphic.world_object.position = (0, 0, 0) - self.docks["top"].center_graphic(self._title_graphic, zoom=1.5) - self._title_graphic.world_object.position_y = -3.5 + @property + def frame(self) -> Frame: + """Frame that the subplot lives in""" + return self._frame class Dock(PlotArea): - _valid_positions = ["right", "left", "top", "bottom"] - def __init__( self, parent: Subplot, - position: str, size: int, ): - if position not in self._valid_positions: - raise ValueError( - f"the `position` of an AnchoredViewport must be one of: {self._valid_positions}" - ) - self._size = size - self._position = position super().__init__( parent=parent, @@ -194,10 +183,6 @@ def __init__( renderer=parent.renderer, ) - @property - def position(self) -> str: - return self._position - @property def size(self) -> int: """Get or set the size of this dock""" @@ -206,14 +191,7 @@ def size(self) -> int: @size.setter def size(self, s: int): self._size = s - if self.position == "top": - # TODO: treat title dock separately, do not allow user to change viewport stuff - return - - self.get_figure(self.parent)._fpl_set_subplot_viewport_rect(self.parent) - self.get_figure(self.parent)._fpl_set_subplot_dock_viewport_rect( - self.parent, self._position - ) + self.get_figure()._fpl_reset_layout() def _render(self): if self.size == 0: diff --git a/fastplotlib/layouts/_utils.py b/fastplotlib/layouts/_utils.py index b42971570..866c26aa3 100644 --- a/fastplotlib/layouts/_utils.py +++ b/fastplotlib/layouts/_utils.py @@ -1,10 +1,24 @@ import importlib +from itertools import product + +import numpy as np import pygfx from pygfx import WgpuRenderer, Texture, Renderer from ..utils.gui import BaseRenderCanvas, RenderCanvas +try: + import imgui_bundle +except ImportError: + IMGUI = False +else: + IMGUI = True + + +# number of pixels taken by the imgui toolbar when present +IMGUI_TOOLBAR_HEIGHT = 39 + def make_canvas_and_renderer( canvas: str | BaseRenderCanvas | Texture | None, @@ -92,3 +106,20 @@ def create_controller( ) return controller_types[controller_type](camera) + + +def get_extents_from_grid( + shape: tuple[int, int], +) -> list[tuple[float, float, float, float]]: + """create fractional extents from a given grid shape""" + x_min = np.arange(0, 1, (1 / shape[1])) + x_max = x_min + 1 / shape[1] + y_min = np.arange(0, 1, (1 / shape[0])) + y_max = y_min + 1 / shape[0] + + extents = list() + for row_ix, col_ix in product(range(shape[0]), range(shape[1])): + extent = x_min[col_ix], x_max[col_ix], y_min[row_ix], y_max[row_ix] + extents.append(extent) + + return extents diff --git a/fastplotlib/ui/_subplot_toolbar.py b/fastplotlib/ui/_subplot_toolbar.py index 7d183bf6d..a06e81b90 100644 --- a/fastplotlib/ui/_subplot_toolbar.py +++ b/fastplotlib/ui/_subplot_toolbar.py @@ -2,6 +2,7 @@ from ..layouts._subplot import Subplot from ._base import Window +from ..layouts._utils import IMGUI_TOOLBAR_HEIGHT class SubplotToolbar(Window): @@ -16,15 +17,18 @@ def __init__(self, subplot: Subplot, fa_icons: imgui.ImFont): def update(self): # get subplot rect - x, y, width, height = self._subplot.viewport.rect - y += self._subplot.docks["bottom"].size + x, y, width, height = self._subplot.frame.rect # place the toolbar window below the subplot - pos = (x, y + height) + pos = (x + 1, y + height - IMGUI_TOOLBAR_HEIGHT) - imgui.set_next_window_size((width, 0)) + imgui.set_next_window_size((width - 18, 0)) imgui.set_next_window_pos(pos) - flags = imgui.WindowFlags_.no_collapse | imgui.WindowFlags_.no_title_bar + flags = ( + imgui.WindowFlags_.no_collapse + | imgui.WindowFlags_.no_title_bar + | imgui.WindowFlags_.no_background + ) imgui.begin(f"Toolbar-{hex(id(self._subplot))}", p_open=None, flags=flags) diff --git a/fastplotlib/ui/right_click_menus/_standard_menu.py b/fastplotlib/ui/right_click_menus/_standard_menu.py index 772baa170..1937df858 100644 --- a/fastplotlib/ui/right_click_menus/_standard_menu.py +++ b/fastplotlib/ui/right_click_menus/_standard_menu.py @@ -55,7 +55,7 @@ def update(self): # open popup only if mouse was not moved between mouse_down and mouse_up events if self._last_right_click_pos == imgui.get_mouse_pos(): - if self.get_subplot(): + if self.get_subplot() is not False: # must explicitly check for False # open only if right click was inside a subplot imgui.open_popup(f"right-click-menu") @@ -64,7 +64,7 @@ def update(self): self.cleanup() if imgui.begin_popup(f"right-click-menu"): - if not self.get_subplot(): + if self.get_subplot() is False: # must explicitly check for False # for some reason it will still trigger at certain locations # despite open_popup() only being called when an actual # subplot is returned diff --git a/fastplotlib/utils/types.py b/fastplotlib/utils/types.py new file mode 100644 index 000000000..e99fce2fc --- /dev/null +++ b/fastplotlib/utils/types.py @@ -0,0 +1,4 @@ +from collections import namedtuple + + +SelectorColorStates = namedtuple("state", ["idle", "highlight", "action"]) diff --git a/scripts/generate_add_graphic_methods.py b/scripts/generate_add_graphic_methods.py index d69185521..533ae77c6 100644 --- a/scripts/generate_add_graphic_methods.py +++ b/scripts/generate_add_graphic_methods.py @@ -34,8 +34,6 @@ def generate_add_graphics_methods(): f.write("from ..graphics._base import Graphic\n\n") f.write("\nclass GraphicMethodsMixin:\n") - f.write(" def __init__(self):\n") - f.write(" pass\n\n") f.write( " def _create_graphic(self, graphic_class, *args, **kwargs) -> Graphic:\n" diff --git a/tests/test_text_graphic.py b/tests/test_text_graphic.py index a13dfe690..deb25ca6b 100644 --- a/tests/test_text_graphic.py +++ b/tests/test_text_graphic.py @@ -25,7 +25,7 @@ def test_create_graphic(): assert text.font_size == 14 assert isinstance(text._font_size, FontSize) - assert text.world_object.geometry.font_size == 14 + assert text.world_object.font_size == 14 assert text.face_color == pygfx.Color("w") assert isinstance(text._face_color, TextFaceColor) @@ -82,7 +82,7 @@ def test_text_changes_events(): text.font_size = 10.0 assert text.font_size == 10.0 - assert text.world_object.geometry.font_size == 10 + assert text.world_object.font_size == 10 check_event(text, "font_size", 10) text.face_color = "r" From 61d7962cfc76fc91bc9c18c2e94998d88e00c463 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Mon, 17 Mar 2025 14:07:52 -0400 Subject: [PATCH 162/185] fix comment, fix doc example readme file (#769) * fix comment, fix doc example readme file * update get_cmap_texture to return pygfx.Texture since cmap lib changed --- examples/window_layouts/README.rst | 4 ++-- fastplotlib/layouts/_plot_area.py | 4 +--- fastplotlib/utils/functions.py | 2 +- tests/test_image_graphic.py | 6 ++++++ 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/examples/window_layouts/README.rst b/examples/window_layouts/README.rst index 3c7df2366..23684627b 100644 --- a/examples/window_layouts/README.rst +++ b/examples/window_layouts/README.rst @@ -1,2 +1,2 @@ -WindowLayout Examples -===================== +Window Layout Examples +====================== diff --git a/fastplotlib/layouts/_plot_area.py b/fastplotlib/layouts/_plot_area.py index 2e69af100..e780607ce 100644 --- a/fastplotlib/layouts/_plot_area.py +++ b/fastplotlib/layouts/_plot_area.py @@ -87,12 +87,10 @@ def __init__( self._animate_funcs_pre: list[callable] = list() self._animate_funcs_post: list[callable] = list() - # list of hex id strings for all graphics managed by this PlotArea - # the real Graphic instances are managed by REFERENCES + # list of all graphics managed by this PlotArea self._graphics: list[Graphic] = list() # selectors are in their own list so they can be excluded from scene bbox calculations - # managed similar to GRAPHICS for garbage collection etc. self._selectors: list[BaseSelector] = list() # legends, managed just like other graphics as explained above diff --git a/fastplotlib/utils/functions.py b/fastplotlib/utils/functions.py index 910eba8e8..6ad365e40 100644 --- a/fastplotlib/utils/functions.py +++ b/fastplotlib/utils/functions.py @@ -205,7 +205,7 @@ def make_colors(n_colors: int, cmap: str, alpha: float = 1.0) -> np.ndarray: def get_cmap_texture(name: str, alpha: float = 1.0) -> Texture: - return cmap_lib.Colormap(name).to_pygfx() + return Texture(get_cmap(name, alpha), dim=1) def make_colors_dict(labels: Sequence, cmap: str, **kwargs) -> OrderedDict: diff --git a/tests/test_image_graphic.py b/tests/test_image_graphic.py index 0ea9979a6..02b982d80 100644 --- a/tests/test_image_graphic.py +++ b/tests/test_image_graphic.py @@ -2,6 +2,8 @@ from numpy import testing as npt import imageio.v3 as iio +import pygfx + import fastplotlib as fpl from fastplotlib.graphics._features import FeatureEvent from fastplotlib.utils import make_colors @@ -86,6 +88,10 @@ def test_gray(): # the entire image should be in the single Texture buffer npt.assert_almost_equal(ig.data.buffer[0, 0].data, GRAY_IMAGE) + assert isinstance(ig._material, pygfx.ImageBasicMaterial) + assert isinstance(ig._material.map, pygfx.TextureMap) + assert isinstance(ig._material.map.texture, pygfx.Texture) + ig.cmap = "viridis" assert ig.cmap == "viridis" check_event(graphic=ig, feature="cmap", value="viridis") From 10d6dd5f662a862f7fcba75982697336a44c2c07 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Tue, 18 Mar 2025 08:30:45 -0400 Subject: [PATCH 163/185] Imgui stats (#771) * add imgui stats overlay, add canvas_kwargs kwarg to Figure * update spiral example * random sizes for scatter points * reduce n * better spin rate for docs gallery --- examples/scatter/spinning_spiral.py | 34 ++++++++++++++++++++++++---- fastplotlib/layouts/_figure.py | 12 +++++++++- fastplotlib/layouts/_imgui_figure.py | 25 ++++++++++++-------- fastplotlib/layouts/_utils.py | 4 ++-- setup.py | 4 ++-- 5 files changed, 59 insertions(+), 20 deletions(-) diff --git a/examples/scatter/spinning_spiral.py b/examples/scatter/spinning_spiral.py index c032fc1c8..56cdcb906 100644 --- a/examples/scatter/spinning_spiral.py +++ b/examples/scatter/spinning_spiral.py @@ -2,11 +2,13 @@ Spinning spiral scatter ======================= -Example of a spinning spiral scatter +Example of a spinning spiral scatter. + +This example with 1 million points runs at 125 fps on an AMD RX 570. """ # test_example = false -# sphinx_gallery_pygfx_docs = 'animate 10s' +# sphinx_gallery_pygfx_docs = 'animate 15s' import numpy as np import fastplotlib as fpl @@ -23,16 +25,32 @@ data = np.column_stack([xs, ys, zs]) -figure = fpl.Figure(cameras="3d", size=(700, 560)) +# generate some random sizes for the points +sizes = np.abs(np.random.normal(loc=0, scale=1, size=n)) + +figure = fpl.Figure( + cameras="3d", + size=(700, 560), + canvas_kwargs={"max_fps": 500, "vsync": False} +) -spiral = figure[0, 0].add_scatter(data, cmap="viridis_r", alpha=0.8) +spiral = figure[0, 0].add_scatter(data, cmap="viridis_r", alpha=0.5, sizes=sizes) + +# pre-generate normally distributed data to jitter the points before each render +jitter = np.random.normal(scale=0.001, size=n * 3).reshape((n, 3)) def update(): # rotate around y axis spiral.rotate(0.005, axis="y") + # add small jitter - spiral.data[:] += np.random.normal(scale=0.01, size=n * 3).reshape((n, 3)) + spiral.data[:] += jitter + # shift array to provide a random-sampling effect + # without re-running a random generator on each iteration + # generating 1 million normally distributed points takes ~50ms even with SFC64 + jitter[1000:] = jitter[:-1000] + jitter[:1000] = jitter[-1000:] figure.add_animations(update) @@ -51,10 +69,16 @@ def update(): 'maintain_aspect': True, 'depth_range': None } + figure[0, 0].camera.set_state(camera_state) figure[0, 0].axes.visible = False +if fpl.IMGUI: + # show fps with imgui overlay + figure.imgui_show_fps = True + + # NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively # please see our docs for using fastplotlib interactively in ipython and jupyter if __name__ == "__main__": diff --git a/fastplotlib/layouts/_figure.py b/fastplotlib/layouts/_figure.py index e1822eb64..a1bae965e 100644 --- a/fastplotlib/layouts/_figure.py +++ b/fastplotlib/layouts/_figure.py @@ -48,6 +48,7 @@ def __init__( controllers: pygfx.Controller | Iterable[Iterable[pygfx.Controller]] = None, canvas: str | BaseRenderCanvas | pygfx.Texture = None, renderer: pygfx.WgpuRenderer = None, + canvas_kwargs: dict = None, size: tuple[int, int] = (500, 300), names: list | np.ndarray = None, ): @@ -111,6 +112,9 @@ def __init__( renderer: pygfx.Renderer, optional pygfx renderer instance + canvas_kwargs: dict, optional + kwargs to pass to the canvas + size: (int, int), optional starting size of canvas in absolute pixels, default (500, 300) @@ -163,8 +167,14 @@ def __init__( else: subplot_names = None + if canvas_kwargs is not None: + if size not in canvas_kwargs.keys(): + canvas_kwargs["size"] = size + else: + canvas_kwargs = {"size": size, "max_fps": 60.0, "vsync": True} + canvas, renderer = make_canvas_and_renderer( - canvas, renderer, canvas_kwargs={"size": size} + canvas, renderer, canvas_kwargs=canvas_kwargs ) canvas.add_event_handler(self._fpl_reset_layout, "resize") diff --git a/fastplotlib/layouts/_imgui_figure.py b/fastplotlib/layouts/_imgui_figure.py index f6d3da20f..40145fe50 100644 --- a/fastplotlib/layouts/_imgui_figure.py +++ b/fastplotlib/layouts/_imgui_figure.py @@ -6,13 +6,12 @@ import imgui_bundle from imgui_bundle import imgui, icons_fontawesome_6 as fa -from wgpu.utils.imgui import ImguiRenderer +from wgpu.utils.imgui import ImguiRenderer, Stats from rendercanvas import BaseRenderCanvas import pygfx from ._figure import Figure -from ._utils import make_canvas_and_renderer from ..ui import EdgeWindow, SubplotToolbar, StandardRightClickMenu, Popup, GUI_EDGES from ..ui import ColormapPicker @@ -21,8 +20,8 @@ class ImguiFigure(Figure): def __init__( self, shape: tuple[int, int] = (1, 1), - rects=None, - extents=None, + rects: list[tuple | np.ndarray] = None, + extents: list[tuple | np.ndarray] = None, cameras: ( Literal["2d", "3d"] | Iterable[Iterable[Literal["2d", "3d"]]] @@ -42,16 +41,12 @@ def __init__( controllers: pygfx.Controller | Iterable[Iterable[pygfx.Controller]] = None, canvas: str | BaseRenderCanvas | pygfx.Texture = None, renderer: pygfx.WgpuRenderer = None, + canvas_kwargs: dict = None, size: tuple[int, int] = (500, 300), names: list | np.ndarray = None, ): self._guis: dict[str, EdgeWindow] = {k: None for k in GUI_EDGES} - canvas, renderer = make_canvas_and_renderer( - canvas, renderer, canvas_kwargs={"size": size} - ) - self._imgui_renderer = ImguiRenderer(renderer.device, canvas) - super().__init__( shape=shape, rects=rects, @@ -62,10 +57,13 @@ def __init__( controllers=controllers, canvas=canvas, renderer=renderer, + canvas_kwargs=canvas_kwargs, size=size, names=names, ) + self._imgui_renderer = ImguiRenderer(self.renderer.device, self.canvas) + fronts_path = str( Path(imgui_bundle.__file__).parent.joinpath( "assets", "fonts", "Font_Awesome_6_Free-Solid-900.otf" @@ -97,6 +95,9 @@ def __init__( self._popups: dict[str, Popup] = {} + self.imgui_show_fps = False + self._stats = Stats(self.renderer.device, self.canvas) + self.register_popup(ColormapPicker) @property @@ -110,7 +111,11 @@ def imgui_renderer(self) -> ImguiRenderer: return self._imgui_renderer def _render(self, draw=False): - super()._render(draw) + if self.imgui_show_fps: + with self._stats: + super()._render(draw) + else: + super()._render(draw) self.imgui_renderer.render() diff --git a/fastplotlib/layouts/_utils.py b/fastplotlib/layouts/_utils.py index 866c26aa3..98a6268f1 100644 --- a/fastplotlib/layouts/_utils.py +++ b/fastplotlib/layouts/_utils.py @@ -31,12 +31,12 @@ def make_canvas_and_renderer( """ if canvas is None: - canvas = RenderCanvas(max_fps=60, **canvas_kwargs) + canvas = RenderCanvas(**canvas_kwargs) elif isinstance(canvas, str): import rendercanvas m = importlib.import_module("rendercanvas." + canvas) - canvas = m.RenderCanvas(max_fps=60, **canvas_kwargs) + canvas = m.RenderCanvas(**canvas_kwargs) elif not isinstance(canvas, (BaseRenderCanvas, Texture)): raise TypeError( f"canvas option must either be a valid BaseRenderCanvas implementation, a pygfx Texture" diff --git a/setup.py b/setup.py index 9834884aa..3fb5368d5 100644 --- a/setup.py +++ b/setup.py @@ -4,8 +4,8 @@ install_requires = [ "numpy>=1.23.0", - "pygfx>=0.7.0", - "wgpu>=0.18.1", + "pygfx>=0.8.0", + "wgpu>=0.20.0", "cmap>=0.1.3", ] From a057faad6c44b2b8e6721f45781845439b4e9f97 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Sun, 23 Mar 2025 15:08:25 -0400 Subject: [PATCH 164/185] update gov and CoC about LLM spam (#774) * Update CODE_OF_CONDUCT.md * Update GOVERNANCE.md * Update GOVERNANCE.md --- CODE_OF_CONDUCT.md | 1 + GOVERNANCE.md | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 65efc3352..0ae81f6f0 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -63,6 +63,7 @@ We strive to: - Excessive profanity. Please avoid swearwords; people differ greatly in their sensitivity to swearing. - Repeated harassment of others. In general, if someone asks you to stop, then stop. - Advocating for, or encouraging, any of the above behavior. + - LLM spam or inauthentic interaction that is completely generated by an LLM is discouraged. We welcome the use of LLMs as tools, but unsolicited LLM bot accounts for example are not encouraged. # Diversity statement diff --git a/GOVERNANCE.md b/GOVERNANCE.md index e7e4fc8f4..876757d40 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -103,6 +103,8 @@ Anyone (absolutely anyone, not just the leadership team members) who feels that ### Process +#### Usual process + 1. Contact the neutral moderator with a description of the conflict, max of 250 words. 2. Neutral moderator must schedule a vote within 15 days. If that is not possible then within the next 45 days. 3. The individual who has invoked the conflict vote can choose to present their case, or they may choose to let the neutral moderator represent them. @@ -110,6 +112,10 @@ Anyone (absolutely anyone, not just the leadership team members) who feels that 4. The maintainers vote on one of the actions from “Enforcement Guidelines”: https://www.contributor-covenant.org/version/2/1/code_of_conduct/. It is advised that the first offense leads to action (1) “Correction”. Repeated or serious offenses from the same individual/organization may lead to escalating levels of actions. Very bad behavior, as determined by the leadership team, can justify a first offense resulting in (3) “Temporary Ban” or (4) “Permanent Ban”. 5. The advisory committee members may advise on the actions, but the ultimate decision is voted on by the maintainers. +#### Bot accounts, LLM accounts, and spam + +Unsolicited bot accounts, inauthentic interaction that is completetely generated by an LLM, and LLM spam are against our Code of Conduct. Bot accounts with fully LLM generated comments, issues, pull requests, discussion posts, or any other unsolicited LLM generated content will be deleted by the maintainers without notice and the account will not be allowed to interact with the fastplotlib organization. + ## Transparency Governance decisions, meeting minutes, and voting outcomes are publicly documented and accessible. We aim for transparency to allow the broader community to understand and trust the governance process. From d97307b3ac35d20081d0e1623878555f1f5a1cef Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Wed, 26 Mar 2025 10:05:33 -0400 Subject: [PATCH 165/185] Update setup.py (#775) --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 3fb5368d5..3ca95de0f 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ install_requires = [ "numpy>=1.23.0", - "pygfx>=0.8.0", + "pygfx~=0.9.0", "wgpu>=0.20.0", "cmap>=0.1.3", ] From 88359d32be8a68892a7279db1b665750d74b8eae Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Thu, 27 Mar 2025 12:49:42 -0400 Subject: [PATCH 166/185] Update VERSION to 0.5.0 (#778) --- fastplotlib/VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastplotlib/VERSION b/fastplotlib/VERSION index 1d0ba9ea1..8f0916f76 100644 --- a/fastplotlib/VERSION +++ b/fastplotlib/VERSION @@ -1 +1 @@ -0.4.0 +0.5.0 From c42c9e344e9bc1df145b2cc38a0824417b940153 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Mon, 31 Mar 2025 05:39:26 +0200 Subject: [PATCH 167/185] Use pyproject.toml (#782) * Use pyproject.toml * forgot to safe * black * tweak to allow sed to do its thing * black that one file that ci complains about --- .github/workflows/ci.yml | 2 +- .github/workflows/docs-deploy.yml | 6 +- .github/workflows/screenshots.yml | 2 +- MANIFEST.in | 4 - fastplotlib/VERSION | 1 - fastplotlib/__init__.py | 5 +- fastplotlib/_version.py | 5 ++ fastplotlib/layouts/_graphic_methods_mixin.py | 24 +++--- pyproject.toml | 83 ++++++++++++++++++- setup.py | 76 ----------------- 10 files changed, 106 insertions(+), 102 deletions(-) delete mode 100644 MANIFEST.in delete mode 100644 fastplotlib/VERSION create mode 100644 fastplotlib/_version.py delete mode 100644 setup.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0274add7d..528b62772 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,7 +52,7 @@ jobs: run: | python -m pip install --upgrade pip setuptools # remove pygfx from install_requires, we install using pygfx@main - sed -i "/pygfx/d" ./setup.py + sed -i "/pygfx/d" ./pyproject.toml pip install git+https://github.com/pygfx/pygfx.git@main - name: Install fastplotlib run: | diff --git a/.github/workflows/docs-deploy.yml b/.github/workflows/docs-deploy.yml index a0cb54357..470e2e5a5 100644 --- a/.github/workflows/docs-deploy.yml +++ b/.github/workflows/docs-deploy.yml @@ -40,7 +40,7 @@ jobs: run: | python -m pip install --upgrade pip setuptools # remove pygfx from install_requires, we install using pygfx@main - sed -i "/pygfx/d" ./setup.py + sed -i "/pygfx/d" ./pyproject.toml pip install git+https://github.com/pygfx/pygfx.git@main pip install -e ".[docs,notebook,imgui]" - name: Show wgpu backend @@ -68,7 +68,7 @@ jobs: if: ${{ github.ref == 'refs/heads/main' }} # any push to main goes to fastplotlib.org/ver/dev run: echo "DOCS_VERSION_DIR=dev" >> "$GITHUB_ENV" - + # upload docs via SCP - name: Deploy docs uses: appleboy/scp-action@v0.1.7 @@ -90,7 +90,7 @@ jobs: with: message: | 📚 Docs preview built and uploaded! https://www.fastplotlib.org/ver/${{ env.DOCS_VERSION_DIR }} - + # upload docs via SCP - name: Deploy docs release if: ${{ github.ref_type == 'tag' }} diff --git a/.github/workflows/screenshots.yml b/.github/workflows/screenshots.yml index 0985fc179..cfaf419b8 100644 --- a/.github/workflows/screenshots.yml +++ b/.github/workflows/screenshots.yml @@ -36,7 +36,7 @@ jobs: run: | python -m pip install --upgrade pip setuptools # remove pygfx from install_requires, we install using pygfx@main - sed -i "/pygfx/d" ./setup.py + sed -i "/pygfx/d" ./pyproject.toml pip install git+https://github.com/pygfx/pygfx.git@main - name: Install fastplotlib run: | diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index b8debd28d..000000000 --- a/MANIFEST.in +++ /dev/null @@ -1,4 +0,0 @@ -recursive-include fastplotlib/utils/colormaps/ * -include fastplotlib/VERSION -recursive-include fastplotlib/assets/ * - diff --git a/fastplotlib/VERSION b/fastplotlib/VERSION deleted file mode 100644 index 8f0916f76..000000000 --- a/fastplotlib/VERSION +++ /dev/null @@ -1 +0,0 @@ -0.5.0 diff --git a/fastplotlib/__init__.py b/fastplotlib/__init__.py index 7eb9554e8..6d92efffa 100644 --- a/fastplotlib/__init__.py +++ b/fastplotlib/__init__.py @@ -1,5 +1,7 @@ from pathlib import Path +from ._version import __version__, version_info + # this must be the first import for auto-canvas detection from .utils import loop # noqa from .graphics import * @@ -20,9 +22,6 @@ from .utils import config, enumerate_adapters, select_adapter, print_wgpu_report -with open(Path(__file__).parent.joinpath("VERSION"), "r") as f: - __version__ = f.read().split("\n")[0] - if len(enumerate_adapters()) < 1: from warnings import warn diff --git a/fastplotlib/_version.py b/fastplotlib/_version.py new file mode 100644 index 000000000..172da7121 --- /dev/null +++ b/fastplotlib/_version.py @@ -0,0 +1,5 @@ +__version__ = "0.5.0" + +version_info = tuple( + int(i) if i.isnumeric() else i for i in __version__.split("+")[0].split(".") +) diff --git a/fastplotlib/layouts/_graphic_methods_mixin.py b/fastplotlib/layouts/_graphic_methods_mixin.py index a04b681f5..a753eec73 100644 --- a/fastplotlib/layouts/_graphic_methods_mixin.py +++ b/fastplotlib/layouts/_graphic_methods_mixin.py @@ -32,7 +32,7 @@ def add_image( interpolation: str = "nearest", cmap_interpolation: str = "linear", isolated_buffer: bool = True, - **kwargs + **kwargs, ) -> ImageGraphic: """ @@ -78,7 +78,7 @@ def add_image( interpolation, cmap_interpolation, isolated_buffer, - **kwargs + **kwargs, ) def add_line_collection( @@ -96,7 +96,7 @@ def add_line_collection( metadatas: Union[Sequence[Any], numpy.ndarray] = None, isolated_buffer: bool = True, kwargs_lines: list[dict] = None, - **kwargs + **kwargs, ) -> LineCollection: """ @@ -169,7 +169,7 @@ def add_line_collection( metadatas, isolated_buffer, kwargs_lines, - **kwargs + **kwargs, ) def add_line( @@ -183,7 +183,7 @@ def add_line( cmap_transform: Union[numpy.ndarray, Iterable] = None, isolated_buffer: bool = True, size_space: str = "screen", - **kwargs + **kwargs, ) -> LineGraphic: """ @@ -234,7 +234,7 @@ def add_line( cmap_transform, isolated_buffer, size_space, - **kwargs + **kwargs, ) def add_line_stack( @@ -253,7 +253,7 @@ def add_line_stack( separation: float = 10.0, separation_axis: str = "y", kwargs_lines: list[dict] = None, - **kwargs + **kwargs, ) -> LineStack: """ @@ -334,7 +334,7 @@ def add_line_stack( separation, separation_axis, kwargs_lines, - **kwargs + **kwargs, ) def add_scatter( @@ -349,7 +349,7 @@ def add_scatter( sizes: Union[float, numpy.ndarray, Iterable[float]] = 1, uniform_size: bool = False, size_space: str = "screen", - **kwargs + **kwargs, ) -> ScatterGraphic: """ @@ -409,7 +409,7 @@ def add_scatter( sizes, uniform_size, size_space, - **kwargs + **kwargs, ) def add_text( @@ -422,7 +422,7 @@ def add_text( screen_space: bool = True, offset: tuple[float] = (0, 0, 0), anchor: str = "middle-center", - **kwargs + **kwargs, ) -> TextGraphic: """ @@ -473,5 +473,5 @@ def add_text( screen_space, offset, anchor, - **kwargs + **kwargs, ) diff --git a/pyproject.toml b/pyproject.toml index 4d957aee3..846c9070b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,84 @@ +# ===== Project info + +[project] +dynamic = ["version"] +name = "fastplotlib" +description = "Next-gen fast plotting library running on WGPU using the Pygfx rendering engine " +readme = "README.md" +license = { file = "LICENSE" } +authors = [{ name = "Kushal Kolar" }, { name = "Caitlin Lewis" }] +keywords = [ + "visualization", + "science", + "interactive", + "pygfx", + "webgpu", + "wgpu", + "vulkan", + "gpu", +] +requires-python = ">= 3.10" +dependencies = [ + "numpy>=1.23.0", + "pygfx>=0.9.0", + "wgpu>=0.20.0", + "cmap>=0.1.3", + # (this comment keeps this list multiline in VSCode) +] + +[project.optional-dependencies] +docs = [ + "sphinx", + "sphinx-gallery", + "pydata-sphinx-theme", + "glfw", + "ipywidgets>=8.0.0,<9", + "sphinx-copybutton", + "sphinx-design", + "pandoc", + "imageio[ffmpeg]", + "matplotlib", + "scikit-learn", +] +notebook = [ + "jupyterlab", + "jupyter-rfb>=0.5.1", + "ipywidgets>=8.0.0,<9", + "sidecar", +] +tests = [ + "pytest", + "nbmake", + "black", + "scipy", + "imageio[ffmpeg]", + "scikit-learn", + "tqdm", +] +imgui = ["imgui-bundle"] +dev = ["fastplotlib[docs,notebook,tests,imgui]"] + +[project.urls] +Homepage = "https://www.fastplotlib.org/" +Documentation = "https://www.fastplotlib.org/" +Repository = "https://github.com/fastplotlib/fastplotlib" + +# ===== Building + [build-system] -requires = ["setuptools", "wheel"] +requires = ["flit_core >=3.2,<4"] +build-backend = "flit_core.buildapi" + +# ===== Tooling + +# [tool.ruff] +# line-length = 88 +# [tool.ruff.lint] +# select = ["F", "E", "W", "N", "B", "RUF", "TC"] +# ignore = [ +# "E501", # Line too long +# "E731", # Do not assign a `lambda` expression, use a `def` +# "B019", # Use of `functools.lru_cache` or `functools.cache` on methods can lead to memory leaks +# "RUF012", # Mutable class attributes should be annotated with `typing.ClassVar`" +# ] diff --git a/setup.py b/setup.py deleted file mode 100644 index 3ca95de0f..000000000 --- a/setup.py +++ /dev/null @@ -1,76 +0,0 @@ -from setuptools import setup, find_packages -from pathlib import Path - - -install_requires = [ - "numpy>=1.23.0", - "pygfx~=0.9.0", - "wgpu>=0.20.0", - "cmap>=0.1.3", -] - - -extras_require = { - "docs": [ - "sphinx", - "sphinx-gallery", - "pydata-sphinx-theme", - "glfw", - "ipywidgets>=8.0.0,<9", - "sphinx-copybutton", - "sphinx-design", - "pandoc", - "imageio[ffmpeg]", - "matplotlib", - "scikit-learn", - ], - "notebook": [ - "jupyterlab", - "jupyter-rfb>=0.5.1", - "ipywidgets>=8.0.0,<9", - "sidecar", - ], - "tests": [ - "pytest", - "nbmake", - "black", - "scipy", - "imageio[ffmpeg]", - "scikit-learn", - "tqdm", - ], - "imgui": ["imgui-bundle"], -} - - -with open(Path(__file__).parent.joinpath("README.md")) as f: - readme = f.read() - -with open(Path(__file__).parent.joinpath("fastplotlib", "VERSION"), "r") as f: - ver = f.read().split("\n")[0] - - -classifiers = [ - "Programming Language :: Python :: 3", - "Topic :: Scientific/Engineering :: Visualization", - "License :: OSI Approved :: Apache Software License", - "Intended Audience :: Science/Research", -] - - -setup( - name="fastplotlib", - version=ver, - long_description=readme, - long_description_content_type="text/markdown", - packages=find_packages(), - url="https://github.com/fastplotlib/fastplotlib", - license="Apache 2.0", - author="Kushal Kolar, Caitlin Lewis", - author_email="", - python_requires=">=3.10", - install_requires=install_requires, - extras_require=extras_require, - include_package_data=True, - description="A fast plotting library built using the pygfx render engine", -) From 8ef6581c474f79995b2462297b558e673982c106 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Tue, 1 Apr 2025 04:49:57 +0200 Subject: [PATCH 168/185] Use _version.py that includes details using git (#784) --- fastplotlib/_version.py | 108 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/fastplotlib/_version.py b/fastplotlib/_version.py index 172da7121..ddeeb3d84 100644 --- a/fastplotlib/_version.py +++ b/fastplotlib/_version.py @@ -1,5 +1,113 @@ +""" +Versioning: we use a hard-coded version number, because it's simple and always +works. For dev installs we add extra version info from Git. +""" + +import logging +import subprocess +from pathlib import Path + + +# This is the reference version number, to be bumped before each release. +# The build system detects this definition when building a distribution. __version__ = "0.5.0" +# Allow using nearly the same code in different projects +project_name = "fastplotlib" + + +logger = logging.getLogger(project_name.lower()) + +# Get whether this is a repo. If so, repo_dir is the path, otherwise repo_dir is None. +repo_dir = Path(__file__).parents[1] +repo_dir = repo_dir if repo_dir.joinpath(".git").is_dir() else None + + +def get_version(): + """Get the version string.""" + if repo_dir: + return get_extended_version() + else: + return __version__ + + +def get_extended_version(): + """Get an extended version string with information from git.""" + + release, post, labels = get_version_info_from_git() + + # Sample first 3 parts of __version__ + base_release = ".".join(__version__.split(".")[:3]) + + # Check release + if not release: + release = base_release + elif release != base_release: + logger.warning( + f"{project_name} version from git ({release}) and __version__ ({base_release}) don't match." + ) + + # Build the total version + version = release + if post and post != "0": + version += f".post{post}" + if labels: + version += "+" + ".".join(labels) + + return version + + +def get_version_info_from_git(): + """Get (release, post, labels) from Git. + + With `release` the version number from the latest tag, `post` the + number of commits since that tag, and `labels` a tuple with the + git-hash and optionally a dirty flag. + """ + + # Call out to Git + command = [ + "git", + "describe", + "--long", + "--always", + "--tags", + "--dirty", + "--first-parent", + ] + try: + p = subprocess.run(command, cwd=repo_dir, capture_output=True) + except Exception as e: + logger.warning(f"Could not get {project_name} version: {e}") + p = None + + # Parse the result into parts + if p is None: + parts = (None, None, "unknown") + else: + output = p.stdout.decode(errors="ignore") + if p.returncode: + stderr = p.stderr.decode(errors="ignore") + logger.warning( + f"Could not get {project_name} version.\n\nstdout: " + + output + + "\n\nstderr: " + + stderr + ) + parts = (None, None, "unknown") + else: + parts = output.strip().lstrip("v").split("-") + if len(parts) <= 2: + # No tags (and thus also no post). Only git hash and maybe 'dirty' + parts = (None, None, *parts) + + # Return unpacked parts + release, post, *labels = parts + return release, post, labels + + +__version__ = get_version() + version_info = tuple( int(i) if i.isnumeric() else i for i in __version__.split("+")[0].split(".") ) From be8e0c8f9a3304df8d84b7c3d9d6a2f8f73d8bc6 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Mon, 7 Apr 2025 13:33:58 -0400 Subject: [PATCH 169/185] more event docs, make some things public (#773) * more docs on events * add some events examples * make features public * make subplot public * update w.r.t. feature being public * update tables * update generate_api.py * update guide * rename: FeatureEvent -> PropertyEvent * expose Graphic * update legend * tests, rename: FeatureEvent -> PropertyEvent * edit * update * deleted: events_more.rst * Lingering _featuees * rename in api docs * rename * more examples * update, and key event example * black * type annotations for event instances in examples * update faq * add hover example * Update examples * add example * add drag example * update exmpales * fix get_nearest * update utils for docs * update doc * fix * black * update drag_points * update * fix * update * better LinearRegionSelector example, fix calc center * add moving label example * add to conf * add readme * update docs conf.py * add paint_image example * add controller examples * fix * ipywidget example, update guide * update iw rgb arg * add ipywidgets webp to guide * update linear_region_selectors_match_offsets.png after fix * forgot to add non imgui screenshot * update ipywidget examples * docstring * plot helpers API docs should work now * update switcher * update switcher * docstring * start turning event tables to list of dicts instead of hard-coded rst tables * update common features with table spec * spelling * add code for list of dicts -> rst table, not yet tested * needs to be writeable * replace == with npt.assert_almost_equal() * rename FeatureEvent -> GraphicFeatureEvent * GraphicFeatureEvent as top level import * black * graphic feature event tables * stupid * forgot to add things * remove space * black --- docs/source/_static/guide_ipywidgets.webp | Bin 0 -> 1778300 bytes docs/source/_static/switcher.json | 17 +- docs/source/api/graphic_features/Deleted.rst | 2 +- docs/source/api/graphic_features/FontSize.rst | 2 +- .../graphic_features/GraphicFeatureEvent.rst | 38 + .../source/api/graphic_features/ImageCmap.rst | 2 +- .../ImageCmapInterpolation.rst | 2 +- .../graphic_features/ImageInterpolation.rst | 2 +- .../source/api/graphic_features/ImageVmax.rst | 2 +- .../source/api/graphic_features/ImageVmin.rst | 2 +- .../LinearRegionSelectionFeature.rst | 2 +- .../LinearSelectionFeature.rst | 2 +- docs/source/api/graphic_features/Name.rst | 2 +- docs/source/api/graphic_features/Offset.rst | 2 +- .../graphic_features/PointsSizesFeature.rst | 2 +- .../RectangleSelectionFeature.rst | 2 +- docs/source/api/graphic_features/Rotation.rst | 2 +- .../source/api/graphic_features/SizeSpace.rst | 2 +- docs/source/api/graphic_features/TextData.rst | 2 +- .../api/graphic_features/TextFaceColor.rst | 2 +- .../api/graphic_features/TextOutlineColor.rst | 2 +- .../graphic_features/TextOutlineThickness.rst | 2 +- .../api/graphic_features/TextureArray.rst | 2 +- .../source/api/graphic_features/Thickness.rst | 2 +- .../api/graphic_features/UniformColor.rst | 2 +- .../api/graphic_features/UniformSize.rst | 2 +- .../api/graphic_features/VertexCmap.rst | 2 +- .../api/graphic_features/VertexColors.rst | 2 +- .../api/graphic_features/VertexPositions.rst | 2 +- docs/source/api/graphic_features/Visible.rst | 2 +- docs/source/api/graphic_features/index.rst | 1 + docs/source/api/graphics/index.rst | 2 +- docs/source/api/utils.rst | 4 + docs/source/conf.py | 4 + docs/source/generate_api.py | 130 ++- docs/source/user_guide/event_tables.rst | 1020 +++++++++++++++++ docs/source/user_guide/faq.rst | 2 + docs/source/user_guide/guide.rst | 336 +++--- docs/source/user_guide/index.rst | 1 + examples/controllers/README.rst | 2 + examples/controllers/specify_integers.py | 50 + examples/controllers/specify_names.py | 47 + examples/controllers/sync_all.py | 30 + examples/events/README.rst | 4 + examples/events/cmap_event.py | 75 ++ examples/events/drag_points.py | 99 ++ .../simple_event.py => events/image_click.py} | 28 +- examples/events/image_data_event.py | 56 + examples/events/key_events.py | 85 ++ examples/events/line_data_thickness_event.py | 79 ++ examples/events/lines_mouse_nearest.py | 62 + examples/events/paint_image.py | 71 ++ examples/events/scatter_click.py | 66 ++ examples/events/scatter_hover.py | 69 ++ examples/events/scatter_hover_transforms.py | 126 ++ .../image_widget/image_widget_single_video.py | 2 +- examples/ipywidgets/README.rst | 2 + .../ipywidgets/ipywidgets_modify_image.py | 69 ++ .../ipywidgets/ipywidgets_sliders_line.py | 91 ++ .../linear_region_selectors_match_offsets.png | 4 +- ...-linear_region_selectors_match_offsets.png | 4 +- .../selection_tools/linear_region_selector.py | 16 +- examples/tests/testutils.py | 3 +- examples/text/README.rst | 2 + examples/text/moving_label.py | 84 ++ fastplotlib/__init__.py | 1 + fastplotlib/graphics/__init__.py | 3 +- fastplotlib/graphics/_base.py | 20 +- fastplotlib/graphics/_positions_base.py | 4 +- .../{_features => features}/__init__.py | 3 +- .../graphics/{_features => features}/_base.py | 14 +- .../{_features => features}/_common.py | 87 +- .../{_features => features}/_image.py | 67 +- .../_positions_graphics.py | 161 ++- .../_selection_features.py | 121 +- .../graphics/{_features => features}/_text.py | 52 +- .../graphics/{_features => features}/utils.py | 0 fastplotlib/graphics/image.py | 11 +- fastplotlib/graphics/line.py | 20 +- fastplotlib/graphics/scatter.py | 18 +- .../graphics/selectors/_base_selector.py | 2 - fastplotlib/graphics/selectors/_linear.py | 4 +- .../graphics/selectors/_linear_region.py | 4 +- fastplotlib/graphics/selectors/_rectangle.py | 4 +- fastplotlib/graphics/text.py | 12 +- fastplotlib/layouts/__init__.py | 1 + fastplotlib/legends/legend.py | 6 +- fastplotlib/utils/_plot_helpers.py | 8 +- tests/events.py | 8 +- tests/test_colors_buffer_manager.py | 12 +- tests/test_common_features.py | 12 +- tests/test_image_graphic.py | 8 +- tests/test_positions_data_buffer_manager.py | 14 +- tests/test_positions_graphics.py | 6 +- tests/test_sizes_buffer_manager.py | 2 +- tests/test_text_graphic.py | 8 +- tests/test_texture_array.py | 2 +- 97 files changed, 3058 insertions(+), 472 deletions(-) create mode 100644 docs/source/_static/guide_ipywidgets.webp create mode 100644 docs/source/api/graphic_features/GraphicFeatureEvent.rst create mode 100644 docs/source/user_guide/event_tables.rst create mode 100644 examples/controllers/README.rst create mode 100644 examples/controllers/specify_integers.py create mode 100644 examples/controllers/specify_names.py create mode 100644 examples/controllers/sync_all.py create mode 100644 examples/events/README.rst create mode 100644 examples/events/cmap_event.py create mode 100644 examples/events/drag_points.py rename examples/{misc/simple_event.py => events/image_click.py} (58%) create mode 100644 examples/events/image_data_event.py create mode 100644 examples/events/key_events.py create mode 100644 examples/events/line_data_thickness_event.py create mode 100644 examples/events/lines_mouse_nearest.py create mode 100644 examples/events/paint_image.py create mode 100644 examples/events/scatter_click.py create mode 100644 examples/events/scatter_hover.py create mode 100644 examples/events/scatter_hover_transforms.py create mode 100644 examples/ipywidgets/README.rst create mode 100644 examples/ipywidgets/ipywidgets_modify_image.py create mode 100644 examples/ipywidgets/ipywidgets_sliders_line.py create mode 100644 examples/text/README.rst create mode 100644 examples/text/moving_label.py rename fastplotlib/graphics/{_features => features}/__init__.py (96%) rename fastplotlib/graphics/{_features => features}/_base.py (96%) rename fastplotlib/graphics/{_features => features}/_common.py (53%) rename fastplotlib/graphics/{_features => features}/_image.py (81%) rename fastplotlib/graphics/{_features => features}/_positions_graphics.py (75%) rename fastplotlib/graphics/{_features => features}/_selection_features.py (74%) rename fastplotlib/graphics/{_features => features}/_text.py (65%) rename fastplotlib/graphics/{_features => features}/utils.py (100%) diff --git a/docs/source/_static/guide_ipywidgets.webp b/docs/source/_static/guide_ipywidgets.webp new file mode 100644 index 0000000000000000000000000000000000000000..9a79633816b8d851524e151bd7779fa903edbf11 GIT binary patch literal 1778300 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
SqWY2P~~%n;e1&+s4HKS5_MNHPAm*Le5j&+|}@ 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%0Ap+uWr77bh(ro>{H@-ZoIowqJRt)0jPZxQX!Fpat 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

}?pQ*a6-1J2d1s0BnxE&`iA;oS=P{LG)|qw4 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 z

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%0Aia)SgjsQhHwB(q>-EDx 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 z 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ੋqmg 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
>AE}mti@8uDvw1ELuGALuchcT#cP+x*rZ$r_w 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

MOUju8=Rt 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
i)bxI|pghabX>H8K&vU=R~h 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%0Dsp5hAdtsaVt6xZ2yiSM= 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 XbP|mj&Z=a#UY+ffI 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 N4XY58D`>*O`Ck-=pZEX( literal 0 HcmV?d00001 diff --git a/docs/source/_static/switcher.json b/docs/source/_static/switcher.json index 67f723e2f..9f792b252 100644 --- a/docs/source/_static/switcher.json +++ b/docs/source/_static/switcher.json @@ -1,7 +1,22 @@ [ + { + "name": "release", + "version": "v0.4.0", + "url": "http://www.fastplotlib.org/" + }, { "name": "dev/main", "version": "dev", - "url": "http://www.fastplotlib.org/versions/dev" + "url": "http://www.fastplotlib.org/ver/dev" + }, + { + "name": "v0.3.0", + "version": "v0.3.0", + "url": "http://www.fastplotlib.org/ver/0.3.0" + }, + { + "name": "v0.4.0", + "version": "v0.4.0", + "url": "http://www.fastplotlib.org/ver/0.4.0" } ] diff --git a/docs/source/api/graphic_features/Deleted.rst b/docs/source/api/graphic_features/Deleted.rst index 09131c4a7..ffc704917 100644 --- a/docs/source/api/graphic_features/Deleted.rst +++ b/docs/source/api/graphic_features/Deleted.rst @@ -6,7 +6,7 @@ Deleted ======= Deleted ======= -.. currentmodule:: fastplotlib.graphics._features +.. currentmodule:: fastplotlib.graphics.features Constructor ~~~~~~~~~~~ diff --git a/docs/source/api/graphic_features/FontSize.rst b/docs/source/api/graphic_features/FontSize.rst index 4b8df9826..5e34c6038 100644 --- a/docs/source/api/graphic_features/FontSize.rst +++ b/docs/source/api/graphic_features/FontSize.rst @@ -6,7 +6,7 @@ FontSize ======== FontSize ======== -.. currentmodule:: fastplotlib.graphics._features +.. currentmodule:: fastplotlib.graphics.features Constructor ~~~~~~~~~~~ diff --git a/docs/source/api/graphic_features/GraphicFeatureEvent.rst b/docs/source/api/graphic_features/GraphicFeatureEvent.rst new file mode 100644 index 000000000..233462052 --- /dev/null +++ b/docs/source/api/graphic_features/GraphicFeatureEvent.rst @@ -0,0 +1,38 @@ +.. _api.GraphicFeatureEvent: + +GraphicFeatureEvent +******************* + +=================== +GraphicFeatureEvent +=================== +.. currentmodule:: fastplotlib.graphics.features + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: GraphicFeatureEvent_api + + GraphicFeatureEvent + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: GraphicFeatureEvent_api + + GraphicFeatureEvent.bubbles + GraphicFeatureEvent.cancelled + GraphicFeatureEvent.current_target + GraphicFeatureEvent.root + GraphicFeatureEvent.target + GraphicFeatureEvent.time_stamp + GraphicFeatureEvent.type + +Methods +~~~~~~~ +.. autosummary:: + :toctree: GraphicFeatureEvent_api + + GraphicFeatureEvent.cancel + GraphicFeatureEvent.stop_propagation + diff --git a/docs/source/api/graphic_features/ImageCmap.rst b/docs/source/api/graphic_features/ImageCmap.rst index 23d16a4a2..2c23a3406 100644 --- a/docs/source/api/graphic_features/ImageCmap.rst +++ b/docs/source/api/graphic_features/ImageCmap.rst @@ -6,7 +6,7 @@ ImageCmap ========= ImageCmap ========= -.. currentmodule:: fastplotlib.graphics._features +.. currentmodule:: fastplotlib.graphics.features Constructor ~~~~~~~~~~~ diff --git a/docs/source/api/graphic_features/ImageCmapInterpolation.rst b/docs/source/api/graphic_features/ImageCmapInterpolation.rst index 7e04ec788..0577f2d70 100644 --- a/docs/source/api/graphic_features/ImageCmapInterpolation.rst +++ b/docs/source/api/graphic_features/ImageCmapInterpolation.rst @@ -6,7 +6,7 @@ ImageCmapInterpolation ====================== ImageCmapInterpolation ====================== -.. currentmodule:: fastplotlib.graphics._features +.. currentmodule:: fastplotlib.graphics.features Constructor ~~~~~~~~~~~ diff --git a/docs/source/api/graphic_features/ImageInterpolation.rst b/docs/source/api/graphic_features/ImageInterpolation.rst index 866e76333..ebf69c279 100644 --- a/docs/source/api/graphic_features/ImageInterpolation.rst +++ b/docs/source/api/graphic_features/ImageInterpolation.rst @@ -6,7 +6,7 @@ ImageInterpolation ================== ImageInterpolation ================== -.. currentmodule:: fastplotlib.graphics._features +.. currentmodule:: fastplotlib.graphics.features Constructor ~~~~~~~~~~~ diff --git a/docs/source/api/graphic_features/ImageVmax.rst b/docs/source/api/graphic_features/ImageVmax.rst index b7dfe7e2d..aa8d6526a 100644 --- a/docs/source/api/graphic_features/ImageVmax.rst +++ b/docs/source/api/graphic_features/ImageVmax.rst @@ -6,7 +6,7 @@ ImageVmax ========= ImageVmax ========= -.. currentmodule:: fastplotlib.graphics._features +.. currentmodule:: fastplotlib.graphics.features Constructor ~~~~~~~~~~~ diff --git a/docs/source/api/graphic_features/ImageVmin.rst b/docs/source/api/graphic_features/ImageVmin.rst index 0d4634894..361cc5838 100644 --- a/docs/source/api/graphic_features/ImageVmin.rst +++ b/docs/source/api/graphic_features/ImageVmin.rst @@ -6,7 +6,7 @@ ImageVmin ========= ImageVmin ========= -.. currentmodule:: fastplotlib.graphics._features +.. currentmodule:: fastplotlib.graphics.features Constructor ~~~~~~~~~~~ diff --git a/docs/source/api/graphic_features/LinearRegionSelectionFeature.rst b/docs/source/api/graphic_features/LinearRegionSelectionFeature.rst index b8958c86b..9f06f2682 100644 --- a/docs/source/api/graphic_features/LinearRegionSelectionFeature.rst +++ b/docs/source/api/graphic_features/LinearRegionSelectionFeature.rst @@ -6,7 +6,7 @@ LinearRegionSelectionFeature ============================ LinearRegionSelectionFeature ============================ -.. currentmodule:: fastplotlib.graphics._features +.. currentmodule:: fastplotlib.graphics.features Constructor ~~~~~~~~~~~ diff --git a/docs/source/api/graphic_features/LinearSelectionFeature.rst b/docs/source/api/graphic_features/LinearSelectionFeature.rst index ad7b8645a..b9e71cd7b 100644 --- a/docs/source/api/graphic_features/LinearSelectionFeature.rst +++ b/docs/source/api/graphic_features/LinearSelectionFeature.rst @@ -6,7 +6,7 @@ LinearSelectionFeature ====================== LinearSelectionFeature ====================== -.. currentmodule:: fastplotlib.graphics._features +.. currentmodule:: fastplotlib.graphics.features Constructor ~~~~~~~~~~~ diff --git a/docs/source/api/graphic_features/Name.rst b/docs/source/api/graphic_features/Name.rst index 288fcfc22..f5a5235d8 100644 --- a/docs/source/api/graphic_features/Name.rst +++ b/docs/source/api/graphic_features/Name.rst @@ -6,7 +6,7 @@ Name ==== Name ==== -.. currentmodule:: fastplotlib.graphics._features +.. currentmodule:: fastplotlib.graphics.features Constructor ~~~~~~~~~~~ diff --git a/docs/source/api/graphic_features/Offset.rst b/docs/source/api/graphic_features/Offset.rst index 683aaf763..fdb2af66a 100644 --- a/docs/source/api/graphic_features/Offset.rst +++ b/docs/source/api/graphic_features/Offset.rst @@ -6,7 +6,7 @@ Offset ====== Offset ====== -.. currentmodule:: fastplotlib.graphics._features +.. currentmodule:: fastplotlib.graphics.features Constructor ~~~~~~~~~~~ diff --git a/docs/source/api/graphic_features/PointsSizesFeature.rst b/docs/source/api/graphic_features/PointsSizesFeature.rst index 3dcc4eeb2..f3f78b74b 100644 --- a/docs/source/api/graphic_features/PointsSizesFeature.rst +++ b/docs/source/api/graphic_features/PointsSizesFeature.rst @@ -6,7 +6,7 @@ PointsSizesFeature ================== PointsSizesFeature ================== -.. currentmodule:: fastplotlib.graphics._features +.. currentmodule:: fastplotlib.graphics.features Constructor ~~~~~~~~~~~ diff --git a/docs/source/api/graphic_features/RectangleSelectionFeature.rst b/docs/source/api/graphic_features/RectangleSelectionFeature.rst index d35752a24..cdfd1ad3f 100644 --- a/docs/source/api/graphic_features/RectangleSelectionFeature.rst +++ b/docs/source/api/graphic_features/RectangleSelectionFeature.rst @@ -6,7 +6,7 @@ RectangleSelectionFeature ========================= RectangleSelectionFeature ========================= -.. currentmodule:: fastplotlib.graphics._features +.. currentmodule:: fastplotlib.graphics.features Constructor ~~~~~~~~~~~ diff --git a/docs/source/api/graphic_features/Rotation.rst b/docs/source/api/graphic_features/Rotation.rst index f8963b0fd..b7729c7a4 100644 --- a/docs/source/api/graphic_features/Rotation.rst +++ b/docs/source/api/graphic_features/Rotation.rst @@ -6,7 +6,7 @@ Rotation ======== Rotation ======== -.. currentmodule:: fastplotlib.graphics._features +.. currentmodule:: fastplotlib.graphics.features Constructor ~~~~~~~~~~~ diff --git a/docs/source/api/graphic_features/SizeSpace.rst b/docs/source/api/graphic_features/SizeSpace.rst index 0bca1ecc8..e7c8e30be 100644 --- a/docs/source/api/graphic_features/SizeSpace.rst +++ b/docs/source/api/graphic_features/SizeSpace.rst @@ -6,7 +6,7 @@ SizeSpace ========= SizeSpace ========= -.. currentmodule:: fastplotlib.graphics._features +.. currentmodule:: fastplotlib.graphics.features Constructor ~~~~~~~~~~~ diff --git a/docs/source/api/graphic_features/TextData.rst b/docs/source/api/graphic_features/TextData.rst index 1c27b6e48..bf08b08d6 100644 --- a/docs/source/api/graphic_features/TextData.rst +++ b/docs/source/api/graphic_features/TextData.rst @@ -6,7 +6,7 @@ TextData ======== TextData ======== -.. currentmodule:: fastplotlib.graphics._features +.. currentmodule:: fastplotlib.graphics.features Constructor ~~~~~~~~~~~ diff --git a/docs/source/api/graphic_features/TextFaceColor.rst b/docs/source/api/graphic_features/TextFaceColor.rst index 5dae54192..5ab01b04b 100644 --- a/docs/source/api/graphic_features/TextFaceColor.rst +++ b/docs/source/api/graphic_features/TextFaceColor.rst @@ -6,7 +6,7 @@ TextFaceColor ============= TextFaceColor ============= -.. currentmodule:: fastplotlib.graphics._features +.. currentmodule:: fastplotlib.graphics.features Constructor ~~~~~~~~~~~ diff --git a/docs/source/api/graphic_features/TextOutlineColor.rst b/docs/source/api/graphic_features/TextOutlineColor.rst index f7831b0df..571261625 100644 --- a/docs/source/api/graphic_features/TextOutlineColor.rst +++ b/docs/source/api/graphic_features/TextOutlineColor.rst @@ -6,7 +6,7 @@ TextOutlineColor ================ TextOutlineColor ================ -.. currentmodule:: fastplotlib.graphics._features +.. currentmodule:: fastplotlib.graphics.features Constructor ~~~~~~~~~~~ diff --git a/docs/source/api/graphic_features/TextOutlineThickness.rst b/docs/source/api/graphic_features/TextOutlineThickness.rst index 75d485781..450ae54c9 100644 --- a/docs/source/api/graphic_features/TextOutlineThickness.rst +++ b/docs/source/api/graphic_features/TextOutlineThickness.rst @@ -6,7 +6,7 @@ TextOutlineThickness ==================== TextOutlineThickness ==================== -.. currentmodule:: fastplotlib.graphics._features +.. currentmodule:: fastplotlib.graphics.features Constructor ~~~~~~~~~~~ diff --git a/docs/source/api/graphic_features/TextureArray.rst b/docs/source/api/graphic_features/TextureArray.rst index 79707c453..73facc5bf 100644 --- a/docs/source/api/graphic_features/TextureArray.rst +++ b/docs/source/api/graphic_features/TextureArray.rst @@ -6,7 +6,7 @@ TextureArray ============ TextureArray ============ -.. currentmodule:: fastplotlib.graphics._features +.. currentmodule:: fastplotlib.graphics.features Constructor ~~~~~~~~~~~ diff --git a/docs/source/api/graphic_features/Thickness.rst b/docs/source/api/graphic_features/Thickness.rst index 061f96fe8..dc4c5888f 100644 --- a/docs/source/api/graphic_features/Thickness.rst +++ b/docs/source/api/graphic_features/Thickness.rst @@ -6,7 +6,7 @@ Thickness ========= Thickness ========= -.. currentmodule:: fastplotlib.graphics._features +.. currentmodule:: fastplotlib.graphics.features Constructor ~~~~~~~~~~~ diff --git a/docs/source/api/graphic_features/UniformColor.rst b/docs/source/api/graphic_features/UniformColor.rst index 7370589b7..8e9d56eae 100644 --- a/docs/source/api/graphic_features/UniformColor.rst +++ b/docs/source/api/graphic_features/UniformColor.rst @@ -6,7 +6,7 @@ UniformColor ============ UniformColor ============ -.. currentmodule:: fastplotlib.graphics._features +.. currentmodule:: fastplotlib.graphics.features Constructor ~~~~~~~~~~~ diff --git a/docs/source/api/graphic_features/UniformSize.rst b/docs/source/api/graphic_features/UniformSize.rst index e342d6a70..e4727dcb9 100644 --- a/docs/source/api/graphic_features/UniformSize.rst +++ b/docs/source/api/graphic_features/UniformSize.rst @@ -6,7 +6,7 @@ UniformSize =========== UniformSize =========== -.. currentmodule:: fastplotlib.graphics._features +.. currentmodule:: fastplotlib.graphics.features Constructor ~~~~~~~~~~~ diff --git a/docs/source/api/graphic_features/VertexCmap.rst b/docs/source/api/graphic_features/VertexCmap.rst index a3311d6e6..77d96aaf6 100644 --- a/docs/source/api/graphic_features/VertexCmap.rst +++ b/docs/source/api/graphic_features/VertexCmap.rst @@ -6,7 +6,7 @@ VertexCmap ========== VertexCmap ========== -.. currentmodule:: fastplotlib.graphics._features +.. currentmodule:: fastplotlib.graphics.features Constructor ~~~~~~~~~~~ diff --git a/docs/source/api/graphic_features/VertexColors.rst b/docs/source/api/graphic_features/VertexColors.rst index 3c2089a78..d09da7a18 100644 --- a/docs/source/api/graphic_features/VertexColors.rst +++ b/docs/source/api/graphic_features/VertexColors.rst @@ -6,7 +6,7 @@ VertexColors ============ VertexColors ============ -.. currentmodule:: fastplotlib.graphics._features +.. currentmodule:: fastplotlib.graphics.features Constructor ~~~~~~~~~~~ diff --git a/docs/source/api/graphic_features/VertexPositions.rst b/docs/source/api/graphic_features/VertexPositions.rst index 9669ab6d5..d181f07b9 100644 --- a/docs/source/api/graphic_features/VertexPositions.rst +++ b/docs/source/api/graphic_features/VertexPositions.rst @@ -6,7 +6,7 @@ VertexPositions =============== VertexPositions =============== -.. currentmodule:: fastplotlib.graphics._features +.. currentmodule:: fastplotlib.graphics.features Constructor ~~~~~~~~~~~ diff --git a/docs/source/api/graphic_features/Visible.rst b/docs/source/api/graphic_features/Visible.rst index 957b4433a..06bfd2278 100644 --- a/docs/source/api/graphic_features/Visible.rst +++ b/docs/source/api/graphic_features/Visible.rst @@ -6,7 +6,7 @@ Visible ======= Visible ======= -.. currentmodule:: fastplotlib.graphics._features +.. currentmodule:: fastplotlib.graphics.features Constructor ~~~~~~~~~~~ diff --git a/docs/source/api/graphic_features/index.rst b/docs/source/api/graphic_features/index.rst index dc88e97d6..90a58fe8e 100644 --- a/docs/source/api/graphic_features/index.rst +++ b/docs/source/api/graphic_features/index.rst @@ -31,3 +31,4 @@ Graphic Features Rotation Visible Deleted + GraphicFeatureEvent diff --git a/docs/source/api/graphics/index.rst b/docs/source/api/graphics/index.rst index b64ac53c0..a2addb7bf 100644 --- a/docs/source/api/graphics/index.rst +++ b/docs/source/api/graphics/index.rst @@ -5,8 +5,8 @@ Graphics :maxdepth: 1 LineGraphic - ImageGraphic ScatterGraphic + ImageGraphic TextGraphic LineCollection LineStack diff --git a/docs/source/api/utils.rst b/docs/source/api/utils.rst index 6222e22c6..be7b1a049 100644 --- a/docs/source/api/utils.rst +++ b/docs/source/api/utils.rst @@ -4,3 +4,7 @@ fastplotlib.utils .. currentmodule:: fastplotlib.utils .. automodule:: fastplotlib.utils.functions :members: + +.. currentmodule:: fastplotlib.utils +.. automodule:: fastplotlib.utils._plot_helpers + :members: diff --git a/docs/source/conf.py b/docs/source/conf.py index 865c462a6..8d17c97ae 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -60,12 +60,16 @@ "../../examples/image_widget", "../../examples/gridplot", "../../examples/window_layouts", + "../../examples/controllers", "../../examples/line", "../../examples/line_collection", "../../examples/scatter", + "../../examples/text", + "../../examples/events", "../../examples/selection_tools", "../../examples/machine_learning", "../../examples/guis", + "../../examples/ipywidgets", "../../examples/misc", "../../examples/qt", ] diff --git a/docs/source/generate_api.py b/docs/source/generate_api.py index 6887566cb..512826b5e 100644 --- a/docs/source/generate_api.py +++ b/docs/source/generate_api.py @@ -1,12 +1,14 @@ -from typing import * +from collections import defaultdict import inspect -from pathlib import Path +from io import StringIO import os +from pathlib import Path +from typing import * import fastplotlib -from fastplotlib.layouts._subplot import Subplot +from fastplotlib.layouts import Subplot from fastplotlib import graphics -from fastplotlib.graphics import _features, selectors +from fastplotlib.graphics import features, selectors from fastplotlib import widgets from fastplotlib import utils from fastplotlib import ui @@ -21,6 +23,7 @@ SELECTORS_DIR = API_DIR.joinpath("selectors") WIDGETS_DIR = API_DIR.joinpath("widgets") UI_DIR = API_DIR.joinpath("ui") +GUIDE_DIR = current_dir.joinpath("user_guide") doc_sources = [ API_DIR, @@ -56,16 +59,6 @@ "See the rendercanvas docs: https://rendercanvas.readthedocs.io/stable/api.html#rendercanvas.BaseLoop " ) -with open(API_DIR.joinpath("utils.rst"), "w") as f: - f.write( - "fastplotlib.utils\n" - "*****************\n\n" - - "..currentmodule:: fastplotlib.utils\n" - "..automodule:: fastplotlib.utils.functions\n" - " : members:\n" - ) - def get_public_members(cls) -> Tuple[List[str], List[str]]: """ @@ -139,12 +132,18 @@ def generate_class( return out -def generate_functions_module(module, name: str): +def generate_functions_module(module, name: str, generate_header: bool = True): underline = "*" * len(name) + if generate_header: + header = ( + f"{name}\n" + f"{underline}\n" + f"\n" + ) + else: + header = "\n" out = ( - f"{name}\n" - f"{underline}\n" - f"\n" + f"{header}" f".. currentmodule:: {name}\n" f".. automodule:: {module.__name__}\n" f" :members:\n" @@ -173,6 +172,60 @@ def generate_page( to_write = generate_class(cls, module) f.write(to_write) +####################################################### +# Used for GraphicFeature class event table +# copy-pasted from https://pablofernandez.tech/2019/03/21/turning-a-list-of-dicts-into-a-restructured-text-table/ + +def _generate_header(field_names, column_widths): + with StringIO() as output: + for field_name in field_names: + output.write(f"+-{'-' * column_widths[field_name]}-") + output.write("+\n") + for field_name in field_names: + output.write(f"| {field_name} {' ' * (column_widths[field_name] - len(field_name))}") + output.write("|\n") + for field_name in field_names: + output.write(f"+={'=' * column_widths[field_name]}=") + output.write("+\n") + return output.getvalue() + + +def _generate_row(row, field_names, column_widths): + with StringIO() as output: + for field_name in field_names: + output.write(f"| {row[field_name]}{' ' * (column_widths[field_name] - len(str(row[field_name])))} ") + output.write("|\n") + for field_name in field_names: + output.write(f"+-{'-' * column_widths[field_name]}-") + output.write("+\n") + return output.getvalue() + + +def _get_fields(data): + field_names = [] + column_widths = defaultdict(lambda: 0) + for row in data: + for field_name in row: + if field_name not in field_names: + field_names.append(field_name) + column_widths[field_name] = max(column_widths[field_name], len(field_name), len(str(row[field_name]))) + return field_names, column_widths + + +def dict_to_rst_table(data): + """convert a list of dicts to an RST table""" + field_names, column_widths = _get_fields(data) + with StringIO() as output: + output.write(_generate_header(field_names, column_widths)) + for row in data: + output.write(_generate_row(row, field_names, column_widths)) + + output.write("\n") + + return output.getvalue() + +####################################################### + def main(): generate_page( @@ -238,7 +291,7 @@ def main(): ) ############################################################################## - feature_classes = [getattr(_features, f) for f in _features.__all__] + feature_classes = [getattr(features, f) for f in features.__all__] feature_class_names = [f.__name__ for f in feature_classes] @@ -258,7 +311,7 @@ def main(): generate_page( page_name=feature_cls.__name__, classes=[feature_cls], - modules=["fastplotlib.graphics._features"], + modules=["fastplotlib.graphics.features"], source_path=GRAPHIC_FEATURES_DIR.joinpath(f"{feature_cls.__name__}.rst"), ) ############################################################################## @@ -340,11 +393,12 @@ def main(): ############################################################################## utils_str = generate_functions_module(utils.functions, "fastplotlib.utils") + utils_str += generate_functions_module(utils._plot_helpers, "fastplotlib.utils", generate_header=False) with open(API_DIR.joinpath("utils.rst"), "w") as f: f.write(utils_str) - # nake API index file + # make API index file with open(API_DIR.joinpath("index.rst"), "w") as f: f.write( "API Reference\n" @@ -362,5 +416,39 @@ def main(): " utils\n" ) + ############################################################################## + # graphic feature event tables + + def write_table(name, feature_cls): + s = f"{name}\n" + s += "^" * len(name) + "\n\n" + + if hasattr(feature_cls, "event_extra_attrs"): + s += "**extra attributes**\n\n" + s += dict_to_rst_table(feature_cls.event_extra_attrs) + + s += "**event info dict**\n\n" + s += dict_to_rst_table(feature_cls.event_info_spec) + + return s + + with open(GUIDE_DIR.joinpath("event_tables.rst"), "w") as f: + f.write(".. _event_tables:\n\n") + f.write("Event Tables\n") + f.write("============\n\n") + + for graphic_cls in [*graphic_classes, *selector_classes]: + f.write(f"{graphic_cls.__name__}\n") + f.write("-" * len(graphic_cls.__name__) + "\n\n") + for name, type_ in graphic_cls._features.items(): + if isinstance(type_, tuple): + for t in type_: + if t is None: + continue + f.write(write_table(name, t)) + else: + f.write(write_table(name, type_)) + + if __name__ == "__main__": main() diff --git a/docs/source/user_guide/event_tables.rst b/docs/source/user_guide/event_tables.rst new file mode 100644 index 000000000..1b9b2f7ec --- /dev/null +++ b/docs/source/user_guide/event_tables.rst @@ -0,0 +1,1020 @@ +.. _event_tables: + +Event Tables +============ + +LineGraphic +----------- + +data +^^^^ + +**event info dict** + ++----------+----------------------------------------------+--------------------------------------------------------+ +| dict key | type | description | ++==========+==============================================+========================================================+ +| key | slice, index (int) or numpy-like fancy index | key at which vertex positions data were indexed/sliced | ++----------+----------------------------------------------+--------------------------------------------------------+ +| value | int | float | array-like | new data values for points that were changed | ++----------+----------------------------------------------+--------------------------------------------------------+ + +colors +^^^^^^ + +**event info dict** + ++------------+--------------------------------------+------------------------------------------------------+ +| dict key | type | description | ++============+======================================+======================================================+ +| key | slice, index, numpy-like fancy index | index/slice at which colors were indexed/sliced | ++------------+--------------------------------------+------------------------------------------------------+ +| value | np.ndarray [n_points_changed, RGBA] | new color values for points that were changed | ++------------+--------------------------------------+------------------------------------------------------+ +| user_value | str or array-like | user input value that was parsed into the RGBA array | ++------------+--------------------------------------+------------------------------------------------------+ + +colors +^^^^^^ + +**event info dict** + ++----------+-------------------+-----------------+ +| dict key | type | description | ++==========+===================+=================+ +| value | np.ndarray [RGBA] | new color value | ++----------+-------------------+-----------------+ + +cmap +^^^^ + +**event info dict** + ++----------+-------+--------------------------------+ +| dict key | type | description | ++==========+=======+================================+ +| key | slice | key at cmap colors were sliced | ++----------+-------+--------------------------------+ +| value | str | new cmap to set at given slice | ++----------+-------+--------------------------------+ + +thickness +^^^^^^^^^ + +**event info dict** + ++----------+-------+---------------------+ +| dict key | type | description | ++==========+=======+=====================+ +| value | float | new thickness value | ++----------+-------+---------------------+ + +size_space +^^^^^^^^^^ + +**event info dict** + ++----------+------+------------------------------+ +| dict key | type | description | ++==========+======+==============================+ +| value | str | 'screen' | 'world' | 'model' | ++----------+------+------------------------------+ + +name +^^^^ + +**event info dict** + ++----------+------+--------------------+ +| dict key | type | description | ++==========+======+====================+ +| value | str | user provided name | ++----------+------+--------------------+ + +offset +^^^^^^ + +**event info dict** + ++----------+---------------------------------+----------------------+ +| dict key | type | description | ++==========+=================================+======================+ +| value | np.ndarray[float, float, float] | new offset (x, y, z) | ++----------+---------------------------------+----------------------+ + +rotation +^^^^^^^^ + +**event info dict** + ++----------+----------------------------------------+-------------------------+ +| dict key | type | description | ++==========+========================================+=========================+ +| value | np.ndarray[float, float, float, float] | new rotation quaternion | ++----------+----------------------------------------+-------------------------+ + +visible +^^^^^^^ + +**event info dict** + ++----------+------+---------------------+ +| dict key | type | description | ++==========+======+=====================+ +| value | bool | new visibility bool | ++----------+------+---------------------+ + +deleted +^^^^^^^ + +**event info dict** + ++----------+------+-------------------------------+ +| dict key | type | description | ++==========+======+===============================+ +| value | bool | True when graphic was deleted | ++----------+------+-------------------------------+ + +ScatterGraphic +-------------- + +data +^^^^ + +**event info dict** + ++----------+----------------------------------------------+--------------------------------------------------------+ +| dict key | type | description | ++==========+==============================================+========================================================+ +| key | slice, index (int) or numpy-like fancy index | key at which vertex positions data were indexed/sliced | ++----------+----------------------------------------------+--------------------------------------------------------+ +| value | int | float | array-like | new data values for points that were changed | ++----------+----------------------------------------------+--------------------------------------------------------+ + +sizes +^^^^^ + +**event info dict** + ++----------+----------------------------------------------+----------------------------------------------+ +| dict key | type | description | ++==========+==============================================+==============================================+ +| key | slice, index (int) or numpy-like fancy index | key at which point sizes were indexed/sliced | ++----------+----------------------------------------------+----------------------------------------------+ +| value | int | float | array-like | new size values for points that were changed | ++----------+----------------------------------------------+----------------------------------------------+ + +sizes +^^^^^ + +**event info dict** + ++----------+-------+----------------+ +| dict key | type | description | ++==========+=======+================+ +| value | float | new size value | ++----------+-------+----------------+ + +colors +^^^^^^ + +**event info dict** + ++------------+--------------------------------------+------------------------------------------------------+ +| dict key | type | description | ++============+======================================+======================================================+ +| key | slice, index, numpy-like fancy index | index/slice at which colors were indexed/sliced | ++------------+--------------------------------------+------------------------------------------------------+ +| value | np.ndarray [n_points_changed, RGBA] | new color values for points that were changed | ++------------+--------------------------------------+------------------------------------------------------+ +| user_value | str or array-like | user input value that was parsed into the RGBA array | ++------------+--------------------------------------+------------------------------------------------------+ + +colors +^^^^^^ + +**event info dict** + ++----------+-------------------+-----------------+ +| dict key | type | description | ++==========+===================+=================+ +| value | np.ndarray [RGBA] | new color value | ++----------+-------------------+-----------------+ + +cmap +^^^^ + +**event info dict** + ++----------+-------+--------------------------------+ +| dict key | type | description | ++==========+=======+================================+ +| key | slice | key at cmap colors were sliced | ++----------+-------+--------------------------------+ +| value | str | new cmap to set at given slice | ++----------+-------+--------------------------------+ + +size_space +^^^^^^^^^^ + +**event info dict** + ++----------+------+------------------------------+ +| dict key | type | description | ++==========+======+==============================+ +| value | str | 'screen' | 'world' | 'model' | ++----------+------+------------------------------+ + +name +^^^^ + +**event info dict** + ++----------+------+--------------------+ +| dict key | type | description | ++==========+======+====================+ +| value | str | user provided name | ++----------+------+--------------------+ + +offset +^^^^^^ + +**event info dict** + ++----------+---------------------------------+----------------------+ +| dict key | type | description | ++==========+=================================+======================+ +| value | np.ndarray[float, float, float] | new offset (x, y, z) | ++----------+---------------------------------+----------------------+ + +rotation +^^^^^^^^ + +**event info dict** + ++----------+----------------------------------------+-------------------------+ +| dict key | type | description | ++==========+========================================+=========================+ +| value | np.ndarray[float, float, float, float] | new rotation quaternion | ++----------+----------------------------------------+-------------------------+ + +visible +^^^^^^^ + +**event info dict** + ++----------+------+---------------------+ +| dict key | type | description | ++==========+======+=====================+ +| value | bool | new visibility bool | ++----------+------+---------------------+ + +deleted +^^^^^^^ + +**event info dict** + ++----------+------+-------------------------------+ +| dict key | type | description | ++==========+======+===============================+ +| value | bool | True when graphic was deleted | ++----------+------+-------------------------------+ + +ImageGraphic +------------ + +data +^^^^ + +**event info dict** + ++----------+--------------------------------------+--------------------------------------------------+ +| dict key | type | description | ++==========+======================================+==================================================+ +| key | slice, index, numpy-like fancy index | key at which image data was sliced/fancy indexed | ++----------+--------------------------------------+--------------------------------------------------+ +| value | np.ndarray | float | new data values | ++----------+--------------------------------------+--------------------------------------------------+ + +cmap +^^^^ + +**event info dict** + ++----------+------+---------------+ +| dict key | type | description | ++==========+======+===============+ +| value | str | new cmap name | ++----------+------+---------------+ + +vmin +^^^^ + +**event info dict** + ++----------+-------+----------------+ +| dict key | type | description | ++==========+=======+================+ +| value | float | new vmin value | ++----------+-------+----------------+ + +vmax +^^^^ + +**event info dict** + ++----------+-------+----------------+ +| dict key | type | description | ++==========+=======+================+ +| value | float | new vmax value | ++----------+-------+----------------+ + +interpolation +^^^^^^^^^^^^^ + +**event info dict** + ++----------+------+--------------------------------------------+ +| dict key | type | description | ++==========+======+============================================+ +| value | str | new interpolation method, nearest | linear | ++----------+------+--------------------------------------------+ + +cmap_interpolation +^^^^^^^^^^^^^^^^^^ + +**event info dict** + ++----------+------+------------------------------------------------+ +| dict key | type | description | ++==========+======+================================================+ +| value | str | new cmap interpolatio method, nearest | linear | ++----------+------+------------------------------------------------+ + +name +^^^^ + +**event info dict** + ++----------+------+--------------------+ +| dict key | type | description | ++==========+======+====================+ +| value | str | user provided name | ++----------+------+--------------------+ + +offset +^^^^^^ + +**event info dict** + ++----------+---------------------------------+----------------------+ +| dict key | type | description | ++==========+=================================+======================+ +| value | np.ndarray[float, float, float] | new offset (x, y, z) | ++----------+---------------------------------+----------------------+ + +rotation +^^^^^^^^ + +**event info dict** + ++----------+----------------------------------------+-------------------------+ +| dict key | type | description | ++==========+========================================+=========================+ +| value | np.ndarray[float, float, float, float] | new rotation quaternion | ++----------+----------------------------------------+-------------------------+ + +visible +^^^^^^^ + +**event info dict** + ++----------+------+---------------------+ +| dict key | type | description | ++==========+======+=====================+ +| value | bool | new visibility bool | ++----------+------+---------------------+ + +deleted +^^^^^^^ + +**event info dict** + ++----------+------+-------------------------------+ +| dict key | type | description | ++==========+======+===============================+ +| value | bool | True when graphic was deleted | ++----------+------+-------------------------------+ + +TextGraphic +----------- + +text +^^^^ + +**event info dict** + ++----------+------+---------------+ +| dict key | type | description | ++==========+======+===============+ +| value | str | new text data | ++----------+------+---------------+ + +font_size +^^^^^^^^^ + +**event info dict** + ++----------+-------------+---------------+ +| dict key | type | description | ++==========+=============+===============+ +| value | float | int | new font size | ++----------+-------------+---------------+ + +face_color +^^^^^^^^^^ + +**event info dict** + ++----------+------------------+----------------+ +| dict key | type | description | ++==========+==================+================+ +| value | str | np.ndarray | new text color | ++----------+------------------+----------------+ + +outline_color +^^^^^^^^^^^^^ + +**event info dict** + ++----------+------------------+-------------------+ +| dict key | type | description | ++==========+==================+===================+ +| value | str | np.ndarray | new outline color | ++----------+------------------+-------------------+ + +outline_thickness +^^^^^^^^^^^^^^^^^ + +**event info dict** + ++----------+-------+----------------------------+ +| dict key | type | description | ++==========+=======+============================+ +| value | float | new text outline thickness | ++----------+-------+----------------------------+ + +name +^^^^ + +**event info dict** + ++----------+------+--------------------+ +| dict key | type | description | ++==========+======+====================+ +| value | str | user provided name | ++----------+------+--------------------+ + +offset +^^^^^^ + +**event info dict** + ++----------+---------------------------------+----------------------+ +| dict key | type | description | ++==========+=================================+======================+ +| value | np.ndarray[float, float, float] | new offset (x, y, z) | ++----------+---------------------------------+----------------------+ + +rotation +^^^^^^^^ + +**event info dict** + ++----------+----------------------------------------+-------------------------+ +| dict key | type | description | ++==========+========================================+=========================+ +| value | np.ndarray[float, float, float, float] | new rotation quaternion | ++----------+----------------------------------------+-------------------------+ + +visible +^^^^^^^ + +**event info dict** + ++----------+------+---------------------+ +| dict key | type | description | ++==========+======+=====================+ +| value | bool | new visibility bool | ++----------+------+---------------------+ + +deleted +^^^^^^^ + +**event info dict** + ++----------+------+-------------------------------+ +| dict key | type | description | ++==========+======+===============================+ +| value | bool | True when graphic was deleted | ++----------+------+-------------------------------+ + +LineCollection +-------------- + +data +^^^^ + +**event info dict** + ++----------+----------------------------------------------+--------------------------------------------------------+ +| dict key | type | description | ++==========+==============================================+========================================================+ +| key | slice, index (int) or numpy-like fancy index | key at which vertex positions data were indexed/sliced | ++----------+----------------------------------------------+--------------------------------------------------------+ +| value | int | float | array-like | new data values for points that were changed | ++----------+----------------------------------------------+--------------------------------------------------------+ + +colors +^^^^^^ + +**event info dict** + ++------------+--------------------------------------+------------------------------------------------------+ +| dict key | type | description | ++============+======================================+======================================================+ +| key | slice, index, numpy-like fancy index | index/slice at which colors were indexed/sliced | ++------------+--------------------------------------+------------------------------------------------------+ +| value | np.ndarray [n_points_changed, RGBA] | new color values for points that were changed | ++------------+--------------------------------------+------------------------------------------------------+ +| user_value | str or array-like | user input value that was parsed into the RGBA array | ++------------+--------------------------------------+------------------------------------------------------+ + +colors +^^^^^^ + +**event info dict** + ++----------+-------------------+-----------------+ +| dict key | type | description | ++==========+===================+=================+ +| value | np.ndarray [RGBA] | new color value | ++----------+-------------------+-----------------+ + +cmap +^^^^ + +**event info dict** + ++----------+-------+--------------------------------+ +| dict key | type | description | ++==========+=======+================================+ +| key | slice | key at cmap colors were sliced | ++----------+-------+--------------------------------+ +| value | str | new cmap to set at given slice | ++----------+-------+--------------------------------+ + +thickness +^^^^^^^^^ + +**event info dict** + ++----------+-------+---------------------+ +| dict key | type | description | ++==========+=======+=====================+ +| value | float | new thickness value | ++----------+-------+---------------------+ + +size_space +^^^^^^^^^^ + +**event info dict** + ++----------+------+------------------------------+ +| dict key | type | description | ++==========+======+==============================+ +| value | str | 'screen' | 'world' | 'model' | ++----------+------+------------------------------+ + +name +^^^^ + +**event info dict** + ++----------+------+--------------------+ +| dict key | type | description | ++==========+======+====================+ +| value | str | user provided name | ++----------+------+--------------------+ + +offset +^^^^^^ + +**event info dict** + ++----------+---------------------------------+----------------------+ +| dict key | type | description | ++==========+=================================+======================+ +| value | np.ndarray[float, float, float] | new offset (x, y, z) | ++----------+---------------------------------+----------------------+ + +rotation +^^^^^^^^ + +**event info dict** + ++----------+----------------------------------------+-------------------------+ +| dict key | type | description | ++==========+========================================+=========================+ +| value | np.ndarray[float, float, float, float] | new rotation quaternion | ++----------+----------------------------------------+-------------------------+ + +visible +^^^^^^^ + +**event info dict** + ++----------+------+---------------------+ +| dict key | type | description | ++==========+======+=====================+ +| value | bool | new visibility bool | ++----------+------+---------------------+ + +deleted +^^^^^^^ + +**event info dict** + ++----------+------+-------------------------------+ +| dict key | type | description | ++==========+======+===============================+ +| value | bool | True when graphic was deleted | ++----------+------+-------------------------------+ + +LineStack +--------- + +data +^^^^ + +**event info dict** + ++----------+----------------------------------------------+--------------------------------------------------------+ +| dict key | type | description | ++==========+==============================================+========================================================+ +| key | slice, index (int) or numpy-like fancy index | key at which vertex positions data were indexed/sliced | ++----------+----------------------------------------------+--------------------------------------------------------+ +| value | int | float | array-like | new data values for points that were changed | ++----------+----------------------------------------------+--------------------------------------------------------+ + +colors +^^^^^^ + +**event info dict** + ++------------+--------------------------------------+------------------------------------------------------+ +| dict key | type | description | ++============+======================================+======================================================+ +| key | slice, index, numpy-like fancy index | index/slice at which colors were indexed/sliced | ++------------+--------------------------------------+------------------------------------------------------+ +| value | np.ndarray [n_points_changed, RGBA] | new color values for points that were changed | ++------------+--------------------------------------+------------------------------------------------------+ +| user_value | str or array-like | user input value that was parsed into the RGBA array | ++------------+--------------------------------------+------------------------------------------------------+ + +colors +^^^^^^ + +**event info dict** + ++----------+-------------------+-----------------+ +| dict key | type | description | ++==========+===================+=================+ +| value | np.ndarray [RGBA] | new color value | ++----------+-------------------+-----------------+ + +cmap +^^^^ + +**event info dict** + ++----------+-------+--------------------------------+ +| dict key | type | description | ++==========+=======+================================+ +| key | slice | key at cmap colors were sliced | ++----------+-------+--------------------------------+ +| value | str | new cmap to set at given slice | ++----------+-------+--------------------------------+ + +thickness +^^^^^^^^^ + +**event info dict** + ++----------+-------+---------------------+ +| dict key | type | description | ++==========+=======+=====================+ +| value | float | new thickness value | ++----------+-------+---------------------+ + +size_space +^^^^^^^^^^ + +**event info dict** + ++----------+------+------------------------------+ +| dict key | type | description | ++==========+======+==============================+ +| value | str | 'screen' | 'world' | 'model' | ++----------+------+------------------------------+ + +name +^^^^ + +**event info dict** + ++----------+------+--------------------+ +| dict key | type | description | ++==========+======+====================+ +| value | str | user provided name | ++----------+------+--------------------+ + +offset +^^^^^^ + +**event info dict** + ++----------+---------------------------------+----------------------+ +| dict key | type | description | ++==========+=================================+======================+ +| value | np.ndarray[float, float, float] | new offset (x, y, z) | ++----------+---------------------------------+----------------------+ + +rotation +^^^^^^^^ + +**event info dict** + ++----------+----------------------------------------+-------------------------+ +| dict key | type | description | ++==========+========================================+=========================+ +| value | np.ndarray[float, float, float, float] | new rotation quaternion | ++----------+----------------------------------------+-------------------------+ + +visible +^^^^^^^ + +**event info dict** + ++----------+------+---------------------+ +| dict key | type | description | ++==========+======+=====================+ +| value | bool | new visibility bool | ++----------+------+---------------------+ + +deleted +^^^^^^^ + +**event info dict** + ++----------+------+-------------------------------+ +| dict key | type | description | ++==========+======+===============================+ +| value | bool | True when graphic was deleted | ++----------+------+-------------------------------+ + +LinearSelector +-------------- + +selection +^^^^^^^^^ + +**extra attributes** + ++--------------------+----------+----------------------------------+ +| attribute | type | description | ++====================+==========+==================================+ +| get_selected_index | callable | returns index under the selector | ++--------------------+----------+----------------------------------+ + +**event info dict** + ++----------+-------+-------------------------------+ +| dict key | type | description | ++==========+=======+===============================+ +| value | float | new x or y value of selection | ++----------+-------+-------------------------------+ + +name +^^^^ + +**event info dict** + ++----------+------+--------------------+ +| dict key | type | description | ++==========+======+====================+ +| value | str | user provided name | ++----------+------+--------------------+ + +offset +^^^^^^ + +**event info dict** + ++----------+---------------------------------+----------------------+ +| dict key | type | description | ++==========+=================================+======================+ +| value | np.ndarray[float, float, float] | new offset (x, y, z) | ++----------+---------------------------------+----------------------+ + +rotation +^^^^^^^^ + +**event info dict** + ++----------+----------------------------------------+-------------------------+ +| dict key | type | description | ++==========+========================================+=========================+ +| value | np.ndarray[float, float, float, float] | new rotation quaternion | ++----------+----------------------------------------+-------------------------+ + +visible +^^^^^^^ + +**event info dict** + ++----------+------+---------------------+ +| dict key | type | description | ++==========+======+=====================+ +| value | bool | new visibility bool | ++----------+------+---------------------+ + +deleted +^^^^^^^ + +**event info dict** + ++----------+------+-------------------------------+ +| dict key | type | description | ++==========+======+===============================+ +| value | bool | True when graphic was deleted | ++----------+------+-------------------------------+ + +LinearRegionSelector +-------------------- + +selection +^^^^^^^^^ + +**extra attributes** + ++----------------------+----------+------------------------------------+ +| attribute | type | description | ++======================+==========+====================================+ +| get_selected_indices | callable | returns indices under the selector | ++----------------------+----------+------------------------------------+ +| get_selected_data | callable | returns data under the selector | ++----------------------+----------+------------------------------------+ + +**event info dict** + ++----------+------------+-----------------------------+ +| dict key | type | description | ++==========+============+=============================+ +| value | np.ndarray | new [min, max] of selection | ++----------+------------+-----------------------------+ + +name +^^^^ + +**event info dict** + ++----------+------+--------------------+ +| dict key | type | description | ++==========+======+====================+ +| value | str | user provided name | ++----------+------+--------------------+ + +offset +^^^^^^ + +**event info dict** + ++----------+---------------------------------+----------------------+ +| dict key | type | description | ++==========+=================================+======================+ +| value | np.ndarray[float, float, float] | new offset (x, y, z) | ++----------+---------------------------------+----------------------+ + +rotation +^^^^^^^^ + +**event info dict** + ++----------+----------------------------------------+-------------------------+ +| dict key | type | description | ++==========+========================================+=========================+ +| value | np.ndarray[float, float, float, float] | new rotation quaternion | ++----------+----------------------------------------+-------------------------+ + +visible +^^^^^^^ + +**event info dict** + ++----------+------+---------------------+ +| dict key | type | description | ++==========+======+=====================+ +| value | bool | new visibility bool | ++----------+------+---------------------+ + +deleted +^^^^^^^ + +**event info dict** + ++----------+------+-------------------------------+ +| dict key | type | description | ++==========+======+===============================+ +| value | bool | True when graphic was deleted | ++----------+------+-------------------------------+ + +RectangleSelector +----------------- + +selection +^^^^^^^^^ + +**extra attributes** + ++----------------------+----------+------------------------------------+ +| attribute | type | description | ++======================+==========+====================================+ +| get_selected_indices | callable | returns indices under the selector | ++----------------------+----------+------------------------------------+ +| get_selected_data | callable | returns data under the selector | ++----------------------+----------+------------------------------------+ + +**event info dict** + ++----------+------------+-------------------------------------------+ +| dict key | type | description | ++==========+============+===========================================+ +| value | np.ndarray | new [xmin, xmax, ymin, ymax] of selection | ++----------+------------+-------------------------------------------+ + +name +^^^^ + +**event info dict** + ++----------+------+--------------------+ +| dict key | type | description | ++==========+======+====================+ +| value | str | user provided name | ++----------+------+--------------------+ + +offset +^^^^^^ + +**event info dict** + ++----------+---------------------------------+----------------------+ +| dict key | type | description | ++==========+=================================+======================+ +| value | np.ndarray[float, float, float] | new offset (x, y, z) | ++----------+---------------------------------+----------------------+ + +rotation +^^^^^^^^ + +**event info dict** + ++----------+----------------------------------------+-------------------------+ +| dict key | type | description | ++==========+========================================+=========================+ +| value | np.ndarray[float, float, float, float] | new rotation quaternion | ++----------+----------------------------------------+-------------------------+ + +visible +^^^^^^^ + +**event info dict** + ++----------+------+---------------------+ +| dict key | type | description | ++==========+======+=====================+ +| value | bool | new visibility bool | ++----------+------+---------------------+ + +deleted +^^^^^^^ + +**event info dict** + ++----------+------+-------------------------------+ +| dict key | type | description | ++==========+======+===============================+ +| value | bool | True when graphic was deleted | ++----------+------+-------------------------------+ + diff --git a/docs/source/user_guide/faq.rst b/docs/source/user_guide/faq.rst index 0061a04d4..5985efae1 100644 --- a/docs/source/user_guide/faq.rst +++ b/docs/source/user_guide/faq.rst @@ -56,6 +56,8 @@ Should I use ``fastplotlib`` for making publication figures? While `fastplotlib` figures can be exported to PNG using ``figure.export()``, `fastplotlib` is not intended for creating *static* publication figures. There are many other libraries that are well-suited for this task. + The rendering engine pygfx has a starting point for an svg renderer, you may try and expand upon it: https://github.com/pygfx/pygfx/tree/main/pygfx/renderers/svg + How does ``fastplotlib`` handle data loading? --------------------------------------------- diff --git a/docs/source/user_guide/guide.rst b/docs/source/user_guide/guide.rst index d544c42a3..1bdb3377c 100644 --- a/docs/source/user_guide/guide.rst +++ b/docs/source/user_guide/guide.rst @@ -41,8 +41,8 @@ The fundamental goal of ``fastplotlib`` is to provide a high-level, expressive A make it easy and intuitive to produce interactive visualizations that are as performant and vibrant as a modern video game 😄 -How to use ``fastplotlib`` --------------------------- +``fastplotlib`` basics +---------------------- Before giving a detailed overview of the library, here is a minimal example:: @@ -71,16 +71,22 @@ This is just a simple example of how the ``fastplotlib`` API works to create a p However, we are just scratching the surface of what is possible with ``fastplotlib``. Next, let's take a look at the building blocks of ``fastplotlib`` and how they can be used to create more complex visualizations. +Aside from this user guide, the Examples Gallery is the best place to learn specific things in fastplotlib. +If you still need help don't hesitate to post an issue or discussion post! + Figure ------ -The starting point for creating any visualization in ``fastplotlib`` is a ``Figure`` object. This can be a single plot or a grid of subplots. +The starting point for creating any visualization in ``fastplotlib`` is a ``Figure`` object. This can be a single subplot or many subplots. The ``Figure`` object houses and takes care of the underlying rendering components such as the camera, controller, renderer, and canvas. Most users won't need to use these directly; however, the ability to directly interact with the rendering engine is still available if needed. -By default, if no ``shape`` argument is provided when creating a ``Figure``, there will be a single subplot. All subplots in a ``Figure`` can be accessed using -indexing (i.e. ``fig_object[i ,j]``). +By default, if no ``shape`` argument is provided when creating a ``Figure``, there will be a single ``Subplot``. + +If a shape argument is provided, all subplots in a ``Figure`` can be accessed by indexing (i.e. ``fig_object[i ,j]``). A "window layout" +with customizable subplot positions and sizes can also be set by providing a ``rects`` or ``extents`` argument. The Examples Gallery +has a few examples that show how to create a "Window Layout". After defining a ``Figure``, we can begin to add ``Graphic`` objects. @@ -99,18 +105,22 @@ to be easily accessed from figures:: add image graphic image_graphic = fig[0, 0].add_image(data=data, name="astronaut") - # show plot + # show figure fig.show() - # index plot to get graphic + # index subplot to get graphic fig[0, 0]["astronaut"] + # another way to index graphics in a subplot + fig[0, 0].graphics[0] is fig[0, 0]["astronaut"] # will return `True` + .. See the examples gallery for examples on how to create and interactive with all the various types of graphics. -Graphics also have mutable properties that can be linked to events. Some of these properties, such as the ``data`` or ``colors`` of a line can even be indexed, -allowing for the creation of very powerful visualizations. +Graphics also have mutable properties. Some of these properties, such as the ``data`` or ``colors`` of a line can even be sliced, +allowing for the creation of very powerful visualizations. Event handlers can be added to a graphic to capture changes to +any of these properties. (1) Common properties that all graphics have @@ -132,17 +142,17 @@ allowing for the creation of very powerful visualizations. (a) ``ImageGraphic`` - +------------------------+------------------------------------+ - | Feature Name | Description | - +========================+====================================+ - | data | Underlying image data | - +------------------------+------------------------------------+ - | vmin | Lower contrast limit of an image | - +------------------------+------------------------------------+ - | vmax | Upper contrast limit of an image | - +------------------------+------------------------------------+ - | cmap | Colormap of an image | - +------------------------+------------------------------------+ + +------------------------+---------------------------------------------------+ + | Feature Name | Description | + +========================+===================================================+ + | data | Underlying image data | + +------------------------+---------------------------------------------------+ + | vmin | Lower contrast limit of an image | + +------------------------+---------------------------------------------------+ + | vmax | Upper contrast limit of an image | + +------------------------+---------------------------------------------------+ + | cmap | Colormap for a grayscale image, ignored if RGB(A) | + +------------------------+---------------------------------------------------+ (b) ``LineGraphic``, ``LineCollection``, ``LineStack`` @@ -244,14 +254,13 @@ your data, you are able to select an entire region. See the examples gallery for more in-depth examples with selector tools. Now we have the basics of creating a ``Figure``, adding ``Graphics`` to a ``Figure``, and working with ``Graphic`` properties to dynamically change or alter them. -Let's take a look at how we can define events to link ``Graphics`` and their properties together. Events ------ -Events can be a multitude of things: traditional events such as mouse or keyboard events, or events related to ``Graphic`` properties. +Events can be a multitude of things: canvas events such as mouse or keyboard events, or events related to ``Graphic`` properties. -There are two ways to add events in ``fastplotlib``. +There are two ways to add events to a graphic: 1) Use the method `add_event_handler()` :: @@ -272,24 +281,24 @@ There are two ways to add events in ``fastplotlib``. .. -The ``event_handler`` is a user-defined function that accepts an event instance as the first and only positional argument. +The ``event_handler`` is a user-defined callback function that accepts an event instance as the first and only positional argument. Information about the structure of event instances are described below. The ``"event_type"`` -is a string that identifies the type of event; this can be either a ``pygfx.Event`` or a ``Graphic`` property event. +is a string that identifies the type of event. ``graphic.supported_events`` will return a tuple of all ``event_type`` strings that this graphic supports. When an event occurs, the user-defined event handler will receive an event object. Depending on the type of event, the -event object will have relevant information that can be used in the callback. See below for event tables. +event object will have relevant information that can be used in the callback. See the next section for details. Graphic property events ^^^^^^^^^^^^^^^^^^^^^^^ -All ``Graphic`` events have the following attributes: +All ``Graphic`` events are instances of ``fastplotlib.GraphicFeatureEvent`` and have the following attributes: +------------+-------------+-----------------------------------------------+ | attribute | type | description | +============+=============+===============================================+ - | type | str | "colors" - name of the event | + | type | str | name of the event type | +------------+-------------+-----------------------------------------------+ | graphic | Graphic | graphic instance that the event is from | +------------+-------------+-----------------------------------------------+ @@ -300,144 +309,80 @@ All ``Graphic`` events have the following attributes: | time_stamp | float | time when the event occurred, in ms | +------------+-------------+-----------------------------------------------+ -The ``info`` attribute will house additional information for different ``Graphic`` property events: - -event_type: "colors" - - Vertex Colors - - **info dict** - - +------------+-----------------------------------------------------------+----------------------------------------------------------------------------------+ - | dict key | value type | value description | - +============+===========================================================+==================================================================================+ - | key | int | slice | np.ndarray[int | bool] | tuple[slice, ...] | key at which colors were indexed/sliced | - +------------+-----------------------------------------------------------+----------------------------------------------------------------------------------+ - | value | np.ndarray | new color values for points that were changed, shape is [n_points_changed, RGBA] | - +------------+-----------------------------------------------------------+----------------------------------------------------------------------------------+ - | user_value | str | np.ndarray | tuple[float] | list[float] | list[str] | user input value that was parsed into the RGBA array | - +------------+-----------------------------------------------------------+----------------------------------------------------------------------------------+ - - Uniform Colors - - **info dict** +Selectors have one event called ``selection`` which has extra attributes in addition to those listed in the table above. +The selection event section covers these. - +------------+-----------------------------------------------------------+----------------------------------------------------------------------------------+ - | dict key | value type | value description | - +============+===========================================================+==================================================================================+ - | value | np.ndarray | new color values for points that were changed, shape is [n_points_changed, RGBA] | - +------------+-----------------------------------------------------------+----------------------------------------------------------------------------------+ +The ``info`` attribute for most graphic property events will have one key, ``"value"``, which is the new value +of the graphic property. Events for graphic properties that represent arrays, such the ``data`` properties for +images, lines, and scatters will contain more entries. Here are a list of all graphic properties that have such +additional entries: -event_type: "sizes" +* ``ImageGraphic`` + * data - **info dict** +* ``LineGraphic`` + * data, colors, cmap - +----------+----------------------------------------------------------+------------------------------------------------------------------------------------------+ - | dict key | value type | value description | - +==========+==========================================================+==========================================================================================+ - | key | int | slice | np.ndarray[int | bool] | tuple[slice, ...] | key at which vertex positions data were indexed/sliced | - +----------+----------------------------------------------------------+------------------------------------------------------------------------------------------+ - | value | np.ndarray | float | list[float] | new data values for points that were changed, shape depends on the indices that were set | - +----------+----------------------------------------------------------+------------------------------------------------------------------------------------------+ +* ``ScatterGraphic`` + * data, colors, cmap, sizes -event_type: "data" +You can understand an event's attributes by adding a simple event handler:: - **info dict** - - +----------+----------------------------------------------------------+------------------------------------------------------------------------------------------+ - | dict key | value type | value description | - +==========+==========================================================+==========================================================================================+ - | key | int | slice | np.ndarray[int | bool] | tuple[slice, ...] | key at which vertex positions data were indexed/sliced | - +----------+----------------------------------------------------------+------------------------------------------------------------------------------------------+ - | value | np.ndarray | float | list[float] | new data values for points that were changed, shape depends on the indices that were set | - +----------+----------------------------------------------------------+------------------------------------------------------------------------------------------+ - -event_type: "thickness" - - **info dict** - - +------------+-----------------------------------------------------------+----------------------------------------------------------------------------------+ - | dict key | value type | value description | - +============+===========================================================+==================================================================================+ - | value | float | new thickness value | - +------------+-----------------------------------------------------------+----------------------------------------------------------------------------------+ - -event_type: "cmap" - - **info dict** - - +------------+-----------------------------------------------------------+----------------------------------------------------------------------------------+ - | dict key | value type | value description | - +============+===========================================================+==================================================================================+ - | value | string | new colormap value | - +------------+-----------------------------------------------------------+----------------------------------------------------------------------------------+ + @graphic.add_event_handler("event_type") + def handler(ev): + print(ev.type) + print(ev.graphic) + print(ev.info) -event_type: "selection" + # trigger the event + graphic.event_type = - ``LinearSelector`` + # direct example + @image_graphic.add_event_handler("cmap") + def cmap_changed(ev): + print(ev.type) + print(ev.info) - **additional event attributes:** + image_graphic.cmap = "viridis" + # this will trigger the cmap event and print the following: + # 'cmap' + # {"value": "viridis"} - +--------------------+----------+------------------------------------+ - | attribute | type | description | - +====================+==========+====================================+ - | get_selected_index | callable | returns indices under the selector | - +--------------------+----------+------------------------------------+ +.. - **info dict:** +The :ref:`event_tables` provide a description of the event info dicts for all Graphic Feature Events. - +----------+------------+-------------------------------+ - | dict key | value type | value description | - +==========+============+===============================+ - | value | np.ndarray | new x or y value of selection | - +----------+------------+-------------------------------+ +Selection event +~~~~~~~~~~~~~~~ - ``LinearRegionSelector`` +The ``selection`` event for selectors has additional attributes, mostly ``callable`` methods, that aid in using the +selector tool, such as getting the indices or data under the selection. The ``info`` dict will contain one entry ``value`` +which is the new selection value. - **additional event attributes:** +The :ref:`event_tables` provide a description of the additional attributes as well as the event info dicts for selector events. - +----------------------+----------+------------------------------------+ - | attribute | type | description | - +======================+==========+====================================+ - | get_selected_indices | callable | returns indices under the selector | - +----------------------+----------+------------------------------------+ - | get_selected_data | callable | returns data under the selector | - +----------------------+----------+------------------------------------+ +Canvas Events +^^^^^^^^^^^^^ - **info dict:** +Canvas events can be added to a graphic or to a Figure (see next section). +Here is a description of all canvas events and their attributes. - +----------+------------+-----------------------------+ - | dict key | value type | value description | - +==========+============+=============================+ - | value | np.ndarray | new [min, max] of selection | - +----------+------------+-----------------------------+ +The examples gallery provides several examples using pointer and key events. -Rendering engine events from a Graphic -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Pointer events +~~~~~~~~~~~~~~ -Rendering engine event handlers can be added to a graphic or to a Figure (see next section). -Here is a description of all rendering engine events and their attributes. +**List of pointer events:** * **pointer_down**: emitted when the user interacts with mouse, - touch or other pointer devices, by pressing it down. - - * *x*: horizontal position of the pointer within the widget. - * *y*: vertical position of the pointer within the widget. - * *button*: the button to which this event applies. See "Mouse buttons" section below for details. - * *buttons*: a tuple of buttons being pressed down. - * *modifiers*: a tuple of modifier keys being pressed down. See section below for details. - * *ntouches*: the number of simultaneous pointers being down. - * *touches*: a dict with int keys (pointer id's), and values that are dicts - that contain "x", "y", and "pressure". - * *time_stamp*: a timestamp in seconds. * **pointer_up**: emitted when the user releases a pointer. - This event has the same keys as the pointer down event. * **pointer_move**: emitted when the user moves a pointer. - This event has the same keys as the pointer down event. This event is throttled. +* **click**: emmitted when a mouse button is clicked. + * **double_click**: emitted on a double-click. This event looks like a pointer event, but without the touches. @@ -465,25 +410,19 @@ Here is a description of all rendering engine events and their attributes. * *modifiers*: a tuple of modifier keys being pressed down. * *time_stamp*: a timestamp in seconds. -* **key_down**: emitted when a key is pressed down. - - * *key*: the key being pressed as a string. See section below for details. - * *modifiers*: a tuple of modifier keys being pressed down. - * *time_stamp*: a timestamp in seconds. - -* **key_up**: emitted when a key is released. - This event has the same keys as the key down event. - +All pointer events have the following attributes: -Time stamps -~~~~~~~~~~~ +* *x*: horizontal position of the pointer within the widget. +* *y*: vertical position of the pointer within the widget. +* *button*: the button to which this event applies. See "Mouse buttons" section below for details. +* *buttons*: a tuple of buttons being pressed down (see below) +* *modifiers*: a tuple of modifier keys being pressed down. See section below for details. +* *ntouches*: the number of simultaneous pointers being down. +* *touches*: a dict with int keys (pointer id's), and values that are dicts + that contain "x", "y", and "pressure". +* *time_stamp*: a timestamp in seconds. -Since the time origin of ``time_stamp`` values is undefined, -time stamp values only make sense in relation to other time stamps. - - -Mouse buttons -~~~~~~~~~~~~~ +**Mouse buttons:** * 0: No button. * 1: Left button. @@ -491,9 +430,20 @@ Mouse buttons * 3: Middle button * 4-9: etc. +Key events +~~~~~~~~~~ + +**List of key (keyboard keys) events:** + +* **key_down**: emitted when a key is pressed down. + +* **key_up**: emitted when a key is released. + +Key events have the following attributes: -Keys -~~~~ +* *key*: the key being pressed as a string. See section below for details. +* *modifiers*: a tuple of modifier keys being pressed down. +* *time_stamp*: a timestamp in seconds. The key names follow the `browser spec `_. @@ -504,13 +454,18 @@ The key names follow the `browser spec `_ library is great for rapidly building UIs for prototyping +in jupyter. It is particularly useful for scientific and engineering applications since we can rapidly create a UI to +interact with our ``fastplotlib`` visualization. The main downside is that it only works in jupyter. + +.. image:: ../_static/guide_ipywidgets.webp + +For examples please see the examples gallery. + +Qt +^^ + +Qt is a very popular UI library written in C++, ``PyQt6`` and ``PySide6`` provide python bindings. There are countless +tutorials on how to build a UI using Qt which you can easily find if you google ``PyQt``. You can embed a ``Figure`` as +a Qt widget within a Qt application. + +For examples please see the examples gallery. + +imgui +^^^^^ + +`Imgui `_ is also a very popular library used for building UIs. The difference +between imgui and ipywidgets, Qt, and wx is the imgui UI can be rendered directly on the same canvas as a fastplotlib +``Figure``. This is hugely advantageous, it means that you can write an imgui UI and it will run on any GUI backend, +i.e. it will work in jupyter, Qt, glfw and wx windows! The programming model is different from Qt and ipywidgets, there +are no callbacks, but it is easy to learn if you see a few examples. + +We specifically use `imgui-bundle `_ for the python bindings in fastplotlib. +There is large community and many resources out there on building UIs using imgui. + +For examples on integrating imgui with a fastplotlib Figure please see the examples gallery. + +**Some tips:** + +The ``imgui-bundle`` docs as of March 2025 don't have a nice API list (as far as I know), here is how we go about developing UIs with imgui: + +1. Use the ``pyimgui`` API docs to locate the type of UI element we want, for example if we want a ``slider_int``: https://pyimgui.readthedocs.io/en/latest/reference/imgui.core.html#imgui.core.slider_int + +2. Look at the function signature in the ``imgui-bundle`` sources. You can usually access this easily with your IDE: https://github.com/pthom/imgui_bundle/blob/a5e7d46555832c40e9be277d4747eac5a303dbfc/bindings/imgui_bundle/imgui/__init__.pyi#L1693-L1696 + +3. ``pyimgui`` and ``imgui-bundle`` sometimes don't have the same function signature, so we use a combination of the pyimgui docs and +imgui-bundle function signature to understand and implement the UI element. + ImageWidget ----------- @@ -572,12 +579,9 @@ Let's look at an example: :: movie = iio.imread("imageio:cockatoo.mp4") - # convert RGB movie to grayscale - gray_movie = np.dot(movie[..., :3], [0.299, 0.587, 0.114]) - iw_movie = ImageWidget( - data=gray_movie, - cmap="gray" + data=movie, + rgb=True ) iw_movie.show() diff --git a/docs/source/user_guide/index.rst b/docs/source/user_guide/index.rst index 59189be22..92f0da98c 100644 --- a/docs/source/user_guide/index.rst +++ b/docs/source/user_guide/index.rst @@ -6,5 +6,6 @@ User Guide :maxdepth: 2 guide + event_tables gpu faq diff --git a/examples/controllers/README.rst b/examples/controllers/README.rst new file mode 100644 index 000000000..824087ce3 --- /dev/null +++ b/examples/controllers/README.rst @@ -0,0 +1,2 @@ +Controller examples +=================== diff --git a/examples/controllers/specify_integers.py b/examples/controllers/specify_integers.py new file mode 100644 index 000000000..128f240ad --- /dev/null +++ b/examples/controllers/specify_integers.py @@ -0,0 +1,50 @@ +""" +Specify IDs with integers +========================= + +Specify controllers to sync subplots using integer IDs +""" + +# test_example = false +# sphinx_gallery_pygfx_docs = 'screenshot' + +import numpy as np +import fastplotlib as fpl + + +xs = np.linspace(0, 2 * np.pi, 100) +sine = np.sin(xs) +cosine = np.cos(xs) + +# controller IDs +# one controller is created for each unique ID +# if the IDs are the same, those subplots will be synced +ids = [ + [0, 1], + [2, 0], +] + +names = [f"contr. id: {i}" for i in np.asarray(ids).ravel()] + +figure = fpl.Figure( + shape=(2, 2), + controller_ids=ids, + names=names, + size=(700, 560), +) + +figure[0, 0].add_line(np.column_stack([xs, sine])) + +figure[0, 1].add_line(np.random.rand(100)) +figure[1, 0].add_line(np.random.rand(100)) + +figure[1, 1].add_line(np.column_stack([xs, cosine])) + +figure.show(maintain_aspect=False) + + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.loop.run() diff --git a/examples/controllers/specify_names.py b/examples/controllers/specify_names.py new file mode 100644 index 000000000..fb0539c4a --- /dev/null +++ b/examples/controllers/specify_names.py @@ -0,0 +1,47 @@ +""" +Specify IDs with subplot names +============================== + +Provide a list of tuples where each tuple has subplot names. The same controller will be used for the subplots +indicated by each of these tuples +""" + +# test_example = false +# sphinx_gallery_pygfx_docs = 'screenshot' + +import numpy as np +import fastplotlib as fpl + + +xs = np.linspace(0, 2 * np.pi, 100) +ys = np.sin(xs) + +# create some subplots names +names = ["subplot_0", "subplot_1", "subplot_2", "subplot_3", "subplot_4", "subplot_5"] + +# list of tuples of subplot names +# subplots within each tuple will use the same controller. +ids = [ + ("subplot_0", "subplot_3"), + ("subplot_1", "subplot_2", "subplot_4"), +] + + +figure = fpl.Figure( + shape=(2, 3), + controller_ids=ids, + names=names, + size=(700, 560), +) + +for subplot in figure: + subplot.add_line(np.column_stack([xs, ys + np.random.normal(scale=0.1, size=100)])) + +figure.show(maintain_aspect=False) + + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.loop.run() diff --git a/examples/controllers/sync_all.py b/examples/controllers/sync_all.py new file mode 100644 index 000000000..0683a8827 --- /dev/null +++ b/examples/controllers/sync_all.py @@ -0,0 +1,30 @@ +""" +Sync subplots +============= + +Use one controller for all subplots. +""" + +# test_example = false +# sphinx_gallery_pygfx_docs = 'screenshot' + +import numpy as np +import fastplotlib as fpl + + +xs = np.linspace(0, 2 * np.pi, 100) +ys = np.sin(xs) + +figure = fpl.Figure(shape=(2, 2), controller_ids="sync", size=(700, 560)) + +for subplot in figure: + subplot.add_line(np.column_stack([xs, ys + np.random.normal(scale=0.5, size=100)])) + +figure.show(maintain_aspect=False) + + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.loop.run() diff --git a/examples/events/README.rst b/examples/events/README.rst new file mode 100644 index 000000000..8e2deca4b --- /dev/null +++ b/examples/events/README.rst @@ -0,0 +1,4 @@ +Events +====== + +Several examples using events \ No newline at end of file diff --git a/examples/events/cmap_event.py b/examples/events/cmap_event.py new file mode 100644 index 000000000..6cd68f333 --- /dev/null +++ b/examples/events/cmap_event.py @@ -0,0 +1,75 @@ +""" +cmap event +========== + +Add a cmap event handler to multiple graphics. When any one graphic changes the cmap, the cmap of all other graphics +is also changed. + +This also shows how bidirectional events are supported. +""" + +# test_example = false +# sphinx_gallery_pygfx_docs = 'screenshot' + +import numpy as np +import fastplotlib as fpl +import imageio.v3 as iio + +# load images +img1 = iio.imread("imageio:camera.png") +img2 = iio.imread("imageio:moon.png") + +# Create a figure +figure = fpl.Figure( + shape=(2, 2), + size=(700, 560), + names=["camera", "moon", "sine", "cloud"], +) + +# create graphics +figure["camera"].add_image(img1) +figure["moon"].add_image(img2) + +# sine wave +xs = np.linspace(0, 4 * np.pi, 100) +ys = np.sin(xs) + +figure["sine"].add_line(np.column_stack([xs, ys])) + +# make a 2D gaussian cloud +cloud_data = np.random.normal(0, scale=3, size=1000).reshape(500, 2) +figure["cloud"].add_scatter( + cloud_data, + sizes=3, + cmap="plasma", + cmap_transform=np.linalg.norm(cloud_data, axis=1) # cmap transform using distance from origin +) +figure["cloud"].axes.intersection = (0, 0, 0) + +# show the plot +figure.show() + + +# event handler to change the cmap of all graphics when the cmap of any one graphic changes +def cmap_changed(ev: fpl.GraphicFeatureEvent): + # get the new cmap + new_cmap = ev.info["value"] + + # set cmap of the graphics in the other subplots + for subplot in figure: + subplot.graphics[0].cmap = new_cmap + + +for subplot in figure: + # add event handler to the graphic added to each subplot + subplot.graphics[0].add_event_handler(cmap_changed, "cmap") + + +# change the cmap of graphic image, triggers all other graphics to set the cmap +figure["camera"].graphics[0].cmap = "jet" + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.loop.run() diff --git a/examples/events/drag_points.py b/examples/events/drag_points.py new file mode 100644 index 000000000..9a91779d4 --- /dev/null +++ b/examples/events/drag_points.py @@ -0,0 +1,99 @@ +""" +Drag points +=========== + +Example where you can drag scatter points on a line. This example also demonstrates how you can use a shared buffer +between two graphics to represent the same data using different graphics. When you update the data of one graphic the +data of the other graphic is also changed simultaneously since they use the same underlying buffer on the GPU. + +""" + +# test_example = false +# sphinx_gallery_pygfx_docs = 'screenshot' + +import numpy as np +import fastplotlib as fpl +import pygfx + +xs = np.linspace(0, 2 * np.pi, 10) +ys = np.sin(xs) + +data = np.column_stack([xs, ys]) + +figure = fpl.Figure(size=(700, 560)) + +# add a line +line_graphic = figure[0, 0].add_line(data) + +# add a scatter, share the line graphic buffer! +scatter_graphic = figure[0, 0].add_scatter(data=line_graphic.data, sizes=25, colors="r") + +is_moving = False +vertex_index = None + + +@scatter_graphic.add_event_handler("pointer_down") +def start_drag(ev: pygfx.PointerEvent): + global is_moving + global vertex_index + + if ev.button != 1: + return + + is_moving = True + vertex_index = ev.pick_info["vertex_index"] + scatter_graphic.colors[vertex_index] = "cyan" + + +@figure.renderer.add_event_handler("pointer_move") +def move_point(ev): + global is_moving + global vertex_index + + # if not moving, return + if not is_moving: + return + + # disable controller + figure[0, 0].controller.enabled = False + + # map x, y from screen space to world space + pos = figure[0, 0].map_screen_to_world(ev) + + if pos is None: + # end movement + is_moving = False + scatter_graphic.colors[vertex_index] = "r" # reset color + vertex_index = None + return + + # change scatter data + # since we are sharing the buffer, the line data will also change + scatter_graphic.data[vertex_index, :-1] = pos[:-1] + + # re-enable controller + figure[0, 0].controller.enabled = True + + +@figure.renderer.add_event_handler("pointer_up") +def end_drag(ev: pygfx.PointerEvent): + global is_moving + global vertex_index + + # end movement + if is_moving: + # reset color + scatter_graphic.colors[vertex_index] = "r" + + is_moving = False + vertex_index = None + + +figure.show() + + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.loop.run() diff --git a/examples/misc/simple_event.py b/examples/events/image_click.py similarity index 58% rename from examples/misc/simple_event.py rename to examples/events/image_click.py index e382f04b5..acb6cde37 100644 --- a/examples/misc/simple_event.py +++ b/examples/events/image_click.py @@ -1,14 +1,15 @@ """ -Simple Event -============ +Image click event +================= -Example showing how to add a simple callback event. +Example showing how to use a click event on an image. """ # test_example = false # sphinx_gallery_pygfx_docs = 'screenshot' import fastplotlib as fpl +import pygfx import imageio.v3 as iio data = iio.imread("imageio:camera.png") @@ -16,32 +17,21 @@ # Create a figure figure = fpl.Figure(size=(700, 560)) -# plot sine wave, use a single color -image_graphic = figure[0,0].add_image(data=data) +# create image graphic +image_graphic = figure[0, 0].add_image(data=data) # show the plot figure.show() -# define callback function to print the event data -def callback_func(event_data): - print(event_data.info) - - -# Will print event data when the color changes -image_graphic.add_event_handler(callback_func, "cmap") - -image_graphic.cmap = "viridis" - - # adding a click event, we can also use decorators to add event handlers @image_graphic.add_event_handler("click") -def click_event(event_data): +def click_event(ev: pygfx.PointerEvent): # get the click location in screen coordinates - xy = (event_data.x, event_data.y) + xy = (ev.x, ev.y) # map the screen coordinates to world coordinates - xy = figure[0,0].map_screen_to_world(xy)[:-1] + xy = figure[0, 0].map_screen_to_world(xy)[:-1] # print the click location print(xy) diff --git a/examples/events/image_data_event.py b/examples/events/image_data_event.py new file mode 100644 index 000000000..32f78996c --- /dev/null +++ b/examples/events/image_data_event.py @@ -0,0 +1,56 @@ +""" +Image data event +================ + +Example showing how to add an event handler to an ImageGraphic to capture when the data changes. +""" + +# test_example = false +# sphinx_gallery_pygfx_docs = 'screenshot' + +import fastplotlib as fpl +import imageio.v3 as iio +from scipy.ndimage import gaussian_filter + +rgb_weights = [0.299, 0.587, 0.114] + +# load images, convert to grayscale +img1 = iio.imread("imageio:wikkie.png") @ rgb_weights +img2 = iio.imread("imageio:astronaut.png") @ rgb_weights + +# Create a figure +figure = fpl.Figure( + shape=(1, 2), + size=(700, 560), + names=["image", "gaussian filtered image"] +) + +# create image graphics +image_raw = figure[0, 0].add_image(img1) +image_filt = figure[0, 1].add_image(gaussian_filter(img1, sigma=5)) + +# show the plot +figure.show() + + +# add event handler +@image_raw.add_event_handler("data") +def data_changed(ev: fpl.GraphicFeatureEvent): + # get the new image data + new_img = ev.info["value"] + + # set the filtered image graphic + image_filt.data = gaussian_filter(new_img, sigma=5) + + +# set the data on the first image graphic +# this will trigger the `data_changed()` handler to be called +image_raw.data = img2 + + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.loop.run() + diff --git a/examples/events/key_events.py b/examples/events/key_events.py new file mode 100644 index 000000000..6979d44d7 --- /dev/null +++ b/examples/events/key_events.py @@ -0,0 +1,85 @@ +""" +Key Events +========== + +Move an image around using and change some of its properties using keyboard events. + +- Use the arrows keys to move the image by changing its offset + +- Press "v", "g", "p" to change the colormaps (viridis, grey, plasma). + +- Press "r" to rotate the image +18 degrees (pi / 10 radians) +- Press "Shift + R" to rotate the image -18 degrees +- Axis of rotation is the origin + +- Press "-", "=" to decrease/increase the vmin +- Press "_", "+" to decrease/increase the vmax + +We use the ImageWidget here because the histogram LUT tool makes it easy to see the changes in vmin and vmax. +""" + +# test_example = false +# sphinx_gallery_pygfx_docs = 'screenshot' + +import numpy as np +import fastplotlib as fpl +import pygfx +import imageio.v3 as iio + +data = iio.imread("imageio:camera.png") + +iw = fpl.ImageWidget(data, figure_kwargs={"size": (700, 560)}) + +image = iw.managed_graphics[0] + + +@iw.figure.renderer.add_event_handler("key_down") +def handle_event(ev: pygfx.KeyboardEvent): + match ev.key: + # change the cmap + case "v": + image.cmap = "viridis" + case "g": + image.cmap = "grey" + case "p": + image.cmap = "plasma" + + # keys to change vmin/vmax + case "-": + image.vmin -= 1 + case "=": + image.vmin += 1 + case "_": + image.vmax -= 1 + case "+": + image.vmax += 1 + + # rotate + case "r": + image.rotate(np.pi / 10, axis="z") + case "R": + image.rotate(-np.pi / 10, axis="z") + + # arrow key events to move the image + case "ArrowUp": + image.offset = image.offset + [0, -10, 0] # remember y-axis is flipped for images + case "ArrowDown": + image.offset = image.offset + [0, 10, 0] + case "ArrowLeft": + image.offset = image.offset + [-10, 0, 0] + case "ArrowRight": + image.offset = image.offset + [10, 0, 0] + + +iw.show() + + +figure = iw.figure # ignore, this is just so the docs gallery scraper picks up the figure + + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.loop.run() + diff --git a/examples/events/line_data_thickness_event.py b/examples/events/line_data_thickness_event.py new file mode 100644 index 000000000..4baaba42c --- /dev/null +++ b/examples/events/line_data_thickness_event.py @@ -0,0 +1,79 @@ +""" +Events line data thickness +========================== + +Simple example of adding event handlers for line data and thickness. +""" + +# test_example = false +# sphinx_gallery_pygfx_docs = 'screenshot' + +import fastplotlib as fpl +import numpy as np + +figure = fpl.Figure(size=(700, 560)) + +xs = np.linspace(0, 4 * np.pi, 100) +# sine wave +ys = np.sin(xs) +sine = np.column_stack([xs, ys]) + +# cosine wave +ys = np.cos(xs) +cosine = np.column_stack([xs, ys]) + +# create line graphics +sine_graphic = figure[0, 0].add_line(data=sine) +cosine_graphic = figure[0, 0].add_line(data=cosine, offset=(0, 4, 0)) + +# make a list of the line graphics for convenience +lines = [sine_graphic, cosine_graphic] + + +def change_thickness(ev: fpl.GraphicFeatureEvent): + # sets thickness of all the lines + new_value = ev.info["value"] + + for g in lines: + g.thickness = new_value + + +def change_data(ev: fpl.GraphicFeatureEvent): + # sets data of all the lines using the given event and value from the event + + # the user's slice/index + # This can be a single int index, a slice, + # or even a numpy array of int or bool for fancy indexing! + indices = ev.info["key"] + + # the new values to set at the given indices + new_values = ev.info["value"] + + # set the data for all the lines + for g in lines: + g.data[indices] = new_values + + +# add the event handlers to the line graphics +for g in lines: + g.add_event_handler(change_thickness, "thickness") + g.add_event_handler(change_data, "data") + + +figure.show() +figure[0, 0].axes.intersection = (0, 0, 0) + +# set the y-value of the middle 40 points of the sine graphic to 1 +# after the sine_graphic sets its data, the event handlers will be called +# and therefore the cosine graphic will also set its data using the event data +sine_graphic.data[30:70, 1] = np.ones(40) + +# set the thickness of the cosine graphic, this will trigger an event +# that causes the sine graphic's thickness to also be set from this value +cosine_graphic.thickness = 10 + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.loop.run() diff --git a/examples/events/lines_mouse_nearest.py b/examples/events/lines_mouse_nearest.py new file mode 100644 index 000000000..8c9601de6 --- /dev/null +++ b/examples/events/lines_mouse_nearest.py @@ -0,0 +1,62 @@ +""" +Highlight nearest circle +======================== + +Shows how to use the "pointer_move" event to get the nearest circle and highlight it. + +""" + +# test_example = false +# sphinx_gallery_pygfx_docs = 'screenshot' + +from itertools import product +import numpy as np +import fastplotlib as fpl +import pygfx + + +def make_circle(center, radius: float, n_points: int) -> np.ndarray: + theta = np.linspace(0, 2 * np.pi, n_points) + xs = radius * np.cos(theta) + ys = radius * np.sin(theta) + + return np.column_stack([xs, ys]) + center + +spatial_dims = (100, 100) + +circles = list() +for center in product(range(0, spatial_dims[0], 15), range(0, spatial_dims[1], 15)): + circles.append(make_circle(center, 5, n_points=75)) + +pos_xy = np.vstack(circles) + +figure = fpl.Figure(size=(700, 560)) + +line_collection = figure[0, 0].add_line_collection(circles, colors="w", thickness=5) + + +@figure.renderer.add_event_handler("pointer_move") +def highlight_nearest(ev: pygfx.PointerEvent): + line_collection.colors = "w" + + pos = figure[0, 0].map_screen_to_world(ev) + if pos is None: + return + + # get_nearest_graphics() is a helper function + # sorted the passed array or collection of graphics from nearest to furthest from the passed `pos` + nearest = fpl.utils.get_nearest_graphics(pos, line_collection)[0] + + nearest.colors = "r" + + +# remove clutter +figure[0, 0].axes.visible = False + +figure.show() + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.loop.run() diff --git a/examples/events/paint_image.py b/examples/events/paint_image.py new file mode 100644 index 000000000..cfc2eda11 --- /dev/null +++ b/examples/events/paint_image.py @@ -0,0 +1,71 @@ +""" +Paint an Image +============== + +Click and drag the mouse to paint in the image +""" + +# test_example = false +# sphinx_gallery_pygfx_docs = 'screenshot' + +import numpy as np +import fastplotlib as fpl +import pygfx + +figure = fpl.Figure(size=(700, 560)) + +# add a blank image +image = figure[0, 0].add_image(np.zeros((100, 100)), vmin=0, vmax=255) + +painting = False # use to toggle painting state + + +@image.add_event_handler("pointer_down") +def on_pointer_down(ev: pygfx.PointerEvent): + # start painting when mouse button is down + global painting + + # get image element index, (x, y) pos corresponds to array (column, row) + col, row = ev.pick_info["index"] + + # increase value of this image element + image.data[row, col] = np.clip(image.data[row, col] + 50, 0, 255) + + # toggle on painting state + painting = True + + # disable controller until painting stops when mouse button is un-clicked + figure[0, 0].controller.enabled = False + + +@image.add_event_handler("pointer_move") +def on_pointer_move(ev: pygfx.PointerEvent): + # continue painting when mouse pointer is moved + global painting + + if not painting: + return + + # get image element index, (x, y) pos corresponds to array (column, row) + col, row = ev.pick_info["index"] + + image.data[row, col] = np.clip(image.data[row, col] + 50, 0, 255) + + +@figure.renderer.add_event_handler("pointer_up") +def on_pointer_up(ev: pygfx.PointerEvent): + # toggle off painting state + global painting + painting = False + + # re-enable controller + figure[0, 0].controller.enabled = True + + +figure.show() + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.loop.run() diff --git a/examples/events/scatter_click.py b/examples/events/scatter_click.py new file mode 100644 index 000000000..e56dca743 --- /dev/null +++ b/examples/events/scatter_click.py @@ -0,0 +1,66 @@ +""" +Scatter click +============= + +Add an event handler to click on scatter points and highlight them, i.e. change the color and size of the clicked point. +Fly around the 3D scatter using WASD keys and click on points to highlight them +""" + +# test_example = false +# sphinx_gallery_pygfx_docs = 'screenshot' + +import numpy as np +import fastplotlib as fpl +import pygfx + +# make a gaussian cloud +data = np.random.normal(loc=0, scale=3, size=1500).reshape(500, 3) + +figure = fpl.Figure(cameras="3d", size=(700, 560)) + +scatter = figure[0, 0].add_scatter( + data, # the gaussian cloud + sizes=10, # some big points that are easy to click + cmap="viridis", + cmap_transform=np.linalg.norm(data, axis=1) # color points using distance from origin +) + +# simple dict to restore the original scatter color and size +# of the previously clicked point upon clicking a new point +old_props = {"index": None, "size": None, "color": None} + + +@scatter.add_event_handler("click") +def highlight_point(ev: pygfx.PointerEvent): + global old_props + + # the index of the point that was just clicked + new_index = ev.pick_info["vertex_index"] + + # restore old point's properties + if old_props["index"] is not None: + old_index = old_props["index"] + if new_index == old_index: + # same point was clicked, ignore + return + scatter.colors[old_index] = old_props["color"] + scatter.sizes[old_index] = old_props["size"] + + # store the current property values of this new point + old_props["index"] = new_index + old_props["color"] = scatter.colors[new_index].copy() # if you do not copy you will just get a view of the array! + old_props["size"] = scatter.sizes[new_index] + + # highlight this new point + scatter.colors[new_index] = "magenta" + scatter.sizes[new_index] = 20 + + +figure.show() + + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.loop.run() diff --git a/examples/events/scatter_hover.py b/examples/events/scatter_hover.py new file mode 100644 index 000000000..9d69dc24c --- /dev/null +++ b/examples/events/scatter_hover.py @@ -0,0 +1,69 @@ +""" +Scatter hover +============= + +Add an event handler to hover on scatter points and highlight them, i.e. change the color and size of the clicked point. +Fly around the 3D scatter using WASD keys and click on points to highlight them. + +There is no "hover" event, you can create a hover effect by using "pointer_move" events. +""" + +# test_example = false +# sphinx_gallery_pygfx_docs = 'screenshot' + +import numpy as np +import fastplotlib as fpl +import pygfx + +# make a gaussian cloud +data = np.random.normal(loc=0, scale=3, size=1500).reshape(500, 3) + +figure = fpl.Figure(cameras="3d", size=(700, 560)) + +scatter = figure[0, 0].add_scatter( + data, # the gaussian cloud + sizes=10, # some big points that are easy to click + cmap="viridis", + cmap_transform=np.linalg.norm(data, axis=1) # color points using distance from origin +) + +# simple dict to restore the original scatter color and size +# of the previously clicked point upon clicking a new point +old_props = {"index": None, "size": None, "color": None} + + +@scatter.add_event_handler("pointer_move") +def highlight_point(ev: pygfx.PointerEvent): + global old_props + + # the index of the point that was just entered + new_index = ev.pick_info["vertex_index"] + + # if a new point has been entered, but we have not yet had + # a leave event for the previous point, then reset this old point + if old_props["index"] is not None: + old_index = old_props["index"] + if new_index == old_index: + # same point, ignore + return + scatter.colors[old_index] = old_props["color"] + scatter.sizes[old_index] = old_props["size"] + + # store the current property values of this new point + old_props["index"] = new_index + old_props["color"] = scatter.colors[new_index].copy() # if you do not copy you will just get a view of the array! + old_props["size"] = scatter.sizes[new_index] + + # highlight this new point + scatter.colors[new_index] = "magenta" + scatter.sizes[new_index] = 20 + + +figure.show() + + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.loop.run() diff --git a/examples/events/scatter_hover_transforms.py b/examples/events/scatter_hover_transforms.py new file mode 100644 index 000000000..7f9fbb9ff --- /dev/null +++ b/examples/events/scatter_hover_transforms.py @@ -0,0 +1,126 @@ +""" +Scatter data explore scalers +============================ + +Based on the sklearn preprocessing scalers example. Hover points to highlight the corresponding point of the dataset +transformed by the various scalers. + +See: https://scikit-learn.org/stable/auto_examples/preprocessing/plot_all_scaling.html + +This is another example that uses bi-directional events. +""" + +# test_example = false +# sphinx_gallery_pygfx_docs = 'screenshot' + +from sklearn.datasets import fetch_california_housing +from sklearn.preprocessing import ( + Normalizer, + QuantileTransformer, + PowerTransformer, +) + +import fastplotlib as fpl +import pygfx + +# get the dataset +dataset = fetch_california_housing() +X_full, y = dataset.data, dataset.target +feature_names = dataset.feature_names + +feature_mapping = { + "MedInc": "Median income in block", + "HouseAge": "Median house age in block", + "AveRooms": "Average number of rooms", + "AveBedrms": "Average number of bedrooms", + "Population": "Block population", + "AveOccup": "Average house occupancy", + "Latitude": "House block latitude", + "Longitude": "House block longitude", +} + +# Take only 2 features to make visualization easier +# Feature MedInc has a long tail distribution. +# Feature AveOccup has a few but very large outliers. +features = ["MedInc", "AveOccup"] +features_idx = [feature_names.index(feature) for feature in features] +X = X_full[:, features_idx] + +# list of our scalers and their names as strings +scalers = [PowerTransformer, QuantileTransformer, Normalizer] +names = ["Original Data", *[s.__name__ for s in scalers]] + +# fastplotlib code starts here, make a figure +figure = fpl.Figure( + shape=(2, 2), + names=names, + size=(700, 780), +) + +scatters = list() # list to store our 4 scatter graphics for convenience + +# add a scatter of the original data +s = figure["Original Data"].add_scatter( + data=X, + cmap="viridis", + cmap_transform=y, + sizes=3, +) + +# append to list of scatters +scatters.append(s) + +# add the scaled data as scatter graphics +for scaler in scalers: + name = scaler.__name__ + s = figure[name].add_scatter(scaler().fit_transform(X), cmap="viridis", cmap_transform=y, sizes=3) + scatters.append(s) + + +# simple dict to restore the original scatter color and size +# of the previously clicked point upon clicking a new point +old_props = {"index": None, "size": None, "color": None} + + +def highlight_point(ev: pygfx.PointerEvent): + # event handler to highlight the point when the mouse moves over it + global old_props + + # the index of the point that was just clicked + new_index = ev.pick_info["vertex_index"] + + # restore old point's properties + if old_props["index"] is not None: + old_index = old_props["index"] + if new_index == old_index: + # same point was clicked, ignore + return + for s in scatters: + s.colors[old_index] = old_props["color"] + s.sizes[old_index] = old_props["size"] + + # store the current property values of this new point + old_props["index"] = new_index + # all the scatters have the same colors and size for the corresponding index + # so we can just use the first scatter's original color and size + old_props["color"] = scatters[0].colors[new_index].copy() # if you do not copy you will just get a view of the array! + old_props["size"] = scatters[0].sizes[new_index] + + # highlight this new point + for s in scatters: + s.colors[new_index] = "magenta" + s.sizes[new_index] = 15 + + +# add the event handler to all the scatter graphics +for s in scatters: + s.add_event_handler(highlight_point, "pointer_move") + + +figure.show(maintain_aspect=False) + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.loop.run() diff --git a/examples/image_widget/image_widget_single_video.py b/examples/image_widget/image_widget_single_video.py index 3a0e94fca..aa601d3c1 100644 --- a/examples/image_widget/image_widget_single_video.py +++ b/examples/image_widget/image_widget_single_video.py @@ -20,7 +20,7 @@ movie_sub = movie[:15, ::12, ::12].copy() del movie -iw = fpl.ImageWidget(movie_sub, rgb=[True], figure_kwargs={"size": (700, 560)}) +iw = fpl.ImageWidget(movie_sub, rgb=True, figure_kwargs={"size": (700, 560)}) # ImageWidget supports setting window functions the `time` "t" or `volume` "z" dimension # These can also be given as kwargs to `ImageWidget` during instantiation diff --git a/examples/ipywidgets/README.rst b/examples/ipywidgets/README.rst new file mode 100644 index 000000000..3f6ae9d5f --- /dev/null +++ b/examples/ipywidgets/README.rst @@ -0,0 +1,2 @@ +Using with ipywidgets +===================== diff --git a/examples/ipywidgets/ipywidgets_modify_image.py b/examples/ipywidgets/ipywidgets_modify_image.py new file mode 100644 index 000000000..c0206e945 --- /dev/null +++ b/examples/ipywidgets/ipywidgets_modify_image.py @@ -0,0 +1,69 @@ +""" +ipwidgets modify an ImageGraphic +================================ + +Use ipywidgets to modify some features of an ImageGraphic. Run in jupyterlab. +""" + +# test_example = false +# sphinx_gallery_pygfx_docs = 'code' + +import fastplotlib as fpl +from scipy.ndimage import gaussian_filter +import imageio.v3 as iio +from ipywidgets import FloatRangeSlider, FloatSlider, Select, VBox + +data = iio.imread("imageio:moon.png") + +iw = fpl.ImageWidget(data, figure_kwargs={"size": (700, 560)}) + +# get the ImageGraphic from the image widget +image = iw.managed_graphics[0] + +min_v, max_v = fpl.utils.quick_min_max(data) + +# slider to adjust vmin, vmax of the image +vmin_vmax_slider = FloatRangeSlider(value=(image.vmin, image.vmax), min=min_v, max=max_v, description="vmin, vmax:") + +# slider to adjust sigma of a gaussian kernel used to filter the image (i.e. gaussian blur) +slider_sigma = FloatSlider(min=0.0, max=10.0, value=0.0, description="σ: ") + +# select box to choose the sample image shown in the ImageWidget +select_image = Select(options=["moon.png", "camera.png", "checkerboard.png"], description="image: ") + + +def update_vmin_vmax(change): + vmin, vmax = change["new"] + + image = iw.managed_graphics[0] + image.vmin, image.vmax = vmin, vmax + + +def update_sigma(change): + sigma = change["new"] + + # set a "frame apply" dict onto the ImageWidget + # this maps {image_index: function} + # the function is applied to the image data at the image index given by the key + iw.frame_apply = {0: lambda image_data: gaussian_filter(image_data, sigma=sigma)} + + +def update_image(change): + filename = change["new"] + data = iio.imread(f"imageio:{filename}") + + iw.set_data(data) + + # set vmin, vmax sliders w.r.t. this new image + image = iw.managed_graphics[0] + vmin_vmax_slider.value = image.vmin, image.vmax + vmin_vmax_slider.min, vmin_vmax_slider.max = fpl.utils.quick_min_max(data) + + +# connect the ipywidgets to the handler functions +vmin_vmax_slider.observe(update_vmin_vmax, "value") +slider_sigma.observe(update_sigma, "value") +select_image.observe(update_image, "value") + +# display in a vbox +VBox([iw.show(), vmin_vmax_slider, slider_sigma, select_image]) diff --git a/examples/ipywidgets/ipywidgets_sliders_line.py b/examples/ipywidgets/ipywidgets_sliders_line.py new file mode 100644 index 000000000..8288e5719 --- /dev/null +++ b/examples/ipywidgets/ipywidgets_sliders_line.py @@ -0,0 +1,91 @@ +""" +ipywidget sliders to modify a sine wave +======================================= + +Example with ipywidgets sliders to change a sine wave and view the frequency spectra. You can run this in jupyterlab +""" + +# test_example = false +# sphinx_gallery_pygfx_docs = 'code' + +import numpy as np +import fastplotlib as fpl +from ipywidgets import FloatSlider, Checkbox, VBox + + +def generate_data(freq, duration, sampling_rate, ampl, noise_sigma): + # generate a sine wave using given params + xs = np.linspace(0, duration, sampling_rate * duration) + ys = np.sin((2 * np.pi) * freq * xs) * ampl + + noise = np.random.normal(scale=noise_sigma, size=sampling_rate * duration) + + signal = np.column_stack([xs, ys + noise]) + fft_mag = np.abs(np.fft.rfft(signal[:, 1])) + fft_freqs = np.linspace(0, sampling_rate / 2, num=fft_mag.shape[0]) + + return np.column_stack([xs, ys + noise]), np.column_stack([fft_freqs, fft_mag]) + + +signal, fft = generate_data( + freq=1, + duration=10, + sampling_rate=50, + ampl=1, + noise_sigma=0.05 +) + +# create a figure +figure = fpl.Figure(shape=(2, 1), names=["signal", "fft"], size=(700, 560)) + +# line graphic for the signal +signal_line = figure[0, 0].add_line(signal, thickness=1) + +# easier to understand the frequency of the sine wave if the +# axes go through the middle of the sine wave +figure[0, 0].axes.intersection = (0, 0, 0) + +# line graphic for fft +fft_line = figure[1, 0].add_line(fft) + +# do not maintain the aspect ratio of the fft subplot +figure[1, 0].camera.maintain_aspect = False + +# create ipywidget sliders +slider_freq = FloatSlider(min=0.1, max=10, value=1.0, step=0.1, description="freq: ") +slider_ampl = FloatSlider(min=0.0, max=10, value=1.0, step=0.5, description="ampl: ") +slider_noise = FloatSlider(min=0, max=1, value=0.05, step=0.05, description="noise: ") + +# checkbox +checkbox_autoscale = Checkbox(value=False, description="autoscale: ") + + +def update(*args): + # update whenever a slider changes + freq = slider_freq.value + ampl = slider_ampl.value + noise = slider_noise.value + + signal, fft = generate_data( + freq=freq, + duration=10, + sampling_rate=50, + ampl=ampl, + noise_sigma=noise, + ) + + signal_line.data[:, :-1] = signal + fft_line.data[:, :-1] = fft + + if checkbox_autoscale.value: + for subplot in figure: + subplot.auto_scale(maintain_aspect=False) + + +# when any one slider changes, it calls update +for slider in [slider_freq, slider_ampl, slider_noise]: + slider.observe(update, "value") + +# display the fastplotlib figure and ipywidgets in a VBox (vertically stacked) +# figure.show() just returns an ipywidget object +VBox([figure.show(), slider_freq, slider_ampl, slider_noise, checkbox_autoscale]) diff --git a/examples/screenshots/linear_region_selectors_match_offsets.png b/examples/screenshots/linear_region_selectors_match_offsets.png index 327f14e72..e6fab4c4d 100644 --- a/examples/screenshots/linear_region_selectors_match_offsets.png +++ b/examples/screenshots/linear_region_selectors_match_offsets.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8fac4f439b34a5464792588b77856f08c127c0ee06fa77722818f8d6b48dd64c -size 95433 +oid sha256:f2eac8ffeb8cd35a0c65d51b0952defea61928abb53c865e681fa72af4ac4347 +size 95750 diff --git a/examples/screenshots/no-imgui-linear_region_selectors_match_offsets.png b/examples/screenshots/no-imgui-linear_region_selectors_match_offsets.png index 809908432..d82efa849 100644 --- a/examples/screenshots/no-imgui-linear_region_selectors_match_offsets.png +++ b/examples/screenshots/no-imgui-linear_region_selectors_match_offsets.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:303d562f1a16f6a704415072d43ca08a51e12a702292b522e0f17f397b1aee60 -size 96668 +oid sha256:1b22ee4506bc532344cfcbd5daa0c4e90d9a831d59f1d916bd28534786947771 +size 97036 diff --git a/examples/selection_tools/linear_region_selector.py b/examples/selection_tools/linear_region_selector.py index 6fa17db38..bfbf27811 100644 --- a/examples/selection_tools/linear_region_selector.py +++ b/examples/selection_tools/linear_region_selector.py @@ -29,15 +29,15 @@ names=names, ) -# preallocated size for zoomed data -zoomed_prealloc = 1_000 +# preallocated number of datapoints for zoomed data +zoomed_prealloc = 5_000 # data to plot -xs = np.linspace(0, 10 * np.pi, 1_000) -ys = np.sin(xs) # y = sine(x) +xs = np.linspace(0, 200 * np.pi, 10_000) +ys = np.sin(xs) + np.random.normal(scale=0.2, size=10000) # make sine along x axis -sine_graphic_x = figure[0, 0].add_line(np.column_stack([xs, ys])) +sine_graphic_x = figure[0, 0].add_line(np.column_stack([xs, ys]), thickness=1) # x = sine(y), sine(y) > 0 = 0 sine_y = ys @@ -51,7 +51,7 @@ sine_graphic_y.position_y = 50 # add linear selectors -selector_x = sine_graphic_x.add_linear_region_selector() # default axis is "x" +selector_x = sine_graphic_x.add_linear_region_selector((0, 100)) # default axis is "x" selector_y = sine_graphic_y.add_linear_region_selector(axis="y") # preallocate array for storing zoomed in data @@ -102,8 +102,8 @@ def set_zoom_y(ev): selector_y.add_event_handler(set_zoom_y, "selection") # set initial selection -selector_x.selection = selector_y.selection = (0, 4 * np.pi) - +selector_x.selection = (0, 150) +selector_y.selection = (0, 150) figure.show(maintain_aspect=False) diff --git a/examples/tests/testutils.py b/examples/tests/testutils.py index d6fce52fe..4c23b3481 100644 --- a/examples/tests/testutils.py +++ b/examples/tests/testutils.py @@ -25,8 +25,9 @@ "line_collection/*.py", "gridplot/*.py", "window_layouts/*.py", - "misc/*.py", + "events/*.py", "selection_tools/*.py", + "misc/*.py", "guis/*.py", ] diff --git a/examples/text/README.rst b/examples/text/README.rst new file mode 100644 index 000000000..01466a39f --- /dev/null +++ b/examples/text/README.rst @@ -0,0 +1,2 @@ +Text Examples +============= diff --git a/examples/text/moving_label.py b/examples/text/moving_label.py new file mode 100644 index 000000000..45d2439ee --- /dev/null +++ b/examples/text/moving_label.py @@ -0,0 +1,84 @@ +""" +Moving TextGraphic label +======================== + +A TextGraphic that labels a point on a line and another TextGraphic that moves along the line on every draw. +""" + +# test_example = false +# sphinx_gallery_pygfx_docs = 'animate 10s' + +import numpy as np +import fastplotlib as fpl + +# create a sinc wave +xs = np.linspace(-2 * np.pi, 2 * np.pi, 200) +ys = np.sinc(xs) + +data = np.column_stack([xs, ys]) + +# create a figure +figure = fpl.Figure(size=(700, 450)) + +# sinc wave +line = figure[0, 0].add_line(data, thickness=2) + +# position for the text label on the peak +pos = (0, max(ys), 0) + +# create label for the peak +text_peak = figure[0, 0].add_text( + f"peak ", + font_size=20, + anchor="bottom-right", + offset=pos +) + +# add a point on the peak +point_peak = figure[0, 0].add_scatter(np.asarray([pos]), sizes=10, colors="r") + +# create a text that will move along the line +text_moving = figure[0, 0].add_text( + f"({xs[0]:.2f}, {ys[0]:.2f}) ", + font_size=16, + outline_color="k", + outline_thickness=1, + anchor="top-center", + offset=(*data[0], 0) +) +# a point that will move on the line +point_moving = figure[0, 0].add_scatter(np.asarray([data[0]]), sizes=10, colors="magenta") + + +index = 0 +def update(): + # moves the text and point before every draw + global index + # get the new position + new_pos = (*data[index], 0) + + # move the text and point to the new position + text_moving.offset = new_pos + point_moving.data[0] = new_pos + + # set the text to the new position + text_moving.text = f"({new_pos[0]:.2f}, {new_pos[1]:.2f})" + + # increment index + index += 1 + if index == data.shape[0]: + index = 0 + + +# add update as an animation functions +figure.add_animations(update) + +figure[0, 0].axes.visible = False +figure.show(maintain_aspect=False) + + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.loop.run() diff --git a/fastplotlib/__init__.py b/fastplotlib/__init__.py index 6d92efffa..6dab91605 100644 --- a/fastplotlib/__init__.py +++ b/fastplotlib/__init__.py @@ -5,6 +5,7 @@ # this must be the first import for auto-canvas detection from .utils import loop # noqa from .graphics import * +from .graphics.features import GraphicFeatureEvent from .graphics.selectors import * from .graphics.utils import pause_events from .legends import * diff --git a/fastplotlib/graphics/__init__.py b/fastplotlib/graphics/__init__.py index ff96baa4c..03f361502 100644 --- a/fastplotlib/graphics/__init__.py +++ b/fastplotlib/graphics/__init__.py @@ -1,3 +1,4 @@ +from ._base import Graphic from .line import LineGraphic from .scatter import ScatterGraphic from .image import ImageGraphic @@ -7,8 +8,8 @@ __all__ = [ "LineGraphic", - "ImageGraphic", "ScatterGraphic", + "ImageGraphic", "TextGraphic", "LineCollection", "LineStack", diff --git a/fastplotlib/graphics/_base.py b/fastplotlib/graphics/_base.py index 61ad291ee..e115107b0 100644 --- a/fastplotlib/graphics/_base.py +++ b/fastplotlib/graphics/_base.py @@ -16,7 +16,7 @@ import pygfx -from ._features import ( +from .features import ( BufferManager, Deleted, Name, @@ -50,7 +50,7 @@ class Graphic: - _features: set[str] = {} + _features: dict[str, type] = dict() def __init_subclass__(cls, **kwargs): # set the type of the graphic in lower case like "image", "line_collection", etc. @@ -63,12 +63,12 @@ def __init_subclass__(cls, **kwargs): # set of all features cls._features = { - *cls._features, - "name", - "offset", - "rotation", - "visible", - "deleted", + **cls._features, + "name": Name, + "offset": Offset, + "rotation": Rotation, + "visible": Visible, + "deleted": Deleted, } super().__init_subclass__(**kwargs) @@ -129,7 +129,7 @@ def __init__( @property def supported_events(self) -> tuple[str]: """events supported by this graphic""" - return (*tuple(self._features), *PYGFX_EVENTS) + return (*tuple(self._features.keys()), *PYGFX_EVENTS) @property def name(self) -> str | None: @@ -273,7 +273,7 @@ def decorator(_callback): # add to our record self._event_handlers[t].add(_callback) - if t in self._features: + if t in self._features.keys(): # fpl feature event feature = getattr(self, f"_{t}") feature.add_event_handler(_callback_wrapper) diff --git a/fastplotlib/graphics/_positions_base.py b/fastplotlib/graphics/_positions_base.py index 565a4cd98..5d98d16d1 100644 --- a/fastplotlib/graphics/_positions_base.py +++ b/fastplotlib/graphics/_positions_base.py @@ -4,7 +4,7 @@ import pygfx from ._base import Graphic -from ._features import ( +from .features import ( VertexPositions, VertexColors, UniformColor, @@ -58,7 +58,7 @@ def cmap(self, name: str): @property def size_space(self): """ - The coordinate space in which the size is expressed (‘screen’, ‘world’, ‘model’) + The coordinate space in which the size is expressed ('screen', 'world', 'model') See https://docs.pygfx.org/stable/_autosummary/utils/utils/enums/pygfx.utils.enums.CoordSpace.html#pygfx.utils.enums.CoordSpace for available options. """ diff --git a/fastplotlib/graphics/_features/__init__.py b/fastplotlib/graphics/features/__init__.py similarity index 96% rename from fastplotlib/graphics/_features/__init__.py rename to fastplotlib/graphics/features/__init__.py index a1915bbe9..18bcf5187 100644 --- a/fastplotlib/graphics/_features/__init__.py +++ b/fastplotlib/graphics/features/__init__.py @@ -19,7 +19,7 @@ from ._base import ( GraphicFeature, BufferManager, - FeatureEvent, + GraphicFeatureEvent, to_gpu_supported_dtype, ) @@ -67,4 +67,5 @@ "Rotation", "Visible", "Deleted", + "GraphicFeatureEvent", ] diff --git a/fastplotlib/graphics/_features/_base.py b/fastplotlib/graphics/features/_base.py similarity index 96% rename from fastplotlib/graphics/_features/_base.py rename to fastplotlib/graphics/features/_base.py index 1088dc005..d32904ae5 100644 --- a/fastplotlib/graphics/_features/_base.py +++ b/fastplotlib/graphics/features/_base.py @@ -1,5 +1,5 @@ from warnings import warn -from typing import Any, Literal +from typing import Literal import numpy as np from numpy.typing import NDArray @@ -23,7 +23,7 @@ def to_gpu_supported_dtype(array): return np.asarray(array).astype(np.float32) -class FeatureEvent(pygfx.Event): +class GraphicFeatureEvent(pygfx.Event): """ **All event instances have the following attributes** @@ -34,11 +34,11 @@ class FeatureEvent(pygfx.Event): +------------+-------------+-----------------------------------------------+ | graphic | Graphic | graphic instance that the event is from | +------------+-------------+-----------------------------------------------+ - | info | dict | event info dictionary (see below) | + | info | dict | event info dictionary | +------------+-------------+-----------------------------------------------+ | target | WorldObject | pygfx rendering engine object for the graphic | +------------+-------------+-----------------------------------------------+ - | time_stamp | float | time when the event occured, in ms | + | time_stamp | float | time when the event occurred, in ms | +------------+-------------+-----------------------------------------------+ """ @@ -57,7 +57,7 @@ def __init__(self, **kwargs): self._reentrant_block: bool = False @property - def value(self) -> Any: + def value(self): """Graphic Feature value, must be implemented in subclass""" raise NotImplemented @@ -120,7 +120,7 @@ def clear_event_handlers(self): """Clear all event handlers""" self._event_handlers.clear() - def _call_event_handlers(self, event_data: FeatureEvent): + def _call_event_handlers(self, event_data: GraphicFeatureEvent): if self._block_events: return @@ -310,7 +310,7 @@ def _emit_event(self, type: str, key, value): "key": key, "value": value, } - event = FeatureEvent(type, info=event_info) + event = GraphicFeatureEvent(type, info=event_info) self._call_event_handlers(event) diff --git a/fastplotlib/graphics/_features/_common.py b/fastplotlib/graphics/features/_common.py similarity index 53% rename from fastplotlib/graphics/_features/_common.py rename to fastplotlib/graphics/features/_common.py index e9c49a475..71e979f77 100644 --- a/fastplotlib/graphics/_features/_common.py +++ b/fastplotlib/graphics/features/_common.py @@ -1,12 +1,17 @@ import numpy as np -from ._base import GraphicFeature, FeatureEvent, block_reentrance +from ._base import GraphicFeature, GraphicFeatureEvent, block_reentrance class Name(GraphicFeature): - """Graphic name""" + property_name = "name" + event_info_spec = [ + {"dict key": "value", "type": "str", "description": "user provided name"}, + ] def __init__(self, value: str): + """Graphic name""" + self._value = value super().__init__() @@ -24,17 +29,29 @@ def set_value(self, graphic, value: str): self._value = value - event = FeatureEvent(type="name", info={"value": value}) + event = GraphicFeatureEvent(type="name", info={"value": value}) self._call_event_handlers(event) class Offset(GraphicFeature): - """Offset position of the graphic, [x, y, z]""" + property_name = "offset" + event_info_spec = [ + { + "dict key": "value", + "type": "np.ndarray[float, float, float]", + "description": "new offset (x, y, z)", + }, + ] def __init__(self, value: np.ndarray | list | tuple): + """Offset position of the graphic, [x, y, z]""" + self._validate(value) - self._value = np.array(value) - self._value.flags.writeable = False + # initialize zeros array + self._value = np.zeros(3) + + # set values + self._value[:] = value super().__init__() def _validate(self, value): @@ -48,22 +65,38 @@ def value(self) -> np.ndarray: @block_reentrance def set_value(self, graphic, value: np.ndarray | list | tuple): self._validate(value) + value = np.asarray(value) graphic.world_object.world.position = value - self._value = graphic.world_object.world.position.copy() - self._value.flags.writeable = False - event = FeatureEvent(type="offset", info={"value": value}) + # sometimes there are transforms so get the final position value like this + value = graphic.world_object.world.position.copy() + + # set value of existing feature value array + self._value[:] = value + + event = GraphicFeatureEvent(type="offset", info={"value": value}) self._call_event_handlers(event) class Rotation(GraphicFeature): - """Graphic rotation quaternion""" + property_name = "offset" + event_info_spec = [ + { + "dict key": "value", + "type": "np.ndarray[float, float, float, float]", + "description": "new rotation quaternion", + }, + ] def __init__(self, value: np.ndarray | list | tuple): + """Graphic rotation quaternion""" + self._validate(value) - self._value = np.array(value) - self._value.flags.writeable = False + # create zeros array + self._value = np.zeros(4) + + self._value[:] = value super().__init__() def _validate(self, value): @@ -79,18 +112,29 @@ def value(self) -> np.ndarray: @block_reentrance def set_value(self, graphic, value: np.ndarray | list | tuple): self._validate(value) + value = np.asarray(value) graphic.world_object.world.rotation = value - self._value = graphic.world_object.world.rotation.copy() - self._value.flags.writeable = False - event = FeatureEvent(type="rotation", info={"value": value}) + # get the actual final quaternion value, pygfx adjusts to make sure || q ||_2 == 1 + # i.e. pygfx checks to make sure norm 1 and other transforms + value = graphic.world_object.world.rotation.copy() + + # set value of existing feature value array + self._value[:] = value + + event = GraphicFeatureEvent(type="rotation", info={"value": value}) self._call_event_handlers(event) class Visible(GraphicFeature): """Access or change the visibility.""" + property_name = "offset" + event_info_spec = [ + {"dict key": "value", "type": "bool", "description": "new visibility bool"}, + ] + def __init__(self, value: bool): self._value = value super().__init__() @@ -104,7 +148,7 @@ def set_value(self, graphic, value: bool): graphic.world_object.visible = value self._value = value - event = FeatureEvent(type="visible", info={"value": value}) + event = GraphicFeatureEvent(type="visible", info={"value": value}) self._call_event_handlers(event) @@ -113,6 +157,15 @@ class Deleted(GraphicFeature): Used when a graphic is deleted, triggers events that can be useful to indicate this graphic has been deleted """ + property_name = "deleted" + event_info_spec = [ + { + "dict key": "value", + "type": "bool", + "description": "True when graphic was deleted", + }, + ] + def __init__(self, value: bool): self._value = value super().__init__() @@ -124,5 +177,5 @@ def value(self) -> bool: @block_reentrance def set_value(self, graphic, value: bool): self._value = value - event = FeatureEvent(type="deleted", info={"value": value}) + event = GraphicFeatureEvent(type="deleted", info={"value": value}) self._call_event_handlers(event) diff --git a/fastplotlib/graphics/_features/_image.py b/fastplotlib/graphics/features/_image.py similarity index 81% rename from fastplotlib/graphics/_features/_image.py rename to fastplotlib/graphics/features/_image.py index c0e2b28d2..c47a26e6a 100644 --- a/fastplotlib/graphics/_features/_image.py +++ b/fastplotlib/graphics/features/_image.py @@ -5,7 +5,7 @@ import numpy as np import pygfx -from ._base import GraphicFeature, FeatureEvent, block_reentrance +from ._base import GraphicFeature, GraphicFeatureEvent, block_reentrance from ...utils import ( make_colors, @@ -15,6 +15,19 @@ # manages an array of 8192x8192 Textures representing chunks of an image class TextureArray(GraphicFeature): + event_info_spec = [ + { + "dict key": "key", + "type": "slice, index, numpy-like fancy index", + "description": "key at which image data was sliced/fancy indexed", + }, + { + "dict key": "value", + "type": "np.ndarray | float", + "description": "new data values", + }, + ] + def __init__(self, data, isolated_buffer: bool = True): super().__init__() @@ -142,7 +155,7 @@ def __setitem__(self, key, value): for texture in self.buffer.ravel(): texture.update_range((0, 0, 0), texture.size) - event = FeatureEvent("data", info={"key": key, "value": value}) + event = GraphicFeatureEvent("data", info={"key": key, "value": value}) self._call_event_handlers(event) def __len__(self): @@ -152,6 +165,14 @@ def __len__(self): class ImageVmin(GraphicFeature): """lower contrast limit""" + event_info_spec = [ + { + "dict key": "value", + "type": "float", + "description": "new vmin value", + }, + ] + def __init__(self, value: float): self._value = value super().__init__() @@ -166,13 +187,21 @@ def set_value(self, graphic, value: float): graphic._material.clim = (value, vmax) self._value = value - event = FeatureEvent(type="vmin", info={"value": value}) + event = GraphicFeatureEvent(type="vmin", info={"value": value}) self._call_event_handlers(event) class ImageVmax(GraphicFeature): """upper contrast limit""" + event_info_spec = [ + { + "dict key": "value", + "type": "float", + "description": "new vmax value", + }, + ] + def __init__(self, value: float): self._value = value super().__init__() @@ -187,13 +216,21 @@ def set_value(self, graphic, value: float): graphic._material.clim = (vmin, value) self._value = value - event = FeatureEvent(type="vmax", info={"value": value}) + event = GraphicFeatureEvent(type="vmax", info={"value": value}) self._call_event_handlers(event) class ImageCmap(GraphicFeature): """colormap for texture""" + event_info_spec = [ + { + "dict key": "value", + "type": "str", + "description": "new cmap name", + }, + ] + def __init__(self, value: str): self._value = value self.texture = get_cmap_texture(value) @@ -210,13 +247,21 @@ def set_value(self, graphic, value: str): graphic._material.map.texture.update_range((0, 0, 0), size=(256, 1, 1)) self._value = value - event = FeatureEvent(type="cmap", info={"value": value}) + event = GraphicFeatureEvent(type="cmap", info={"value": value}) self._call_event_handlers(event) class ImageInterpolation(GraphicFeature): """Image interpolation method""" + event_info_spec = [ + { + "dict key": "value", + "type": "str", + "description": "new interpolation method, nearest | linear", + }, + ] + def __init__(self, value: str): self._validate(value) self._value = value @@ -237,13 +282,21 @@ def set_value(self, graphic, value: str): graphic._material.interpolation = value self._value = value - event = FeatureEvent(type="interpolation", info={"value": value}) + event = GraphicFeatureEvent(type="interpolation", info={"value": value}) self._call_event_handlers(event) class ImageCmapInterpolation(GraphicFeature): """Image cmap interpolation method""" + event_info_spec = [ + { + "dict key": "value", + "type": "str", + "description": "new cmap interpolatio method, nearest | linear", + }, + ] + def __init__(self, value: str): self._validate(value) self._value = value @@ -268,5 +321,5 @@ def set_value(self, graphic, value: str): graphic._material.map.mag_filter = value self._value = value - event = FeatureEvent(type="cmap_interpolation", info={"value": value}) + event = GraphicFeatureEvent(type="cmap_interpolation", info={"value": value}) self._call_event_handlers(event) diff --git a/fastplotlib/graphics/_features/_positions_graphics.py b/fastplotlib/graphics/features/_positions_graphics.py similarity index 75% rename from fastplotlib/graphics/_features/_positions_graphics.py rename to fastplotlib/graphics/features/_positions_graphics.py index 78e53f545..868701079 100644 --- a/fastplotlib/graphics/_features/_positions_graphics.py +++ b/fastplotlib/graphics/features/_positions_graphics.py @@ -9,7 +9,7 @@ from ._base import ( GraphicFeature, BufferManager, - FeatureEvent, + GraphicFeatureEvent, to_gpu_supported_dtype, block_reentrance, ) @@ -17,20 +17,24 @@ class VertexColors(BufferManager): - """ - - **info dict** - +------------+-----------------------------------------------------------+----------------------------------------------------------------------------------+ - | dict key | value type | value description | - +============+===========================================================+==================================================================================+ - | key | int | slice | np.ndarray[int | bool] | tuple[slice, ...] | key at which colors were indexed/sliced | - +------------+-----------------------------------------------------------+----------------------------------------------------------------------------------+ - | value | np.ndarray | new color values for points that were changed, shape is [n_points_changed, RGBA] | - +------------+-----------------------------------------------------------+----------------------------------------------------------------------------------+ - | user_value | str | np.ndarray | tuple[float] | list[float] | list[str] | user input value that was parsed into the RGBA array | - +------------+-----------------------------------------------------------+----------------------------------------------------------------------------------+ - - """ + property_name = "colors" + event_info_spec = [ + { + "dict key": "key", + "type": "slice, index, numpy-like fancy index", + "description": "index/slice at which colors were indexed/sliced", + }, + { + "dict key": "value", + "type": "np.ndarray [n_points_changed, RGBA]", + "description": "new color values for points that were changed", + }, + { + "dict key": "user_value", + "type": "str or array-like", + "description": "user input value that was parsed into the RGBA array", + }, + ] def __init__( self, @@ -137,18 +141,28 @@ def __setitem__( "user_value": user_value, } - event = FeatureEvent("colors", info=event_info) + event = GraphicFeatureEvent("colors", info=event_info) self._call_event_handlers(event) def __len__(self): return len(self.buffer.data) -# Manages uniform color for line or scatter material class UniformColor(GraphicFeature): + property_name = "colors" + event_info_spec = [ + { + "dict key": "value", + "type": "np.ndarray [RGBA]", + "description": "new color value", + }, + ] + def __init__( self, value: str | np.ndarray | tuple | list | pygfx.Color, alpha: float = 1.0 ): + """Manages uniform color for line or scatter material""" + v = (*tuple(pygfx.Color(value))[:-1], alpha) # apply alpha self._value = pygfx.Color(v) super().__init__() @@ -163,13 +177,19 @@ def set_value(self, graphic, value: str | np.ndarray | tuple | list | pygfx.Colo graphic.world_object.material.color = value self._value = value - event = FeatureEvent(type="colors", info={"value": value}) + event = GraphicFeatureEvent(type="colors", info={"value": value}) self._call_event_handlers(event) -# manages uniform size for scatter material class UniformSize(GraphicFeature): + property_name = "sizes" + event_info_spec = [ + {"dict key": "value", "type": "float", "description": "new size value"}, + ] + def __init__(self, value: int | float): + """Manages uniform size for scatter material""" + self._value = float(value) super().__init__() @@ -179,16 +199,27 @@ def value(self) -> float: @block_reentrance def set_value(self, graphic, value: float | int): - graphic.world_object.material.size = float(value) + value = float(value) + graphic.world_object.material.size = value self._value = value - event = FeatureEvent(type="sizes", info={"value": value}) + event = GraphicFeatureEvent(type="sizes", info={"value": value}) self._call_event_handlers(event) -# manages the coordinate space for scatter/line class SizeSpace(GraphicFeature): + property_name = "size_space" + event_info_spec = [ + { + "dict key": "value", + "type": "str", + "description": "'screen' | 'world' | 'model'", + }, + ] + def __init__(self, value: str): + """Manages the coordinate space for scatter/line graphic""" + self._value = value super().__init__() @@ -198,27 +229,35 @@ def value(self) -> str: @block_reentrance def set_value(self, graphic, value: str): + if value not in ["screen", "world", "model"]: + raise ValueError( + f"`size_space` must be one of: {['screen', 'world', 'model']}" + ) + if "Line" in graphic.world_object.material.__class__.__name__: graphic.world_object.material.thickness_space = value else: graphic.world_object.material.size_space = value self._value = value - event = FeatureEvent(type="size_space", info={"value": value}) + event = GraphicFeatureEvent(type="size_space", info={"value": value}) self._call_event_handlers(event) class VertexPositions(BufferManager): - """ - +----------+----------------------------------------------------------+------------------------------------------------------------------------------------------+ - | dict key | value type | value description | - +==========+==========================================================+==========================================================================================+ - | key | int | slice | np.ndarray[int | bool] | tuple[slice, ...] | key at which vertex positions data were indexed/sliced | - +----------+----------------------------------------------------------+------------------------------------------------------------------------------------------+ - | value | np.ndarray | float | list[float] | new data values for points that were changed, shape depends on the indices that were set | - +----------+----------------------------------------------------------+------------------------------------------------------------------------------------------+ - - """ + property_name = "data" + event_info_spec = [ + { + "dict key": "key", + "type": "slice, index (int) or numpy-like fancy index", + "description": "key at which vertex positions data were indexed/sliced", + }, + { + "dict key": "value", + "type": "int | float | array-like", + "description": "new data values for points that were changed", + }, + ] def __init__(self, data: Any, isolated_buffer: bool = True): """ @@ -268,15 +307,19 @@ def __len__(self): class PointsSizesFeature(BufferManager): - """ - +----------+-------------------------------------------------------------------+----------------------------------------------+ - | dict key | value type | value description | - +==========+===================================================================+==============================================+ - | key | int | slice | np.ndarray[int | bool] | list[int | bool] | key at which point sizes indexed/sliced | - +----------+-------------------------------------------------------------------+----------------------------------------------+ - | value | int | float | np.ndarray | list[int | float] | tuple[int | float] | new size values for points that were changed | - +----------+-------------------------------------------------------------------+----------------------------------------------+ - """ + property_name = "sizes" + event_info_spec = [ + { + "dict key": "key", + "type": "slice, index (int) or numpy-like fancy index", + "description": "key at which point sizes were indexed/sliced", + }, + { + "dict key": "value", + "type": "int | float | array-like", + "description": "new size values for points that were changed", + }, + ] def __init__( self, @@ -341,7 +384,10 @@ def __len__(self): class Thickness(GraphicFeature): - """line thickness""" + property_name = "thickness" + event_info_spec = [ + {"dict key": "value", "type": "float", "description": "new thickness value"}, + ] def __init__(self, value: float): self._value = value @@ -353,18 +399,28 @@ def value(self) -> float: @block_reentrance def set_value(self, graphic, value: float): + value = float(value) graphic.world_object.material.thickness = value self._value = value - event = FeatureEvent(type="thickness", info={"value": value}) + event = GraphicFeatureEvent(type="thickness", info={"value": value}) self._call_event_handlers(event) class VertexCmap(BufferManager): - """ - Sliceable colormap feature, manages a VertexColors instance and - provides a way to set colormaps with arbitrary transforms - """ + property_name = "cmap" + event_info_spec = [ + { + "dict key": "key", + "type": "slice", + "description": "key at cmap colors were sliced", + }, + { + "dict key": "value", + "type": "str", + "description": "new cmap to set at given slice", + }, + ] def __init__( self, @@ -373,6 +429,11 @@ def __init__( transform: np.ndarray | None, alpha: float = 1.0, ): + """ + Sliceable colormap feature, manages a VertexColors instance and + provides a way to set colormaps with arbitrary transforms + """ + super().__init__(data=vertex_colors.buffer) self._vertex_colors = vertex_colors @@ -405,12 +466,12 @@ def __setitem__(self, key: slice, cmap_name): if not isinstance(key, slice): raise TypeError( "fancy indexing not supported for VertexCmap, only slices " - "of a continuous are supported for apply a cmap" + "of a continuous range are supported for applying a cmap" ) if key.step is not None: raise TypeError( "step sized indexing not currently supported for setting VertexCmap, " - "slices must be a continuous region" + "slices must be a continuous range" ) # parse slice diff --git a/fastplotlib/graphics/_features/_selection_features.py b/fastplotlib/graphics/features/_selection_features.py similarity index 74% rename from fastplotlib/graphics/_features/_selection_features.py rename to fastplotlib/graphics/features/_selection_features.py index c157023b4..233353401 100644 --- a/fastplotlib/graphics/_features/_selection_features.py +++ b/fastplotlib/graphics/features/_selection_features.py @@ -3,28 +3,25 @@ import numpy as np from ...utils import mesh_masks -from ._base import GraphicFeature, FeatureEvent, block_reentrance +from ._base import GraphicFeature, GraphicFeatureEvent, block_reentrance class LinearSelectionFeature(GraphicFeature): - """ - **additional event attributes:** - - +--------------------+----------+------------------------------------+ - | attribute | type | description | - +====================+==========+====================================+ - | get_selected_index | callable | returns indices under the selector | - +--------------------+----------+------------------------------------+ - - **info dict:** - - +----------+------------+-------------------------------+ - | dict key | value type | value description | - +==========+============+===============================+ - | value | np.ndarray | new x or y value of selection | - +----------+------------+-------------------------------+ - - """ + event_info_spec = [ + { + "dict key": "value", + "type": "float", + "description": "new x or y value of selection", + }, + ] + + event_extra_attrs = [ + { + "attribute": "get_selected_index", + "type": "callable", + "description": "returns index under the selector", + } + ] def __init__(self, axis: str, value: float, limits: tuple[float, float]): """ @@ -71,33 +68,33 @@ def set_value(self, selector, value: float): self._value = value - event = FeatureEvent("selection", {"value": value}) + event = GraphicFeatureEvent("selection", {"value": value}) event.get_selected_index = selector.get_selected_index self._call_event_handlers(event) class LinearRegionSelectionFeature(GraphicFeature): - """ - **additional event attributes:** - - +----------------------+----------+------------------------------------+ - | attribute | type | description | - +======================+==========+====================================+ - | get_selected_indices | callable | returns indices under the selector | - +----------------------+----------+------------------------------------+ - | get_selected_data | callable | returns data under the selector | - +----------------------+----------+------------------------------------+ - - **info dict:** - - +----------+------------+-----------------------------+ - | dict key | value type | value description | - +==========+============+=============================+ - | value | np.ndarray | new [min, max] of selection | - +----------+------------+-----------------------------+ - - """ + event_info_spec = [ + { + "dict key": "value", + "type": "np.ndarray", + "description": "new [min, max] of selection", + }, + ] + + event_extra_attrs = [ + { + "attribute": "get_selected_indices", + "type": "callable", + "description": "returns indices under the selector", + }, + { + "attribute": "get_selected_data", + "type": "callable", + "description": "returns data under the selector", + }, + ] def __init__(self, value: tuple[int, int], axis: str, limits: tuple[float, float]): super().__init__() @@ -183,7 +180,7 @@ def set_value(self, selector, value: Sequence[float]): if len(self._event_handlers) < 1: return - event = FeatureEvent("selection", {"value": self.value}) + event = GraphicFeatureEvent("selection", {"value": self.value}) event.get_selected_indices = selector.get_selected_indices event.get_selected_data = selector.get_selected_data @@ -195,26 +192,26 @@ def set_value(self, selector, value: Sequence[float]): class RectangleSelectionFeature(GraphicFeature): - """ - **additional event attributes:** - - +----------------------+----------+------------------------------------+ - | attribute | type | description | - +======================+==========+====================================+ - | get_selected_indices | callable | returns indices under the selector | - +----------------------+----------+------------------------------------+ - | get_selected_data | callable | returns data under the selector | - +----------------------+----------+------------------------------------+ - - **info dict:** - - +----------+------------+-------------------------------------------+ - | dict key | value type | value description | - +==========+============+===========================================+ - | value | np.ndarray | new [xmin, xmax, ymin, ymax] of selection | - +----------+------------+-------------------------------------------+ - - """ + event_info_spec = [ + { + "dict key": "value", + "type": "np.ndarray", + "description": "new [xmin, xmax, ymin, ymax] of selection", + }, + ] + + event_extra_attrs = [ + { + "attribute": "get_selected_indices", + "type": "callable", + "description": "returns indices under the selector", + }, + { + "attribute": "get_selected_data", + "type": "callable", + "description": "returns data under the selector", + }, + ] def __init__( self, @@ -336,7 +333,7 @@ def set_value(self, selector, value: Sequence[float]): if len(self._event_handlers) < 1: return - event = FeatureEvent("selection", {"value": self.value}) + event = GraphicFeatureEvent("selection", {"value": self.value}) event.get_selected_indices = selector.get_selected_indices event.get_selected_data = selector.get_selected_data diff --git a/fastplotlib/graphics/_features/_text.py b/fastplotlib/graphics/features/_text.py similarity index 65% rename from fastplotlib/graphics/_features/_text.py rename to fastplotlib/graphics/features/_text.py index a95fe256c..d8e5e95e8 100644 --- a/fastplotlib/graphics/_features/_text.py +++ b/fastplotlib/graphics/features/_text.py @@ -2,10 +2,18 @@ import pygfx -from ._base import GraphicFeature, FeatureEvent, block_reentrance +from ._base import GraphicFeature, GraphicFeatureEvent, block_reentrance class TextData(GraphicFeature): + event_info_spec = [ + { + "dict key": "value", + "type": "str", + "description": "new text data", + }, + ] + def __init__(self, value: str): self._value = value super().__init__() @@ -19,11 +27,19 @@ def set_value(self, graphic, value: str): graphic.world_object.set_text(value) self._value = value - event = FeatureEvent(type="text", info={"value": value}) + event = GraphicFeatureEvent(type="text", info={"value": value}) self._call_event_handlers(event) class FontSize(GraphicFeature): + event_info_spec = [ + { + "dict key": "value", + "type": "float | int", + "description": "new font size", + }, + ] + def __init__(self, value: float | int): self._value = value super().__init__() @@ -37,11 +53,19 @@ def set_value(self, graphic, value: float | int): graphic.world_object.font_size = value self._value = graphic.world_object.font_size - event = FeatureEvent(type="font_size", info={"value": value}) + event = GraphicFeatureEvent(type="font_size", info={"value": value}) self._call_event_handlers(event) class TextFaceColor(GraphicFeature): + event_info_spec = [ + { + "dict key": "value", + "type": "str | np.ndarray", + "description": "new text color", + }, + ] + def __init__(self, value: str | np.ndarray | list[float] | tuple[float]): self._value = pygfx.Color(value) super().__init__() @@ -56,11 +80,19 @@ def set_value(self, graphic, value: str | np.ndarray | list[float] | tuple[float graphic.world_object.material.color = value self._value = graphic.world_object.material.color - event = FeatureEvent(type="face_color", info={"value": value}) + event = GraphicFeatureEvent(type="face_color", info={"value": value}) self._call_event_handlers(event) class TextOutlineColor(GraphicFeature): + event_info_spec = [ + { + "dict key": "value", + "type": "str | np.ndarray", + "description": "new outline color", + }, + ] + def __init__(self, value: str | np.ndarray | list[float] | tuple[float]): self._value = pygfx.Color(value) super().__init__() @@ -75,11 +107,19 @@ def set_value(self, graphic, value: str | np.ndarray | list[float] | tuple[float graphic.world_object.material.outline_color = value self._value = graphic.world_object.material.outline_color - event = FeatureEvent(type="outline_color", info={"value": value}) + event = GraphicFeatureEvent(type="outline_color", info={"value": value}) self._call_event_handlers(event) class TextOutlineThickness(GraphicFeature): + event_info_spec = [ + { + "dict key": "value", + "type": "float", + "description": "new text outline thickness", + }, + ] + def __init__(self, value: float): self._value = value super().__init__() @@ -93,5 +133,5 @@ def set_value(self, graphic, value: float): graphic.world_object.material.outline_thickness = value self._value = graphic.world_object.material.outline_thickness - event = FeatureEvent(type="outline_thickness", info={"value": value}) + event = GraphicFeatureEvent(type="outline_thickness", info={"value": value}) self._call_event_handlers(event) diff --git a/fastplotlib/graphics/_features/utils.py b/fastplotlib/graphics/features/utils.py similarity index 100% rename from fastplotlib/graphics/_features/utils.py rename to fastplotlib/graphics/features/utils.py diff --git a/fastplotlib/graphics/image.py b/fastplotlib/graphics/image.py index 8b937023b..5f198c84f 100644 --- a/fastplotlib/graphics/image.py +++ b/fastplotlib/graphics/image.py @@ -6,7 +6,7 @@ from ..utils import quick_min_max from ._base import Graphic from .selectors import LinearSelector, LinearRegionSelector, RectangleSelector -from ._features import ( +from .features import ( TextureArray, ImageCmap, ImageVmin, @@ -71,7 +71,14 @@ def chunk_index(self) -> tuple[int, int]: class ImageGraphic(Graphic): - _features = {"data", "cmap", "vmin", "vmax", "interpolation", "cmap_interpolation"} + _features = { + "data": TextureArray, + "cmap": ImageCmap, + "vmin": ImageVmin, + "vmax": ImageVmax, + "interpolation": ImageInterpolation, + "cmap_interpolation": ImageCmapInterpolation, + } def __init__( self, diff --git a/fastplotlib/graphics/line.py b/fastplotlib/graphics/line.py index 489c64930..d02297c64 100644 --- a/fastplotlib/graphics/line.py +++ b/fastplotlib/graphics/line.py @@ -6,11 +6,25 @@ from ._positions_base import PositionsGraphic from .selectors import LinearRegionSelector, LinearSelector, RectangleSelector -from ._features import Thickness, SizeSpace +from .features import ( + Thickness, + VertexPositions, + VertexColors, + UniformColor, + VertexCmap, + SizeSpace, +) +from ..utils import quick_min_max class LineGraphic(PositionsGraphic): - _features = {"data", "colors", "cmap", "thickness", "size_space"} + _features = { + "data": VertexPositions, + "colors": (VertexColors, UniformColor), + "cmap": (VertexCmap, None), # none if UniformColor + "thickness": Thickness, + "size_space": SizeSpace, + } def __init__( self, @@ -298,6 +312,6 @@ def _get_linear_selector_init_args( size = int(np.ptp(magn_vals) * 1.5 + padding) # center of selector along the other axis - center = np.nanmean(magn_vals) + center = sum(quick_min_max(magn_vals)) / 2 return bounds_init, limits, size, center diff --git a/fastplotlib/graphics/scatter.py b/fastplotlib/graphics/scatter.py index 189af4844..a8479bbf6 100644 --- a/fastplotlib/graphics/scatter.py +++ b/fastplotlib/graphics/scatter.py @@ -4,11 +4,25 @@ import pygfx from ._positions_base import PositionsGraphic -from ._features import PointsSizesFeature, UniformSize, SizeSpace +from .features import ( + PointsSizesFeature, + UniformSize, + SizeSpace, + VertexPositions, + VertexColors, + UniformColor, + VertexCmap, +) class ScatterGraphic(PositionsGraphic): - _features = {"data", "sizes", "colors", "cmap", "size_space"} + _features = { + "data": VertexPositions, + "sizes": (PointsSizesFeature, UniformSize), + "colors": (VertexColors, UniformColor), + "cmap": (VertexCmap, None), + "size_space": SizeSpace, + } def __init__( self, diff --git a/fastplotlib/graphics/selectors/_base_selector.py b/fastplotlib/graphics/selectors/_base_selector.py index 5158a9239..629c063bc 100644 --- a/fastplotlib/graphics/selectors/_base_selector.py +++ b/fastplotlib/graphics/selectors/_base_selector.py @@ -35,8 +35,6 @@ class MoveInfo: # Selector base class class BaseSelector(Graphic): - _features = {"selection"} - @property def axis(self) -> str: return self._axis diff --git a/fastplotlib/graphics/selectors/_linear.py b/fastplotlib/graphics/selectors/_linear.py index fe57036a3..7ac0fc761 100644 --- a/fastplotlib/graphics/selectors/_linear.py +++ b/fastplotlib/graphics/selectors/_linear.py @@ -7,11 +7,13 @@ from .._base import Graphic from .._collection_base import GraphicCollection -from .._features._selection_features import LinearSelectionFeature +from ..features._selection_features import LinearSelectionFeature from ._base_selector import BaseSelector class LinearSelector(BaseSelector): + _features = {"selection": LinearSelectionFeature} + @property def parent(self) -> Graphic: return self._parent diff --git a/fastplotlib/graphics/selectors/_linear_region.py b/fastplotlib/graphics/selectors/_linear_region.py index c1e6095f8..1bc3efc2c 100644 --- a/fastplotlib/graphics/selectors/_linear_region.py +++ b/fastplotlib/graphics/selectors/_linear_region.py @@ -6,11 +6,13 @@ from .._base import Graphic from .._collection_base import GraphicCollection -from .._features._selection_features import LinearRegionSelectionFeature +from ..features._selection_features import LinearRegionSelectionFeature from ._base_selector import BaseSelector class LinearRegionSelector(BaseSelector): + _features = {"selection": LinearRegionSelectionFeature} + @property def parent(self) -> Graphic | None: """graphic that the selector is associated with""" diff --git a/fastplotlib/graphics/selectors/_rectangle.py b/fastplotlib/graphics/selectors/_rectangle.py index 51c3209b1..8d0af8e88 100644 --- a/fastplotlib/graphics/selectors/_rectangle.py +++ b/fastplotlib/graphics/selectors/_rectangle.py @@ -7,11 +7,13 @@ from .._collection_base import GraphicCollection from .._base import Graphic -from .._features import RectangleSelectionFeature +from ..features import RectangleSelectionFeature from ._base_selector import BaseSelector class RectangleSelector(BaseSelector): + _features = {"selection": RectangleSelectionFeature} + @property def parent(self) -> Graphic | None: """Graphic that selector is associated with.""" diff --git a/fastplotlib/graphics/text.py b/fastplotlib/graphics/text.py index e3794743a..70f9b2a43 100644 --- a/fastplotlib/graphics/text.py +++ b/fastplotlib/graphics/text.py @@ -2,7 +2,7 @@ import numpy as np from ._base import Graphic -from ._features import ( +from .features import ( TextData, FontSize, TextFaceColor, @@ -13,11 +13,11 @@ class TextGraphic(Graphic): _features = { - "text", - "font_size", - "face_color", - "outline_color", - "outline_thickness", + "text": TextData, + "font_size": FontSize, + "face_color": TextFaceColor, + "outline_color": TextOutlineColor, + "outline_thickness": TextOutlineThickness, } def __init__( diff --git a/fastplotlib/layouts/__init__.py b/fastplotlib/layouts/__init__.py index 8fb1d54d8..23839586c 100644 --- a/fastplotlib/layouts/__init__.py +++ b/fastplotlib/layouts/__init__.py @@ -1,4 +1,5 @@ from ._figure import Figure +from ._subplot import Subplot from ._utils import IMGUI if IMGUI: diff --git a/fastplotlib/legends/legend.py b/fastplotlib/legends/legend.py index df78d5662..69a556109 100644 --- a/fastplotlib/legends/legend.py +++ b/fastplotlib/legends/legend.py @@ -5,8 +5,8 @@ import numpy as np import pygfx -from ..graphics._base import Graphic -from ..graphics._features._base import FeatureEvent +from ..graphics import Graphic +from ..graphics.features import GraphicFeatureEvent from ..graphics import LineGraphic, ScatterGraphic, ImageGraphic from ..utils import mesh_masks @@ -116,7 +116,7 @@ def label(self, text: str): self._parent._check_label_unique(text) self._label_world_object.geometry.set_text(text) - def _update_color(self, ev: FeatureEvent): + def _update_color(self, ev: GraphicFeatureEvent): new_color = ev.info["value"] if np.unique(new_color, axis=0).shape[0] > 1: raise ValueError( diff --git a/fastplotlib/utils/_plot_helpers.py b/fastplotlib/utils/_plot_helpers.py index 5a39b76d0..12afe1cb2 100644 --- a/fastplotlib/utils/_plot_helpers.py +++ b/fastplotlib/utils/_plot_helpers.py @@ -36,10 +36,12 @@ def get_nearest_graphics_indices( if not all(isinstance(g, Graphic) for g in graphics): raise TypeError("all elements of `graphics` must be Graphic objects") - pos = np.asarray(pos) + pos = np.asarray(pos).ravel() - if pos.shape != (2,) or not pos.shape != (3,): - raise TypeError + if pos.shape != (2,) and pos.shape != (3,): + raise TypeError( + f"pos.shape must be (2,) or (3,), the shape of pos you have passed is: {pos.shape}" + ) # get centers centers = np.empty(shape=(len(graphics), len(pos))) diff --git a/tests/events.py b/tests/events.py index ea160dec3..e9b212adb 100644 --- a/tests/events.py +++ b/tests/events.py @@ -5,7 +5,7 @@ import pygfx import fastplotlib as fpl -from fastplotlib.graphics._features import FeatureEvent +from fastplotlib.graphics.features import GraphicFeatureEvent def make_positions_data() -> np.ndarray: @@ -22,7 +22,7 @@ def make_scatter_graphic() -> fpl.ScatterGraphic: return fpl.ScatterGraphic(make_positions_data()) -event_instance: FeatureEvent = None +event_instance: GraphicFeatureEvent = None def event_handler(event): @@ -30,7 +30,7 @@ def event_handler(event): event_instance = event -decorated_event_instance: FeatureEvent = None +decorated_event_instance: GraphicFeatureEvent = None @pytest.mark.parametrize("graphic", [make_line_graphic(), make_scatter_graphic()]) @@ -42,7 +42,7 @@ def test_positions_data_event(graphic: fpl.LineGraphic | fpl.ScatterGraphic): info = {"key": (slice(3, 8, None), 1), "value": value} - expected = FeatureEvent(type="data", info=info) + expected = GraphicFeatureEvent(type="data", info=info) def validate(graphic, handler, expected_feature_event, event_to_test): assert expected_feature_event.type == event_to_test.type diff --git a/tests/test_colors_buffer_manager.py b/tests/test_colors_buffer_manager.py index 8a6c5700f..7b1aef16a 100644 --- a/tests/test_colors_buffer_manager.py +++ b/tests/test_colors_buffer_manager.py @@ -5,7 +5,7 @@ import pygfx import fastplotlib as fpl -from fastplotlib.graphics._features import VertexColors, FeatureEvent +from fastplotlib.graphics.features import VertexColors, GraphicFeatureEvent from .utils import ( generate_slice_indices, generate_color_inputs, @@ -18,7 +18,7 @@ def make_colors_buffer() -> VertexColors: return colors -EVENT_RETURN_VALUE: FeatureEvent = None +EVENT_RETURN_VALUE: GraphicFeatureEvent = None def event_handler(ev): @@ -65,7 +65,7 @@ def test_int(test_graphic): if test_graphic: # test event - assert isinstance(EVENT_RETURN_VALUE, FeatureEvent) + assert isinstance(EVENT_RETURN_VALUE, GraphicFeatureEvent) assert EVENT_RETURN_VALUE.graphic == graphic assert EVENT_RETURN_VALUE.target is graphic.world_object assert EVENT_RETURN_VALUE.info["key"] == 3 @@ -120,7 +120,7 @@ def test_tuple(test_graphic, slice_method): if test_graphic: # test event - assert isinstance(EVENT_RETURN_VALUE, FeatureEvent) + assert isinstance(EVENT_RETURN_VALUE, GraphicFeatureEvent) assert EVENT_RETURN_VALUE.graphic == graphic assert EVENT_RETURN_VALUE.target is graphic.world_object assert EVENT_RETURN_VALUE.info["key"] == (s, slice(None)) @@ -142,7 +142,7 @@ def test_tuple(test_graphic, slice_method): if test_graphic: # test event - assert isinstance(EVENT_RETURN_VALUE, FeatureEvent) + assert isinstance(EVENT_RETURN_VALUE, GraphicFeatureEvent) assert EVENT_RETURN_VALUE.graphic == graphic assert EVENT_RETURN_VALUE.target is graphic.world_object assert EVENT_RETURN_VALUE.info["key"] == slice(None) @@ -218,7 +218,7 @@ def test_slice(color_input, slice_method: dict, test_graphic: bool): if test_graphic: global EVENT_RETURN_VALUE - assert isinstance(EVENT_RETURN_VALUE, FeatureEvent) + assert isinstance(EVENT_RETURN_VALUE, GraphicFeatureEvent) assert EVENT_RETURN_VALUE.graphic == graphic assert EVENT_RETURN_VALUE.target is graphic.world_object if isinstance(s, slice): diff --git a/tests/test_common_features.py b/tests/test_common_features.py index 332ac71ae..5671478a7 100644 --- a/tests/test_common_features.py +++ b/tests/test_common_features.py @@ -4,7 +4,7 @@ import pytest import fastplotlib as fpl -from fastplotlib.graphics._features import FeatureEvent, Name, Offset, Rotation, Visible +from fastplotlib.graphics.features import GraphicFeatureEvent, Name, Offset, Rotation, Visible def make_graphic(kind: str, **kwargs): @@ -29,11 +29,11 @@ def make_graphic(kind: str, **kwargs): ] -RETURN_EVENT_VALUE: FeatureEvent = None -DECORATED_EVENT_VALUE: FeatureEvent = None +RETURN_EVENT_VALUE: GraphicFeatureEvent = None +DECORATED_EVENT_VALUE: GraphicFeatureEvent = None -def return_event(ev: FeatureEvent): +def return_event(ev: GraphicFeatureEvent): global RETURN_EVENT_VALUE RETURN_EVENT_VALUE = ev @@ -138,7 +138,7 @@ def decorated_handler(ev): assert DECORATED_EVENT_VALUE.type == "offset" assert DECORATED_EVENT_VALUE.graphic is graphic assert DECORATED_EVENT_VALUE.target is graphic.world_object - assert DECORATED_EVENT_VALUE.info["value"] == (7.0, 8.0, 9.0) + npt.assert_almost_equal(DECORATED_EVENT_VALUE.info["value"], (7.0, 8.0, 9.0)) @pytest.mark.parametrize( @@ -202,7 +202,7 @@ def decorated_handler(ev): assert DECORATED_EVENT_VALUE.type == "rotation" assert DECORATED_EVENT_VALUE.graphic is graphic assert DECORATED_EVENT_VALUE.target is graphic.world_object - assert DECORATED_EVENT_VALUE.info["value"] == (0, 0, 0.6, 0.8) + npt.assert_almost_equal(DECORATED_EVENT_VALUE.info["value"], (0, 0, 0.6, 0.8)) @pytest.mark.parametrize( diff --git a/tests/test_image_graphic.py b/tests/test_image_graphic.py index 02b982d80..f2d87860b 100644 --- a/tests/test_image_graphic.py +++ b/tests/test_image_graphic.py @@ -5,7 +5,7 @@ import pygfx import fastplotlib as fpl -from fastplotlib.graphics._features import FeatureEvent +from fastplotlib.graphics.features import GraphicFeatureEvent from fastplotlib.utils import make_colors GRAY_IMAGE = iio.imread("imageio:camera.png") @@ -18,7 +18,7 @@ # new screenshot tests too for these when in graphics -EVENT_RETURN_VALUE: FeatureEvent = None +EVENT_RETURN_VALUE: GraphicFeatureEvent = None def event_handler(ev): @@ -28,7 +28,7 @@ def event_handler(ev): def check_event(graphic, feature, value): global EVENT_RETURN_VALUE - assert isinstance(EVENT_RETURN_VALUE, FeatureEvent) + assert isinstance(EVENT_RETURN_VALUE, GraphicFeatureEvent) assert EVENT_RETURN_VALUE.type == feature assert EVENT_RETURN_VALUE.graphic == graphic assert EVENT_RETURN_VALUE.target == graphic.world_object @@ -58,7 +58,7 @@ def check_set_slice( npt.assert_almost_equal(data_values[:, col_slice.stop :], data[:, col_slice.stop :]) global EVENT_RETURN_VALUE - assert isinstance(EVENT_RETURN_VALUE, FeatureEvent) + assert isinstance(EVENT_RETURN_VALUE, GraphicFeatureEvent) assert EVENT_RETURN_VALUE.type == "data" assert EVENT_RETURN_VALUE.graphic == image_graphic assert EVENT_RETURN_VALUE.target == image_graphic.world_object diff --git a/tests/test_positions_data_buffer_manager.py b/tests/test_positions_data_buffer_manager.py index 77d049ab5..18a7b36e8 100644 --- a/tests/test_positions_data_buffer_manager.py +++ b/tests/test_positions_data_buffer_manager.py @@ -3,14 +3,14 @@ import pytest import fastplotlib as fpl -from fastplotlib.graphics._features import VertexPositions, FeatureEvent +from fastplotlib.graphics.features import VertexPositions, GraphicFeatureEvent from .utils import ( generate_slice_indices, generate_positions_spiral_data, ) -EVENT_RETURN_VALUE: FeatureEvent = None +EVENT_RETURN_VALUE: GraphicFeatureEvent = None def event_handler(ev): @@ -72,7 +72,7 @@ def test_int(test_graphic): # check event if test_graphic: - assert isinstance(EVENT_RETURN_VALUE, FeatureEvent) + assert isinstance(EVENT_RETURN_VALUE, GraphicFeatureEvent) assert EVENT_RETURN_VALUE.graphic == graphic assert EVENT_RETURN_VALUE.target is graphic.world_object assert EVENT_RETURN_VALUE.info["key"] == 2 @@ -87,7 +87,7 @@ def test_int(test_graphic): # check event if test_graphic: - assert isinstance(EVENT_RETURN_VALUE, FeatureEvent) + assert isinstance(EVENT_RETURN_VALUE, GraphicFeatureEvent) assert EVENT_RETURN_VALUE.graphic == graphic assert EVENT_RETURN_VALUE.target is graphic.world_object assert EVENT_RETURN_VALUE.info["key"] == slice(None) @@ -148,7 +148,7 @@ def test_slice(test_graphic, slice_method: dict, test_axis: str): # check event if test_graphic: - assert isinstance(EVENT_RETURN_VALUE, FeatureEvent) + assert isinstance(EVENT_RETURN_VALUE, GraphicFeatureEvent) assert EVENT_RETURN_VALUE.graphic == graphic assert EVENT_RETURN_VALUE.target is graphic.world_object if isinstance(s, slice): @@ -172,7 +172,7 @@ def test_slice(test_graphic, slice_method: dict, test_axis: str): # check event if test_graphic: - assert isinstance(EVENT_RETURN_VALUE, FeatureEvent) + assert isinstance(EVENT_RETURN_VALUE, GraphicFeatureEvent) assert EVENT_RETURN_VALUE.graphic == graphic assert EVENT_RETURN_VALUE.target is graphic.world_object if isinstance(s, slice): @@ -191,7 +191,7 @@ def test_slice(test_graphic, slice_method: dict, test_axis: str): # check event if test_graphic: - assert isinstance(EVENT_RETURN_VALUE, FeatureEvent) + assert isinstance(EVENT_RETURN_VALUE, GraphicFeatureEvent) assert EVENT_RETURN_VALUE.graphic == graphic assert EVENT_RETURN_VALUE.target is graphic.world_object if isinstance(s, slice): diff --git a/tests/test_positions_graphics.py b/tests/test_positions_graphics.py index b76ece2ca..ed791b6fa 100644 --- a/tests/test_positions_graphics.py +++ b/tests/test_positions_graphics.py @@ -5,7 +5,7 @@ import pygfx import fastplotlib as fpl -from fastplotlib.graphics._features import ( +from fastplotlib.graphics.features import ( VertexPositions, VertexColors, VertexCmap, @@ -13,7 +13,7 @@ UniformSize, PointsSizesFeature, Thickness, - FeatureEvent, + GraphicFeatureEvent, ) from .utils import ( @@ -58,7 +58,7 @@ } -EVENT_RETURN_VALUE: FeatureEvent = None +EVENT_RETURN_VALUE: GraphicFeatureEvent = None def event_handler(ev): diff --git a/tests/test_sizes_buffer_manager.py b/tests/test_sizes_buffer_manager.py index 1d0a17f3d..2f55eab27 100644 --- a/tests/test_sizes_buffer_manager.py +++ b/tests/test_sizes_buffer_manager.py @@ -2,7 +2,7 @@ from numpy import testing as npt import pytest -from fastplotlib.graphics._features import PointsSizesFeature +from fastplotlib.graphics.features import PointsSizesFeature from .utils import generate_slice_indices diff --git a/tests/test_text_graphic.py b/tests/test_text_graphic.py index deb25ca6b..ec3d0be54 100644 --- a/tests/test_text_graphic.py +++ b/tests/test_text_graphic.py @@ -1,8 +1,8 @@ from numpy import testing as npt import fastplotlib as fpl -from fastplotlib.graphics._features import ( - FeatureEvent, +from fastplotlib.graphics.features import ( + GraphicFeatureEvent, TextData, FontSize, TextFaceColor, @@ -40,7 +40,7 @@ def test_create_graphic(): assert text.world_object.material.outline_thickness == 0 -EVENT_RETURN_VALUE: FeatureEvent = None +EVENT_RETURN_VALUE: GraphicFeatureEvent = None def event_handler(ev): @@ -50,7 +50,7 @@ def event_handler(ev): def check_event(graphic, feature, value): global EVENT_RETURN_VALUE - assert isinstance(EVENT_RETURN_VALUE, FeatureEvent) + assert isinstance(EVENT_RETURN_VALUE, GraphicFeatureEvent) assert EVENT_RETURN_VALUE.type == feature assert EVENT_RETURN_VALUE.graphic == graphic assert EVENT_RETURN_VALUE.target == graphic.world_object diff --git a/tests/test_texture_array.py b/tests/test_texture_array.py index c85fc7652..6220f2fe5 100644 --- a/tests/test_texture_array.py +++ b/tests/test_texture_array.py @@ -5,7 +5,7 @@ import pygfx import fastplotlib as fpl -from fastplotlib.graphics._features import TextureArray +from fastplotlib.graphics.features import TextureArray from fastplotlib.graphics.image import _ImageTile From 718c98c3007f20e3bc56e0c14a52c8f35303f01c Mon Sep 17 00:00:00 2001 From: Flynn <75346097+FlynnOConnell@users.noreply.github.com> Date: Thu, 10 Apr 2025 14:34:08 -0400 Subject: [PATCH 170/185] optimized array subsampling (#721) * add 4D case with a single z-plane * add subsample_array function * use new subsample_array function * remove redundant target_elements from subsample * import utils from __all__ * rename subsample vars using standard numpy names * subsample max_items -> max_size * largest bytesize -> largest array size, lowercase errrrything * use subsample_array in quick_min_max * make 1e6 int * update png from artifact (imgui-screenshots) * fix subsample array * bring back original image_widget_grid * Update functions.py * revert image_widget_grid from regenerate * hopefully the correct screenshot * black * replace iw-zfish-grid-qreplace nb-iw-zfish-grid-init-mw5 to see * replace nb-image-widget screenshots * force git to refresh * nb-iw from regen screenshots * plzplzplzplzplz work --------- Co-authored-by: Kushal Kolar --- .../nb-image-widget-movie-single-0-reset.png | 4 +- .../nb-image-widget-movie-single-0.png | 4 +- .../nb-image-widget-movie-single-279.png | 4 +- ...e-widget-movie-single-50-window-max-33.png | 4 +- ...-widget-movie-single-50-window-mean-13.png | 4 +- ...-widget-movie-single-50-window-mean-33.png | 4 +- ...ge-widget-movie-single-50-window-reset.png | 4 +- .../nb-image-widget-movie-single-50.png | 4 +- ...et-zfish-frame-50-frame-apply-gaussian.png | 4 +- ...idget-zfish-frame-50-frame-apply-reset.png | 4 +- ...ge-widget-zfish-frame-50-max-window-13.png | 4 +- ...e-widget-zfish-frame-50-mean-window-13.png | 4 +- ...ge-widget-zfish-frame-50-mean-window-5.png | 4 +- .../nb-image-widget-zfish-frame-50.png | 4 +- .../nb-image-widget-zfish-frame-99.png | 4 +- ...ish-grid-frame-50-frame-apply-gaussian.png | 4 +- ...-zfish-grid-frame-50-frame-apply-reset.png | 4 +- ...dget-zfish-grid-frame-50-max-window-13.png | 4 +- ...get-zfish-grid-frame-50-mean-window-13.png | 4 +- ...dget-zfish-grid-frame-50-mean-window-5.png | 4 +- .../nb-image-widget-zfish-grid-frame-50.png | 4 +- .../nb-image-widget-zfish-grid-frame-99.png | 4 +- ...e-widget-zfish-grid-init-mean-window-5.png | 4 +- ...fish-grid-set_data-reset-indices-false.png | 4 +- ...zfish-grid-set_data-reset-indices-true.png | 4 +- ...-image-widget-zfish-init-mean-window-5.png | 4 +- ...dget-zfish-mixed-rgb-cockatoo-frame-50.png | 4 +- ...dget-zfish-mixed-rgb-cockatoo-set-data.png | 4 +- ...get-zfish-mixed-rgb-cockatoo-windowrgb.png | 4 +- examples/screenshots/image_widget_grid.png | 5 +- fastplotlib/tools/_histogram_lut.py | 25 ++----- fastplotlib/utils/functions.py | 70 +++++++++++++++++-- 32 files changed, 129 insertions(+), 87 deletions(-) diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-0-reset.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-0-reset.png index bb2e1ee37..0129cb423 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-0-reset.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-0-reset.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6c9b898259fc965452ef0b6ff53ac7fa41196826c6e27b6b5d417d33fb352051 -size 112399 +oid sha256:d3f5a721456b5a54e819fc987b8fa1f61d638f578339a7332ad46a22e7aa8fc0 +size 112674 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-0.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-0.png index bb2e1ee37..0129cb423 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-0.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6c9b898259fc965452ef0b6ff53ac7fa41196826c6e27b6b5d417d33fb352051 -size 112399 +oid sha256:d3f5a721456b5a54e819fc987b8fa1f61d638f578339a7332ad46a22e7aa8fc0 +size 112674 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-279.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-279.png index 1841cd237..4908c8b59 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-279.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-279.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b9cbc2a6916c7518d40812a13276270eb1acfc596f3e6e02e98a6a5185da03a4 -size 132971 +oid sha256:4511a28e728af412f5006bb456f133aea1fdc9c1922c3174f127c79d9878401d +size 133635 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-max-33.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-max-33.png index 6cc1821fa..cfdc3c8a9 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-max-33.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-max-33.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:070748e90bd230a01d3ae7c6d6487815926b0158888a52db272356dc8b0a89d7 -size 119453 +oid sha256:c6910106cd799a4327a6650edbc956ddb9b6a489760b86b279c593575ae805b8 +size 120114 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-13.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-13.png index 3865aef93..92513cf5b 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b24450ccf1f8cf902b8e37e73907186f37a6495f227dcbd5ec53f75c52125f56 -size 105213 +oid sha256:8233dfc429a7fefe96f0fdb89eb2c57188b7963c16db5d1d08f7faefb45d8cb7 +size 105755 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-33.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-33.png index 025086930..8bce59baf 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-33.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-mean-33.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3dfc8e978eddf08d1ed32e16fbf93c037ccdf5f7349180dcda54578a8c9e1a18 -size 97359 +oid sha256:a4af684cdaec8f98081862eb8a377cd419efec64cdf08b662a456276b78f1fb5 +size 98091 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-reset.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-reset.png index 5ff5052b0..61c3c4f6c 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-reset.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50-window-reset.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:00130242d3f199926604df16dda70a062071f002566a8056e4794805f29adfde -size 118044 +oid sha256:133dfe6b0028dda6248df1afde1288c57625be99b25c8224673597de4d4f70fc +size 118588 diff --git a/examples/notebooks/screenshots/nb-image-widget-movie-single-50.png b/examples/notebooks/screenshots/nb-image-widget-movie-single-50.png index 5ff5052b0..61c3c4f6c 100644 --- a/examples/notebooks/screenshots/nb-image-widget-movie-single-50.png +++ b/examples/notebooks/screenshots/nb-image-widget-movie-single-50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:00130242d3f199926604df16dda70a062071f002566a8056e4794805f29adfde -size 118044 +oid sha256:133dfe6b0028dda6248df1afde1288c57625be99b25c8224673597de4d4f70fc +size 118588 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-gaussian.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-gaussian.png index 13297e09f..29fe20f44 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-gaussian.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-gaussian.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:70c7738ed303f5a3e19271e8dfc12ab857a6f3aff767bdbecb485b763a09913e -size 55584 +oid sha256:87a3947d6c59c7f67acca25911e0ab93ddc9231a8c3060d2fffe3c53f39055f2 +size 62263 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-reset.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-reset.png index b8307bc44..c7944f591 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-reset.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-frame-apply-reset.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:66a435e45dc4643135633115af2eeaf70761e408a94d70d94d80c14141574528 -size 69343 +oid sha256:b57c65974362d258ec7be8de391c41d7909ed260b92411f4b0ed8ed03b886a29 +size 73040 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-max-window-13.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-max-window-13.png index d6237dc9f..eb9c9059d 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-max-window-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-max-window-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:731f225fa2de3457956b2095d1cc539734983d041b13d6ad1a1f9d8e7ebfa4bc -size 115239 +oid sha256:008381b267ae26e8693ae51e7a4fabc464288ec8aa911ff3a1deb37543cc4fbe +size 115543 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-13.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-13.png index ecf63a369..8b887f5fd 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7e2d70159ac47c004acb022b3a669e7bd307299ddd590b83c08854b0dba27b70 -size 93885 +oid sha256:fedfec781724d4731f8cc34ffc39388d14dc60dad4a9fae9ff56625edf11f87a +size 94178 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-5.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-5.png index e7106fae9..ef3aa7a92 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-5.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50-mean-window-5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1756783ab90435b46ded650033cf29ac36d2b4380744bf312caa2813267f7f38 -size 89813 +oid sha256:08e8379187754fa14f360ed54f2ed8cf61b3df71a8b6f2e95ff1ed27aa435d60 +size 90105 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50.png index ddd4f85ca..c7944f591 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a35e2e4b892b55f5d2500f895951f6a0289a2df3b69cf12f59409bbc091d1caf -size 72810 +oid sha256:b57c65974362d258ec7be8de391c41d7909ed260b92411f4b0ed8ed03b886a29 +size 73040 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-99.png b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-99.png index d9971c3fd..0d19a35ce 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-frame-99.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-frame-99.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3bdb0ed864c8a6f2118cfe0d29476f61c54576f7b8e041f3c3a895ba0a440c05 -size 65039 +oid sha256:848e89e38b9b5ef97d6bb4b301c0ae10cc29f438518721663ae52fa42f492408 +size 65267 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-gaussian.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-gaussian.png index 6736e108c..96a3b12c8 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-gaussian.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-gaussian.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7ae7c86bee3a30bde6cfa44e1e583e6dfd8de6bb29e7c86cea9141ae30637b4a -size 80627 +oid sha256:17cd05ae14cacdef6aa1eca3544246b814ef21762a33f6e785f6d621ea30ff96 +size 80570 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-reset.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-reset.png index dce99223b..1df19c904 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-reset.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-frame-apply-reset.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b51a5d26f2408748e59e3ee481735694f8f376539b50deb2b5c5a864b7de1079 -size 105581 +oid sha256:a673fa1ffa6f746ab9f462b4d592492ec02bfdd3fb53bdf1f71fb9427f8d6d23 +size 105798 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-max-window-13.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-max-window-13.png index cdea3673d..43230f8be 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-max-window-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-max-window-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e854f7f2fdaeeac6c8358f94a33698b5794c0f6c55b240d384e8c6d51fbfb0ff -size 143301 +oid sha256:446d54cea3d54b0fd92b70abcc090cfee30b19454dce118d9875fbeb8b40b4a8 +size 141294 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-13.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-13.png index 25a2fa53e..0841a8e08 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-13.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-13.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c8c8d3c59c145a4096deceabc71775a03e5e121e82509590787c768944d155bd -size 110744 +oid sha256:99d3706d5574a1236264f556eb3ce6d71e81b65bd8dcce1c1415e5f139316c23 +size 107894 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-5.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-5.png index 00a4a1fd2..28bab9f02 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-5.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50-mean-window-5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c4b4af7b99cad95ea3f688af8633de24b6602bd700cb244f93c28718af2e1e85 -size 114982 +oid sha256:ffa17fc1b71c5146cae88493ed40c606dd0a99f3e10f3827ac349d5a5d6f6108 +size 112702 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50.png index 3b5594c64..1df19c904 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6d28a4be4c76d5c0da5f5767b169acf7048a268b010f33f96829a5de7f06fd7d -size 107477 +oid sha256:a673fa1ffa6f746ab9f462b4d592492ec02bfdd3fb53bdf1f71fb9427f8d6d23 +size 105798 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-99.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-99.png index 239237b45..06ed02628 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-99.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-frame-99.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:30dba982c9a605a7a3c0f2fa6d8cdf0df4160b2913a95b26ffdb6b04ead12add -size 104603 +oid sha256:4d3e88eee05bc68dd17918197602fb5c0a959ad74a4f592aea4514e570d29232 +size 103431 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-init-mean-window-5.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-init-mean-window-5.png index 0745a4d4a..61702a6d9 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-init-mean-window-5.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-init-mean-window-5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e431229806ee32a78fb9313a09af20829c27799798232193feab1723b66b1bca -size 112646 +oid sha256:272156c4261bba40eba92f953a0f5078ad8ff2aa80f06a53f73a3572eb537dd5 +size 111155 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-false.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-false.png index 498b19cb7..412822a40 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-false.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-false.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a8e899b48881e3eb9200cc4e776db1f865b0911c340c06d4009b3ae12aa1fc85 -size 105421 +oid sha256:8203f859fe54e2b59a143a9a569c2854640b1501b9ab4f8512520bbf73dae3c6 +size 105658 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-true.png b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-true.png index 369168141..234924487 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-true.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-grid-set_data-reset-indices-true.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:93933e7ba5f791072df2934c94a782e39ed97f7db5b55c5d71c8c5bbfc69d800 -size 106360 +oid sha256:8ca187ba67e7928c8f96b1f9a0a18bec65f81352701e60c33d47aaadb2756d5c +size 106446 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-init-mean-window-5.png b/examples/notebooks/screenshots/nb-image-widget-zfish-init-mean-window-5.png index b62721be2..870945ce7 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-init-mean-window-5.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-init-mean-window-5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bf38b2af1ceb372cd0949d42c027acb5fcc4c6b9a8f38c5aacdce1cd14e290fe -size 78533 +oid sha256:f42367c833a23d3fe10c6fb0d754338c12a30288d9769ad3f8b1159505abf8ff +size 78796 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-frame-50.png b/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-frame-50.png index 76ed01a7c..7880fc1d8 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-frame-50.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-frame-50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ff462d24820f0bdd509e58267071fa956b5c863b8b8d66fea061c5253b7557cf -size 113926 +oid sha256:cb99cd81a18fa2f8986c5f00071c45dc778c8aa177f4b02dca6bc5fab122b054 +size 114825 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-set-data.png b/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-set-data.png index d9a593ee7..82f3d0a9b 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-set-data.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-set-data.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2b8fd14f8e8a90c3cd3fbb84a00d50b1b826b596d64dfae4a5ea1bab0687d906 -size 110829 +oid sha256:31b2b92b9d983950b58b90a09f16199740e35a0737fc1b18904f507ea322d8f2 +size 111118 diff --git a/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-windowrgb.png b/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-windowrgb.png index cf10c6d42..1446c8941 100644 --- a/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-windowrgb.png +++ b/examples/notebooks/screenshots/nb-image-widget-zfish-mixed-rgb-cockatoo-windowrgb.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d88c64b716d19a3978bd60f8d75ffe09e022183381898fa1c48b77598be8fb7c -size 111193 +oid sha256:0fb724e005c6e081ae3bf235e155f3f526c3480facac7479d9b9452aae81baf0 +size 111437 diff --git a/examples/screenshots/image_widget_grid.png b/examples/screenshots/image_widget_grid.png index 45bc70726..a6ccd144a 100644 --- a/examples/screenshots/image_widget_grid.png +++ b/examples/screenshots/image_widget_grid.png @@ -1,3 +1,4 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:306977f7eebdb652828ba425d73b6018e97c100f3cf8f3cbaa0244ffb6c040a3 -size 249103 + +oid sha256:430cd0ee5c05221c42073345480acbeee672c299311f239dc0790a9495d0d758 +size 248046 diff --git a/fastplotlib/tools/_histogram_lut.py b/fastplotlib/tools/_histogram_lut.py index b8c6633a8..aeb8dd996 100644 --- a/fastplotlib/tools/_histogram_lut.py +++ b/fastplotlib/tools/_histogram_lut.py @@ -5,6 +5,7 @@ import pygfx +from ..utils import subsample_array from ..graphics import LineGraphic, ImageGraphic, TextGraphic from ..graphics.utils import pause_events from ..graphics._base import Graphic @@ -193,28 +194,10 @@ def _fpl_add_plot_area_hook(self, plot_area): self._plot_area.controller.enabled = True def _calculate_histogram(self, data): - if data.ndim > 2: - # subsample to max of 500 x 100 x 100, - # np.histogram takes ~30ms with this size on a 8 core Ryzen laptop - # dim0 is usually time, allow max of 500 timepoints - ss0 = max(1, int(data.shape[0] / 500)) # max to prevent step = 0 - # allow max of 100 for x and y if ndim > 2 - ss1 = max(1, int(data.shape[1] / 100)) - ss2 = max(1, int(data.shape[2] / 100)) - data_ss = data[::ss0, ::ss1, ::ss2] - - hist, edges = np.histogram(data_ss, bins=self._nbins) - - else: - # allow max of 1000 x 1000 - # this takes ~4ms on a 8 core Ryzen laptop - ss0 = max(1, int(data.shape[0] / 1_000)) - ss1 = max(1, int(data.shape[1] / 1_000)) - - data_ss = data[::ss0, ::ss1] - - hist, edges = np.histogram(data_ss, bins=self._nbins) + # get a subsampled view of this array + data_ss = subsample_array(data, max_size=int(1e6)) # 1e6 is default + hist, edges = np.histogram(data_ss, bins=self._nbins) # used if data ptp <= 10 because event things get weird # with tiny world objects due to floating point error diff --git a/fastplotlib/utils/functions.py b/fastplotlib/utils/functions.py index 6ad365e40..e775288d3 100644 --- a/fastplotlib/utils/functions.py +++ b/fastplotlib/utils/functions.py @@ -267,7 +267,7 @@ def make_colors_dict(labels: Sequence, cmap: str, **kwargs) -> OrderedDict: return OrderedDict(zip(labels, colors)) -def quick_min_max(data: np.ndarray) -> tuple[float, float]: +def quick_min_max(data: np.ndarray, max_size=1e6) -> tuple[float, float]: """ Adapted from pyqtgraph.ImageView. Estimate the min/max values of *data* by subsampling. @@ -276,6 +276,9 @@ def quick_min_max(data: np.ndarray) -> tuple[float, float]: ---------- data: np.ndarray or array-like with `min` and `max` attributes + max_size : int, optional + largest array size allowed in the subsampled array. Default is 1e6. + Returns ------- (float, float) @@ -289,11 +292,7 @@ def quick_min_max(data: np.ndarray) -> tuple[float, float]: ): return data.min, data.max - while np.prod(data.shape) > 1e6: - ax = np.argmax(data.shape) - sl = [slice(None)] * data.ndim - sl[ax] = slice(None, None, 2) - data = data[tuple(sl)] + data = subsample_array(data, max_size=max_size) return float(np.nanmin(data)), float(np.nanmax(data)) @@ -405,3 +404,62 @@ def parse_cmap_values( colors = np.vstack([colormap[val] for val in norm_cmap_values]) return colors + + +def subsample_array(arr: np.ndarray, max_size: int = 1e6): + """ + Subsamples an input array while preserving its relative dimensional proportions. + + The dimensions (shape) of the array can be represented as: + + .. math:: + + [d_1, d_2, \\dots d_n] + + The product of the dimensions can be represented as: + + .. math:: + + \\prod_{i=1}^{n} d_i + + To find the factor ``f`` by which to divide the size of each dimension in order to + get max_size ``s`` we must solve for ``f`` in the following expression: + + .. math:: + + \\prod_{i=1}^{n} \\frac{d_i}{\\mathbf{f}} = \\mathbf{s} + + The solution for ``f`` is is simply the nth root of the product of the dims divided by the max_size + where n is the number of dimensions + + .. math:: + + \\mathbf{f} = \\sqrt[n]{\\frac{\\prod_{i=1}^{n} d_i}{\\mathbf{s}}} + + Parameters + ---------- + arr: np.ndarray + input array of any dimensionality to be subsampled. + + max_size: int, default 1e6 + maximum number of elements in subsampled array + + Returns + ------- + np.ndarray + subsample of the input array + """ + if np.prod(arr.shape) <= max_size: + return arr # no need to subsample if already below the threshold + + # get factor by which to divide all dims + f = np.power((np.prod(arr.shape) / max_size), 1.0 / arr.ndim) + + # new shape for subsampled array + ns = np.floor(np.array(arr.shape) / f).clip(min=1) + + # get the step size for the slices + slices = tuple( + slice(None, None, int(s)) for s in np.floor(arr.shape / ns).astype(int) + ) + return np.asarray(arr[slices]) From e6804bb9228bdc390fec0c17429abe4762b06bab Mon Sep 17 00:00:00 2001 From: Flynn <75346097+FlynnOConnell@users.noreply.github.com> Date: Sun, 13 Apr 2025 18:38:15 -0400 Subject: [PATCH 171/185] add ignore_dims arg to subsample_array (#797) * add ignore_dims arg to subsample_array * black --- fastplotlib/utils/functions.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/fastplotlib/utils/functions.py b/fastplotlib/utils/functions.py index e775288d3..87d764e41 100644 --- a/fastplotlib/utils/functions.py +++ b/fastplotlib/utils/functions.py @@ -406,7 +406,9 @@ def parse_cmap_values( return colors -def subsample_array(arr: np.ndarray, max_size: int = 1e6): +def subsample_array( + arr: np.ndarray, max_size: int = 1e6, ignore_dims: Sequence[int] | None = None +): """ Subsamples an input array while preserving its relative dimensional proportions. @@ -444,6 +446,10 @@ def subsample_array(arr: np.ndarray, max_size: int = 1e6): max_size: int, default 1e6 maximum number of elements in subsampled array + ignore_dims: Sequence[int], optional + List of dimension indices to exclude from subsampling (i.e. retain full resolution). + For example, `ignore_dims=[0]` will avoid subsampling along the first axis. + Returns ------- np.ndarray @@ -462,4 +468,12 @@ def subsample_array(arr: np.ndarray, max_size: int = 1e6): slices = tuple( slice(None, None, int(s)) for s in np.floor(arr.shape / ns).astype(int) ) + + # ignore dims e.g. RGB, which we don't want to downsample + if ignore_dims is not None: + for dim in ignore_dims: + slices[dim] = slice(None) + + slices = tuple(slices) + return np.asarray(arr[slices]) From 5ae4052d4fc350db8d5b09c5fd67e3c5367917da Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Mon, 14 Apr 2025 10:16:20 -0400 Subject: [PATCH 172/185] add show fps and blend modes to right click menu options (#796) * add show fps and blend modes to right click menu options * black --- .../ui/right_click_menus/_standard_menu.py | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/fastplotlib/ui/right_click_menus/_standard_menu.py b/fastplotlib/ui/right_click_menus/_standard_menu.py index 1937df858..53a553f34 100644 --- a/fastplotlib/ui/right_click_menus/_standard_menu.py +++ b/fastplotlib/ui/right_click_menus/_standard_menu.py @@ -80,6 +80,11 @@ def update(self): imgui.text(f"subplot: {name}") imgui.separator() + _, show_fps = imgui.menu_item( + "Show fps", "", self.get_subplot().get_figure().imgui_show_fps + ) + self.get_subplot().get_figure().imgui_show_fps = show_fps + # autoscale, center, maintain aspect if imgui.menu_item(f"Autoscale", "", False)[0]: self.get_subplot().auto_scale() @@ -174,4 +179,19 @@ def update(self): imgui.end_menu() + # renderer blend modes + if imgui.begin_menu("Blend mode"): + for blend_mode in sorted( + self.get_subplot().renderer._blenders_available.keys() + ): + clicked, _ = imgui.menu_item( + label=blend_mode, + shortcut="", + p_selected=self.get_subplot().renderer.blend_mode == blend_mode, + ) + + if clicked: + self.get_subplot().renderer.blend_mode = blend_mode + imgui.end_menu() + imgui.end_popup() From 5e910cc78355f0fd475b58900245c3814ee0460c Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Tue, 15 Apr 2025 16:19:25 +0200 Subject: [PATCH 173/185] Improve hover color behavior for selectors (#799) * Improve hover color behavior * black --- .../graphics/selectors/_base_selector.py | 27 +++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/fastplotlib/graphics/selectors/_base_selector.py b/fastplotlib/graphics/selectors/_base_selector.py index 629c063bc..3f9d0083c 100644 --- a/fastplotlib/graphics/selectors/_base_selector.py +++ b/fastplotlib/graphics/selectors/_base_selector.py @@ -136,8 +136,13 @@ def __init__( self._hover_responsive: Tuple[WorldObject, ...] = hover_responsive + # Original color of object that we change the colors of + self._original_colors = {} + + # Colors as they are changed by the hover events, so they can be restored after a move action + self._hover_colors = {} + if hover_responsive is not None: - self._original_colors = dict() for wo in self._hover_responsive: self._original_colors[wo] = wo.material.color @@ -325,6 +330,11 @@ def _move_end(self, ev): self._move_info = None self._moving = False + # Reset hover state + for wo, color in self._hover_colors.items(): + wo.material.color = color + self._hover_colors.clear() + # restore the initial controller state # if it was disabled, keep it disabled if self._initial_controller_state is not None: @@ -378,6 +388,7 @@ def _move_to_pointer(self, ev): self._move_info = None def _pointer_enter(self, ev): + if self._hover_responsive is None: return @@ -388,17 +399,23 @@ def _pointer_enter(self, ev): if wo in self._edges: self._edge_hovered = True - wo.material.color = "magenta" + if self._moving: + self._hover_colors[wo] = "magenta" + else: + wo.material.color = "magenta" def _pointer_leave(self, ev): if self._hover_responsive is None: return + self._edge_hovered = False + # reset colors for wo in self._hover_responsive: - wo.material.color = self._original_colors[wo] - - self._edge_hovered = False + if self._moving: + self._hover_colors[wo] = self._original_colors[wo] + else: + wo.material.color = self._original_colors[wo] def _toggle_arrow_key_moveable(self, ev): self.arrow_key_events_enabled = not self.arrow_key_events_enabled From c95ed34f6e0832f80479d03c28e76cfdac559a97 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Wed, 16 Apr 2025 22:42:48 +0200 Subject: [PATCH 174/185] Refactor selector drag behavior (#800) * Refactor selector drag behavior * black * Use | instead of Union --- .../linear_region_line_collection.py | 7 +- .../selection_tools/linear_region_selector.py | 12 +-- .../linear_region_selectors_match_offsets.py | 12 +-- .../graphics/selectors/_base_selector.py | 80 +++++++++++-------- fastplotlib/graphics/selectors/_linear.py | 16 ++-- .../graphics/selectors/_linear_region.py | 44 +++++----- fastplotlib/graphics/selectors/_polygon.py | 11 ++- fastplotlib/graphics/selectors/_rectangle.py | 46 ++++++----- fastplotlib/layouts/_plot_area.py | 4 +- 9 files changed, 129 insertions(+), 103 deletions(-) diff --git a/examples/selection_tools/linear_region_line_collection.py b/examples/selection_tools/linear_region_line_collection.py index 76739d784..4b85b34dc 100644 --- a/examples/selection_tools/linear_region_line_collection.py +++ b/examples/selection_tools/linear_region_line_collection.py @@ -59,8 +59,11 @@ def update_zoomed_subplots(ev): for i in range(len(zoomed_data)): # interpolate y-vals - data = interpolate(zoomed_data[i], axis=1) - figure[i + 1, 0]["zoomed"].data[:, 1] = data + if zoomed_data[i].size == 0: + figure[i + 1, 0]["zoomed"].data[:, 1] = 0 + else: + data = interpolate(zoomed_data[i], axis=1) + figure[i + 1, 0]["zoomed"].data[:, 1] = data figure[i + 1, 0].auto_scale() diff --git a/examples/selection_tools/linear_region_selector.py b/examples/selection_tools/linear_region_selector.py index bfbf27811..272623370 100644 --- a/examples/selection_tools/linear_region_selector.py +++ b/examples/selection_tools/linear_region_selector.py @@ -79,9 +79,9 @@ def set_zoom_x(ev): if selected_data.size == 0: # no data selected zoomed_x.data[:, 1] = 0 - - # interpolate the y-values since y = f(x) - zoomed_x.data[:, 1] = interpolate(selected_data, axis=1) + else: + # interpolate the y-values since y = f(x) + zoomed_x.data[:, 1] = interpolate(selected_data, axis=1) figure[1, 0].auto_scale() @@ -92,9 +92,9 @@ def set_zoom_y(ev): if selected_data.size == 0: # no data selected zoomed_y.data[:, 1] = 0 - - # interpolate the x values since this x = f(y) - zoomed_y.data[:, 1] = -interpolate(selected_data, axis=0) + else: + # interpolate the x values since this x = f(y) + zoomed_y.data[:, 1] = -interpolate(selected_data, axis=0) figure[1, 1].auto_scale() diff --git a/examples/selection_tools/linear_region_selectors_match_offsets.py b/examples/selection_tools/linear_region_selectors_match_offsets.py index b48e30f28..a803a5e75 100644 --- a/examples/selection_tools/linear_region_selectors_match_offsets.py +++ b/examples/selection_tools/linear_region_selectors_match_offsets.py @@ -74,9 +74,9 @@ def set_zoom_x(ev): if selected_data.size == 0: # no data selected zoomed_x.data[:, 1] = 0 - - # interpolate the y-values since y = f(x) - zoomed_x.data[:, 1] = interpolate(selected_data, axis=1) + else: + # interpolate the y-values since y = f(x) + zoomed_x.data[:, 1] = interpolate(selected_data, axis=1) figure[1, 0].auto_scale() @@ -87,9 +87,9 @@ def set_zoom_y(ev): if selected_data.size == 0: # no data selected zoomed_y.data[:, 1] = 0 - - # interpolate the x values since this x = f(y) - zoomed_y.data[:, 1] = -interpolate(selected_data, axis=0) + else: + # interpolate the x values since this x = f(y) + zoomed_y.data[:, 1] = -interpolate(selected_data, axis=0) figure[1, 1].auto_scale() diff --git a/fastplotlib/graphics/selectors/_base_selector.py b/fastplotlib/graphics/selectors/_base_selector.py index 3f9d0083c..b74bcf759 100644 --- a/fastplotlib/graphics/selectors/_base_selector.py +++ b/fastplotlib/graphics/selectors/_base_selector.py @@ -16,12 +16,17 @@ class MoveInfo: stores move info for a WorldObject """ - # last position for an edge, fill, or vertex in world coordinates - # can be None, such as key events - last_position: Union[np.ndarray, None] + # The initial selection. Differs per type of selector + start_selection: Any + + # The initial world position of the cursor + start_position: np.ndarray | None + + # Delta position in world coordinates + delta: np.ndarray # WorldObject or "key" event - source: Union[WorldObject, str] + source: WorldObject | str # key bindings used to move the selector @@ -148,9 +153,6 @@ def __init__( self._axis = axis - # current delta in world coordinates - self.delta: np.ndarray = None - self.arrow_keys_modifier = arrow_keys_modifier # if not False, moves the slider on every render cycle self._key_move_value = False @@ -278,9 +280,14 @@ def _move_start(self, event_source: WorldObject, ev): pygfx ``Event`` """ - last_position = self._plot_area.map_screen_to_world(ev) + position = self._plot_area.map_screen_to_world(ev) - self._move_info = MoveInfo(last_position=last_position, source=event_source) + self._move_info = MoveInfo( + start_selection=None, + start_position=position, + delta=np.zeros_like(position), + source=event_source, + ) self._moving = True self._initial_controller_state = self._plot_area.controller.enabled @@ -303,21 +310,14 @@ def _move(self, ev): # disable controller during moves self._plot_area.controller.enabled = False - # get pointer current world position - world_pos = self._plot_area.map_screen_to_world(ev) - - # outside this viewport - if world_pos is None: - return + # get pointer current world position, in 'mouse capute mode' + world_pos = self._plot_area.map_screen_to_world(ev, allow_outside=True) - # compute the delta - self.delta = world_pos - self._move_info.last_position + # update the delta + self._move_info.delta = world_pos - self._move_info.start_position self._pygfx_event = ev - self._move_graphic(self.delta) - - # update last position - self._move_info.last_position = world_pos + self._move_graphic(self._move_info) # restore the initial controller state # if it was disabled, keep it disabled @@ -370,22 +370,26 @@ def _move_to_pointer(self, ev): if world_pos is None: return - self.delta = world_pos - current_pos_world + delta = world_pos - current_pos_world self._pygfx_event = ev # use fill by default as the source, such as in region selectors if len(self._fill) > 0: - self._move_info = MoveInfo( - last_position=current_pos_world, source=self._fill[0] + move_info = MoveInfo( + start_selection=None, + start_position=None, + delta=delta, + source=self._fill[0], ) # else use an edge, such as for linear selector else: - self._move_info = MoveInfo( - last_position=current_pos_world, source=self._edges[0] + move_info = MoveInfo( + start_position=current_pos_world, + last_position=current_pos_world, + source=self._edges[0], ) - self._move_graphic(self.delta) - self._move_info = None + self._move_graphic(move_info) def _pointer_enter(self, ev): @@ -428,15 +432,23 @@ def _key_hold(self): # set event source # use fill by default as the source if len(self._fill) > 0: - self._move_info = MoveInfo(last_position=None, source=self._fill[0]) + move_info = MoveInfo( + start_selection=None, + start_position=None, + delta=delta, + source=self._fill[0], + ) # else use an edge else: - self._move_info = MoveInfo(last_position=None, source=self._edges[0]) + move_info = MoveInfo( + start_selection=None, + start_position=None, + delta=delta, + source=self._edges[0], + ) # move the graphic - self._move_graphic(delta=delta) - - self._move_info = None + self._move_graphic(move_info) def _key_down(self, ev): # key bind modifier must be set and must be used for the event @@ -458,8 +470,6 @@ def _key_up(self, ev): if ev.key in key_bind_direction.keys(): self._key_move_value = False - self._move_info = None - def _fpl_prepare_del(self): if hasattr(self, "_pfunc_fill"): self._plot_area.renderer.remove_event_handler( diff --git a/fastplotlib/graphics/selectors/_linear.py b/fastplotlib/graphics/selectors/_linear.py index 7ac0fc761..eb9e43d75 100644 --- a/fastplotlib/graphics/selectors/_linear.py +++ b/fastplotlib/graphics/selectors/_linear.py @@ -8,7 +8,7 @@ from .._base import Graphic from .._collection_base import GraphicCollection from ..features._selection_features import LinearSelectionFeature -from ._base_selector import BaseSelector +from ._base_selector import BaseSelector, MoveInfo class LinearSelector(BaseSelector): @@ -177,8 +177,6 @@ def __init__( world_object.add(self.line_outer) world_object.add(line_inner) - self._move_info: dict = None - if axis == "x": offset = (parent.offset[0], center + parent.offset[1], 0) elif axis == "y": @@ -276,7 +274,7 @@ def _get_selected_index(self, graphic): return min(round(index), upper_bound) - def _move_graphic(self, delta: np.ndarray): + def _move_graphic(self, move_info: MoveInfo): """ Moves the graphic @@ -287,7 +285,9 @@ def _move_graphic(self, delta: np.ndarray): """ - if self.axis == "x": - self.selection = self.selection + delta[0] - else: - self.selection = self.selection + delta[1] + # If this the first move in this drag, store initial selection + if move_info.start_selection is None: + move_info.start_selection = self.selection + + delta = move_info.delta[0] if self.axis == "x" else move_info.delta[1] + self.selection = move_info.start_selection + delta diff --git a/fastplotlib/graphics/selectors/_linear_region.py b/fastplotlib/graphics/selectors/_linear_region.py index 1bc3efc2c..14160b10c 100644 --- a/fastplotlib/graphics/selectors/_linear_region.py +++ b/fastplotlib/graphics/selectors/_linear_region.py @@ -7,7 +7,7 @@ from .._base import Graphic from .._collection_base import GraphicCollection from ..features._selection_features import LinearRegionSelectionFeature -from ._base_selector import BaseSelector +from ._base_selector import BaseSelector, MoveInfo class LinearRegionSelector(BaseSelector): @@ -288,7 +288,7 @@ def get_selected_data( # slices n_datapoints dim data_selections.append(g.data[s]) - return source.data[s] + return data_selections else: if ixs.size == 0: # empty selection @@ -368,31 +368,29 @@ def get_selected_indices( # indices map directly to grid geometry for image data buffer return np.arange(*bounds, dtype=int) - def _move_graphic(self, delta: np.ndarray): + def _move_graphic(self, move_info: MoveInfo): + + # If this the first move in this drag, store initial selection + if move_info.start_selection is None: + move_info.start_selection = self.selection + # add delta to current min, max to get new positions - if self.axis == "x": - # add x value - new_min, new_max = self.selection + delta[0] + delta = move_info.delta[0] if self.axis == "x" else move_info.delta[1] - elif self.axis == "y": - # add y value - new_min, new_max = self.selection + delta[1] + # Get original selection + cur_min, cur_max = move_info.start_selection # move entire selector if event source was fill if self._move_info.source == self.fill: - # prevent weird shrinkage of selector if one edge is already at the limit - if self.selection[0] == self.limits[0] and new_min < self.limits[0]: - # self._move_end(None) # TODO: cancel further movement to prevent weird asynchronization with pointer - return - if self.selection[1] == self.limits[1] and new_max > self.limits[1]: - # self._move_end(None) - return - - # move entire selector - self._selection.set_value(self, (new_min, new_max)) + # Limit the delta to avoid weird resizine behavior + min_delta = self.limits[0] - cur_min + max_delta = self.limits[1] - cur_max + delta = np.clip(delta, min_delta, max_delta) + # Update both bounds with equal amount + self._selection.set_value(self, (cur_min + delta, cur_max + delta)) return - # if selector is not resizable return + # if selector not resizable return if not self._resizable: return @@ -400,8 +398,10 @@ def _move_graphic(self, delta: np.ndarray): # move the edge that caused the event if self._move_info.source == self.edges[0]: # change only left or bottom bound - self._selection.set_value(self, (new_min, self._selection.value[1])) + new_min = min(cur_min + delta, cur_max) + self._selection.set_value(self, (new_min, cur_max)) elif self._move_info.source == self.edges[1]: # change only right or top bound - self._selection.set_value(self, (self.selection[0], new_max)) + new_max = max(cur_max + delta, cur_min) + self._selection.set_value(self, (cur_min, new_max)) diff --git a/fastplotlib/graphics/selectors/_polygon.py b/fastplotlib/graphics/selectors/_polygon.py index a4ecd440c..22e42e63e 100644 --- a/fastplotlib/graphics/selectors/_polygon.py +++ b/fastplotlib/graphics/selectors/_polygon.py @@ -62,11 +62,16 @@ def _add_segment(self, ev): """After click event, adds a new line segment""" self._current_mode = "add" - last_position = self._plot_area.map_screen_to_world(ev) - self._move_info = MoveInfo(last_position=last_position, source=None) + position = self._plot_area.map_screen_to_world(ev) + self._move_info = MoveInfo( + start_selection=None, + start_position=position, + delta=np.zeros_like(position), + source=None, + ) # line with same position for start and end until mouse moves - data = np.array([last_position, last_position]) + data = np.array([position, position]) new_line = pygfx.Line( geometry=pygfx.Geometry(positions=data.astype(np.float32)), diff --git a/fastplotlib/graphics/selectors/_rectangle.py b/fastplotlib/graphics/selectors/_rectangle.py index 8d0af8e88..fcf4467cb 100644 --- a/fastplotlib/graphics/selectors/_rectangle.py +++ b/fastplotlib/graphics/selectors/_rectangle.py @@ -8,7 +8,7 @@ from .._base import Graphic from ..features import RectangleSelectionFeature -from ._base_selector import BaseSelector +from ._base_selector import BaseSelector, MoveInfo class RectangleSelector(BaseSelector): @@ -24,7 +24,7 @@ def selection(self) -> np.ndarray[float]: """ (xmin, xmax, ymin, ymax) of the rectangle selection """ - return self._selection.value + return self._selection.value.copy() @selection.setter def selection(self, selection: Sequence[float]): @@ -479,33 +479,41 @@ def get_selected_indices( return ixs - def _move_graphic(self, delta: np.ndarray): + def _move_graphic(self, move_info: MoveInfo): - # new selection positions - xmin_new = self.selection[0] + delta[0] - xmax_new = self.selection[1] + delta[0] - ymin_new = self.selection[2] + delta[1] - ymax_new = self.selection[3] + delta[1] + # If this the first move in this drag, store initial selection + if move_info.start_selection is None: + move_info.start_selection = self.selection + + # add delta to current min, max to get new positions + deltax, deltay = move_info.delta[0], move_info.delta[1] + + # Get original selection + xmin, xmax, ymin, ymax = move_info.start_selection # move entire selector if source is fill if self._move_info.source == self.fill: - if self.selection[0] == self.limits[0] and xmin_new < self.limits[0]: - return - if self.selection[1] == self.limits[1] and xmax_new > self.limits[1]: - return - if self.selection[2] == self.limits[2] and ymin_new < self.limits[2]: - return - if self.selection[3] == self.limits[3] and ymax_new > self.limits[3]: - return - # set thew new bounds - self._selection.set_value(self, (xmin_new, xmax_new, ymin_new, ymax_new)) + # Limit the delta to avoid weird resizine behavior + min_deltax = self.limits[0] - xmin + max_deltax = self.limits[1] - xmax + min_deltay = self.limits[2] - ymin + max_deltay = self.limits[3] - ymax + deltax = np.clip(deltax, min_deltax, max_deltax) + deltay = np.clip(deltay, min_deltay, max_deltay) + # Update all bounds with equal amount + self._selection.set_value( + self, (xmin + deltax, xmax + deltax, ymin + deltay, ymax + deltay) + ) return # if selector not resizable return if not self._resizable: return - xmin, xmax, ymin, ymax = self.selection + xmin_new = min(xmin + deltax, xmax) + xmax_new = max(xmax + deltax, xmin) + ymin_new = min(ymin + deltay, ymax) + ymax_new = max(ymax + deltay, ymin) if self._move_info.source == self.vertices[0]: # bottom left self._selection.set_value(self, (xmin_new, xmax, ymin_new, ymax)) diff --git a/fastplotlib/layouts/_plot_area.py b/fastplotlib/layouts/_plot_area.py index e780607ce..2934e0589 100644 --- a/fastplotlib/layouts/_plot_area.py +++ b/fastplotlib/layouts/_plot_area.py @@ -273,7 +273,7 @@ def background_color(self, colors: str | tuple[float]): self._background_material.set_colors(*colors) def map_screen_to_world( - self, pos: tuple[float, float] | pygfx.PointerEvent + self, pos: tuple[float, float] | pygfx.PointerEvent, allow_outside: bool = False ) -> np.ndarray | None: """ Map screen position to world position @@ -287,7 +287,7 @@ def map_screen_to_world( if isinstance(pos, pygfx.PointerEvent): pos = pos.x, pos.y - if not self.viewport.is_inside(*pos): + if not allow_outside and not self.viewport.is_inside(*pos): return None vs = self.viewport.logical_size From 29b098d3a2708abbdc0d6f5e51d67cdcd68043fe Mon Sep 17 00:00:00 2001 From: Amol Pasarkar Date: Fri, 18 Apr 2025 19:53:07 -0400 Subject: [PATCH 175/185] Fixes array issue that was causing incompatibility in the subsample array function for lazy array classes (#803) --- fastplotlib/utils/functions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastplotlib/utils/functions.py b/fastplotlib/utils/functions.py index 87d764e41..d6d996a45 100644 --- a/fastplotlib/utils/functions.py +++ b/fastplotlib/utils/functions.py @@ -456,7 +456,7 @@ def subsample_array( subsample of the input array """ if np.prod(arr.shape) <= max_size: - return arr # no need to subsample if already below the threshold + return arr[:] # no need to subsample if already below the threshold # get factor by which to divide all dims f = np.power((np.prod(arr.shape) / max_size), 1.0 / arr.ndim) From 4294969524021e342f908f4c81b57cfbe246b2e8 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Tue, 6 May 2025 19:30:04 -0400 Subject: [PATCH 176/185] fix typo in dev docs (#807) --- docs/source/developer_notes/layouts.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/source/developer_notes/layouts.rst b/docs/source/developer_notes/layouts.rst index 4aacd38da..daf197c44 100644 --- a/docs/source/developer_notes/layouts.rst +++ b/docs/source/developer_notes/layouts.rst @@ -4,8 +4,8 @@ Layouts PlotArea -------- -This is the main base class within layouts. A ``Figure`` and ``Dock`` are areas within a ``Subplot`` that -inherit from ``PlotArea``. +This is the main base class within layouts. A ``Subplot`` and ``Dock`` are areas within a ``Figure``. +``Subplot`` and ``Dock`` inherit from ``PlotArea``. ``PlotArea`` has the following key properties that allow it to be a "plot area" that can be used to view graphical objects: @@ -81,4 +81,4 @@ Now that we have understood ``PlotArea`` and ``Subplot`` we need a way for the u A ``Figure`` contains a grid of subplot and has methods such as ``show()`` to output the figure. ``Figure.__init__`` basically does a lot of parsing of user arguments to determine how to create -the subplots. All subplots within a ``Figure`` share the same canvas and use different viewports to create the subplots. \ No newline at end of file +the subplots. All subplots within a ``Figure`` share the same canvas and use different viewports to create the subplots. From 29d4a87f518c80d5a93538d76e100db5fe58ba12 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Fri, 9 May 2025 21:56:39 -0400 Subject: [PATCH 177/185] use `LineInfiniteSegmentMaterial` for `LinearSelector` (#813) * use LineInfiniteSegmentMaterial * remove comment * linear selector wasn't in screenshot tests * linear selector ground truths --- examples/screenshots/linear_selector.png | 4 +- .../screenshots/no-imgui-linear_selector.png | 3 ++ examples/selection_tools/linear_selector.py | 2 +- fastplotlib/graphics/image.py | 2 - fastplotlib/graphics/line.py | 2 - fastplotlib/graphics/line_collection.py | 2 - fastplotlib/graphics/selectors/_linear.py | 37 ++++++------------- 7 files changed, 17 insertions(+), 35 deletions(-) create mode 100644 examples/screenshots/no-imgui-linear_selector.png diff --git a/examples/screenshots/linear_selector.png b/examples/screenshots/linear_selector.png index 2db42319d..8571d664b 100644 --- a/examples/screenshots/linear_selector.png +++ b/examples/screenshots/linear_selector.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:09f60f24e702dd6b17ba525604c1a04f23682eb08c8c2100d45a34b2626bebc6 -size 153115 +oid sha256:62ded18658bc5cb41129d27eb21f47f029cf7c75bb6388b5d72af6fe9c5cada9 +size 130919 diff --git a/examples/screenshots/no-imgui-linear_selector.png b/examples/screenshots/no-imgui-linear_selector.png new file mode 100644 index 000000000..4416cb4d5 --- /dev/null +++ b/examples/screenshots/no-imgui-linear_selector.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1f1a323dec6d50d1c701632aadbd17d87ee3b3b42171046ca9b1284f93576a3b +size 131922 diff --git a/examples/selection_tools/linear_selector.py b/examples/selection_tools/linear_selector.py index 1edf6345c..d7a8e6739 100644 --- a/examples/selection_tools/linear_selector.py +++ b/examples/selection_tools/linear_selector.py @@ -5,7 +5,7 @@ Example showing how to use a `LinearSelector` with lines and line collections. """ -# test_example = false +# test_example = true # sphinx_gallery_pygfx_docs = 'screenshot' import fastplotlib as fpl diff --git a/fastplotlib/graphics/image.py b/fastplotlib/graphics/image.py index 5f198c84f..bebdbbf6d 100644 --- a/fastplotlib/graphics/image.py +++ b/fastplotlib/graphics/image.py @@ -312,8 +312,6 @@ def add_linear_selector( selector = LinearSelector( selection=selection, limits=limits, - size=size, - center=center, axis=axis, parent=self, **kwargs, diff --git a/fastplotlib/graphics/line.py b/fastplotlib/graphics/line.py index d02297c64..09410e9fd 100644 --- a/fastplotlib/graphics/line.py +++ b/fastplotlib/graphics/line.py @@ -166,8 +166,6 @@ def add_linear_selector( selector = LinearSelector( selection=selection, limits=limits, - size=size, - center=center, axis=axis, parent=self, **kwargs, diff --git a/fastplotlib/graphics/line_collection.py b/fastplotlib/graphics/line_collection.py index c4af5dddc..de4139679 100644 --- a/fastplotlib/graphics/line_collection.py +++ b/fastplotlib/graphics/line_collection.py @@ -374,8 +374,6 @@ def add_linear_selector( selector = LinearSelector( selection=selection, limits=limits, - size=size, - center=center, axis=axis, parent=self, **kwargs, diff --git a/fastplotlib/graphics/selectors/_linear.py b/fastplotlib/graphics/selectors/_linear.py index eb9e43d75..64a673768 100644 --- a/fastplotlib/graphics/selectors/_linear.py +++ b/fastplotlib/graphics/selectors/_linear.py @@ -75,8 +75,6 @@ def __init__( self, selection: float, limits: Sequence[float], - size: float, - center: float, axis: str = "x", parent: Graphic = None, edge_color: str | Sequence[float] | np.ndarray = "w", @@ -95,12 +93,6 @@ def __init__( limits: (int, int) (min, max) limits along the x or y-axis for the selector, in data space - size: float - size of the selector, usually the range of the data - - center: float - center offset of the selector on the orthogonal axis, usually the data mean - axis: str, default "x" "x" | "y", the axis along which the selector can move @@ -131,29 +123,22 @@ def __init__( self._limits = np.asarray(limits) - end_points = [-size / 2, size / 2] - if axis == "x": - xs = np.array([selection, selection]) - ys = np.array(end_points) - zs = np.zeros(2) + xs = np.array([selection, selection], dtype=np.float32) + ys = np.array([0, 1], dtype=np.float32) + zs = np.zeros(2, dtype=np.float32) - line_data = np.column_stack([xs, ys, zs]) elif axis == "y": - xs = np.array(end_points) - ys = np.array([selection, selection]) - zs = np.zeros(2) + xs = np.array([0, 1], dtype=np.float32) + ys = np.array([selection, selection], dtype=np.float32) + zs = np.zeros(2, dtype=np.float32) - line_data = np.column_stack([xs, ys, zs]) else: - raise ValueError("`axis` must be one of 'x' or 'y'") + raise ValueError("`axis` must be one of 'x' | 'y'") - line_data = line_data.astype(np.float32) + line_data = np.column_stack([xs, ys, zs]) - if thickness < 1.1: - material = pygfx.LineThinMaterial - else: - material = pygfx.LineMaterial + material = pygfx.LineInfiniteSegmentMaterial self.colors_outer = pygfx.Color([0.3, 0.3, 0.3, 1.0]) @@ -178,9 +163,9 @@ def __init__( world_object.add(line_inner) if axis == "x": - offset = (parent.offset[0], center + parent.offset[1], 0) + offset = (parent.offset[0], 0, 0) elif axis == "y": - offset = (center + parent.offset[0], parent.offset[1], 0) + offset = (0, parent.offset[1], 0) # init base selector BaseSelector.__init__( From 54deeaf9bbd800913d4caef4cc878c8164424578 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Mon, 19 May 2025 13:59:01 -0400 Subject: [PATCH 178/185] Fix rect manager (#820) * bugfix imgui right click menu * almost fixed rect manager * revert, just disallow left and top edges for now * cleanup docs guide * remove file, fix --- docs/source/user_guide/guide.rst | 22 +-- examples/guis/sine_cosine_funcs.py | 186 ++++++++++++++++++ fastplotlib/layouts/_imgui_figure.py | 18 +- fastplotlib/ui/_base.py | 29 +-- .../ui/right_click_menus/_standard_menu.py | 5 +- 5 files changed, 207 insertions(+), 53 deletions(-) create mode 100644 examples/guis/sine_cosine_funcs.py diff --git a/docs/source/user_guide/guide.rst b/docs/source/user_guide/guide.rst index 1bdb3377c..073fa806c 100644 --- a/docs/source/user_guide/guide.rst +++ b/docs/source/user_guide/guide.rst @@ -549,10 +549,15 @@ between imgui and ipywidgets, Qt, and wx is the imgui UI can be rendered directl i.e. it will work in jupyter, Qt, glfw and wx windows! The programming model is different from Qt and ipywidgets, there are no callbacks, but it is easy to learn if you see a few examples. +.. image:: ../_static/guide_imgui.png + We specifically use `imgui-bundle `_ for the python bindings in fastplotlib. There is large community and many resources out there on building UIs using imgui. -For examples on integrating imgui with a fastplotlib Figure please see the examples gallery. +To install ``fastplotlib`` with ``imgui`` use the ``imgui`` extras option, i.e. ``pip install fastplotlib[imgui]``, or ``pip install imgui_bundle`` if you've already installed fastplotlib. + +Fastplotlib comes built-in with imgui UIs for subplot toolbars and a standard right-click menu with a number of options. +You can also make custom GUIs and embed them within the canvas, see the examples gallery for detailed examples. **Some tips:** @@ -662,21 +667,6 @@ There are several spaces to consider when using ``fastplotlib``: For more information on the various spaces used by rendering engines please see this `article `_ -Imgui ------ - -Fastplotlib uses `imgui_bundle `_ to provide within-canvas UI elemenents if you -installed ``fastplotlib`` using the ``imgui`` toggle, i.e. ``fastplotlib[imgui]``, or installed ``imgui_bundle`` afterwards. - -Fastplotlib comes built-in with imgui UIs for subplot toolbars and a standard right-click menu with a number of options. -You can also make custom GUIs and embed them within the canvas, see the examples gallery for detailed examples. - -.. note:: - Imgui is optional, you can use other GUI frameworks such at Qt or ipywidgets with fastplotlib. You can also of course - use imgui and Qt or ipywidgets. - -.. image:: ../_static/guide_imgui.png - Using ``fastplotlib`` in an interactive shell --------------------------------------------- diff --git a/examples/guis/sine_cosine_funcs.py b/examples/guis/sine_cosine_funcs.py new file mode 100644 index 000000000..c91a3b2e8 --- /dev/null +++ b/examples/guis/sine_cosine_funcs.py @@ -0,0 +1,186 @@ +""" +Sine and Cosine functions +========================= + +Identical to the Unit Circle example but you can change the angular frequencies using a UI + +""" + +# test_example = false +# sphinx_gallery_pygfx_docs = 'screenshot' + +import glfw +import numpy as np +import fastplotlib as fpl +from fastplotlib.ui import EdgeWindow +from imgui_bundle import imgui + + +# initial frequency coefficients for sine and cosine functions +P = 1 +Q = 1 + + +# helper function to make a circle +def make_circle(center, radius: float, p, q, n_points: int) -> np.ndarray: + theta = np.linspace(0, 2 * np.pi, n_points) + xs = radius * np.cos(theta * p) + ys = radius * np.sin(theta * q) + + return np.column_stack([xs, ys]) + center + + +# we can define this layout using "extents", i.e. min and max ranges on the canvas +# (x_min, x_max, y_min, y_max) +# extents can be defined as fractions as shown here +extents = [ + (0, 0.5, 0, 1), # circle subplot + (0.5, 1, 0, 0.5), # sine subplot + (0.5, 1, 0.5, 1), # cosine subplot +] + +# create a figure with 3 subplots +figure = fpl.Figure( + extents=extents, + names=["circle", "sin", "cos"], + size=(700, 560) +) + +# set more descriptive figure titles +figure["circle"].title = "sin(x*p) over cos(x*q)" +figure["sin"].title = "sin(x * p)" +figure["cos"].title = "cos(x * q)" + +# set the axes to intersect at (0, 0, 0) to better illustrate the unit circle +for subplot in figure: + subplot.axes.intersection = (0, 0, 0) + subplot.toolbar = False # reduce clutter + +figure["sin"].camera.maintain_aspect = False +figure["cos"].camera.maintain_aspect = False + +# create sine and cosine data +xs = np.linspace(0, 2 * np.pi, 360) +sine = np.sin(xs * P) +cosine = np.cos(xs * Q) + +# circle data +circle_data = make_circle(center=(0, 0), p=P, q=Q, radius=1, n_points=360) + +# make the circle line graphic, set the cmap transform using the sine function +circle_graphic = figure["circle"].add_line( + circle_data, thickness=4, cmap="bwr", cmap_transform=sine +) + +# line to show the circle radius +# use it to indicate the current position of the sine and cosine selctors (below) +radius_data = np.array([[0, 0, 0], [*circle_data[0], 0]]) +circle_radius_graphic = figure["circle"].add_line( + radius_data, thickness=6, colors="magenta" +) + +# sine line graphic, cmap transform set from the sine function +sine_graphic = figure["sin"].add_line( + sine, thickness=10, cmap="bwr", cmap_transform=sine +) + +# cosine line graphic, cmap transform set from the sine function +# illustrates the sine function values on the cosine graphic +cosine_graphic = figure["cos"].add_line( + cosine, thickness=10, cmap="bwr", cmap_transform=sine +) + +# add linear selectors to the sine and cosine line graphics +sine_selector = sine_graphic.add_linear_selector() +cosine_selector = cosine_graphic.add_linear_selector() + + +def set_circle_cmap(ev): + # sets the cmap transforms + + cmap_transform = ev.graphic.data[:, 1] # y-val data of the sine or cosine graphic + for g in [sine_graphic, cosine_graphic]: + g.cmap.transform = cmap_transform + + # set circle cmap transform + circle_graphic.cmap.transform = cmap_transform + +# when the sine or cosine graphic is clicked, the cmap_transform +# of the sine, cosine and circle line graphics are all set from +# the y-values of the clicked line +sine_graphic.add_event_handler(set_circle_cmap, "click") +cosine_graphic.add_event_handler(set_circle_cmap, "click") + + +def set_x_val(ev): + # used to sync the two selectors + value = ev.info["value"] + index = ev.get_selected_index() + + sine_selector.selection = value + cosine_selector.selection = value + + circle_radius_graphic.data[1, :-1] = circle_data[index] + +# add same event handler to both graphics +sine_selector.add_event_handler(set_x_val, "selection") +cosine_selector.add_event_handler(set_x_val, "selection") + +# initial selection value +sine_selector.selection = 50 + + +class GUIWindow(EdgeWindow): + def __init__(self, figure, size, location, title): + super().__init__(figure=figure, size=size, location=location, title=title) + + self._p = 1 + self._q = 1 + + def _set_data(self): + global sine_graphic, cosine_graphic, circle_graphic, circle_radius_graphic, circle_data + + # make new data + sine = np.sin(xs * self._p) + cosine = np.cos(xs * self._q) + circle_data = make_circle(center=(0, 0), p=self._p, q=self._q, radius=1, n_points=360) + + + # set the graphics + sine_graphic.data[:, 1] = sine + cosine_graphic.data[:, 1] = cosine + circle_graphic.data[:, :2] = circle_data + circle_radius_graphic.data[1, :-1] = circle_data[sine_selector.get_selected_index()] + + def update(self): + flag_set_data = False + + changed, self._p = imgui.input_int("P", v=self._p, step_fast=2) + if changed: + flag_set_data = True + + changed, self._q = imgui.input_int("Q", v=self._q, step_fast=2) + if changed: + flag_set_data = True + + if flag_set_data: + self._set_data() + + +gui = GUIWindow( + figure=figure, + size=100, + location="right", + title="Freq. coeffs" +) + +figure.add_gui(gui) + +figure.show() + + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.loop.run() diff --git a/fastplotlib/layouts/_imgui_figure.py b/fastplotlib/layouts/_imgui_figure.py index 40145fe50..b0267dc75 100644 --- a/fastplotlib/layouts/_imgui_figure.py +++ b/fastplotlib/layouts/_imgui_figure.py @@ -150,7 +150,7 @@ def _draw_imgui(self) -> imgui.ImDrawData: def add_gui(self, gui: EdgeWindow): """ - Add a GUI to the Figure. GUIs can be added to the top, bottom, left or right edge. + Add a GUI to the Figure. GUIs can be added to the left or bottom edge. Parameters ---------- @@ -191,25 +191,15 @@ def get_pygfx_render_area(self, *args) -> tuple[int, int, int, int]: width, height = self.canvas.get_logical_size() - for edge in ["left", "right"]: + for edge in ["right"]: if self.guis[edge]: width -= self._guis[edge].size - for edge in ["top", "bottom"]: + for edge in ["bottom"]: if self.guis[edge]: height -= self._guis[edge].size - if self.guis["left"]: - xpos = self.guis["left"].size - else: - xpos = 0 - - if self.guis["top"]: - ypos = self.guis["top"].size - else: - ypos = 0 - - return xpos, ypos, max(1, width), max(1, height) + return 0, 0, max(1, width), max(1, height) def register_popup(self, popup: Popup.__class__): """ diff --git a/fastplotlib/ui/_base.py b/fastplotlib/ui/_base.py index 6c134d415..e31dd8d4a 100644 --- a/fastplotlib/ui/_base.py +++ b/fastplotlib/ui/_base.py @@ -6,7 +6,7 @@ from ..layouts._figure import Figure -GUI_EDGES = ["top", "right", "bottom", "left"] +GUI_EDGES = ["right", "bottom"] class BaseGUI: @@ -40,7 +40,7 @@ def __init__( self, figure: Figure, size: int, - location: Literal["top", "bottom", "left", "right"], + location: Literal["bottom", "right"], title: str, window_flags: int = imgui.WindowFlags_.no_collapse | imgui.WindowFlags_.no_resize, @@ -48,7 +48,7 @@ def __init__( **kwargs, ): """ - A base class for imgui windows displayed at one of the four edges of a Figure + A base class for imgui windows displayed at the bottom or top edge of a Figure Parameters ---------- @@ -58,7 +58,7 @@ def __init__( size: int width or height of the window, depending on its location - location: str, "top" | "bottom" | "left" | "right" + location: str, "bottom" | "right" location of the window title: str @@ -168,10 +168,6 @@ def get_rect(self) -> tuple[int, int, int, int]: width_canvas, height_canvas = self._figure.canvas.get_logical_size() match self._location: - case "top": - x_pos, y_pos = (0, 0) - width, height = (width_canvas, self.size) - case "bottom": x_pos = 0 y_pos = height_canvas - self.size @@ -179,22 +175,8 @@ def get_rect(self) -> tuple[int, int, int, int]: case "right": x_pos, y_pos = (width_canvas - self.size, 0) - - if self._figure.guis["top"]: - # if there is a GUI in the top edge, make this one below - y_pos += self._figure.guis["top"].size - width, height = (self.size, height_canvas) - if self._figure.guis["bottom"] is not None: - height -= self._figure.guis["bottom"].size - case "left": - x_pos, y_pos = (0, 0) - if self._figure.guis["top"]: - # if there is a GUI in the top edge, make this one below - y_pos += self._figure.guis["top"].size - - width, height = (self.size, height_canvas) if self._figure.guis["bottom"] is not None: height -= self._figure.guis["bottom"].size @@ -203,8 +185,11 @@ def get_rect(self) -> tuple[int, int, int, int]: def draw_window(self): """helps simplify using imgui by managing window creation & position, and pushing/popping the ID""" # window position & size + x, y, w, h = self.get_rect() imgui.set_next_window_size((self.width, self.height)) imgui.set_next_window_pos((self.x, self.y)) + # imgui.set_next_window_pos((x, y)) + # imgui.set_next_window_size((w, h)) flags = self._window_flags # begin window diff --git a/fastplotlib/ui/right_click_menus/_standard_menu.py b/fastplotlib/ui/right_click_menus/_standard_menu.py index 53a553f34..4bb59c51d 100644 --- a/fastplotlib/ui/right_click_menus/_standard_menu.py +++ b/fastplotlib/ui/right_click_menus/_standard_menu.py @@ -31,7 +31,7 @@ def __init__(self, figure, fa_icons): # whether the right click menu is currently open or not self.is_open: bool = False - def get_subplot(self) -> PlotArea | bool: + def get_subplot(self) -> PlotArea | bool | None: """get the subplot that a click occurred in""" if self._last_right_click_pos is None: return False @@ -40,6 +40,9 @@ def get_subplot(self) -> PlotArea | bool: if subplot.viewport.is_inside(*self._last_right_click_pos): return subplot + # not inside a subplot + return False + def cleanup(self): """called when the popup disappears""" self.is_open = False From bc002d1e06c94480af57eaab794a19ecd623567e Mon Sep 17 00:00:00 2001 From: Amol Pasarkar Date: Mon, 19 May 2025 21:38:38 -0400 Subject: [PATCH 179/185] vertex_thickness -> vertex_size to be consistent with names of scatter property names (#815) * vertex_thickness -> vertex_size * black --------- Co-authored-by: Kushal Kolar --- fastplotlib/graphics/selectors/_rectangle.py | 22 ++++++++------------ 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/fastplotlib/graphics/selectors/_rectangle.py b/fastplotlib/graphics/selectors/_rectangle.py index fcf4467cb..8fecb6af5 100644 --- a/fastplotlib/graphics/selectors/_rectangle.py +++ b/fastplotlib/graphics/selectors/_rectangle.py @@ -60,7 +60,7 @@ def __init__( edge_color=(0.8, 0.6, 0), edge_thickness: float = 8, vertex_color=(0.7, 0.4, 0), - vertex_thickness: float = 8, + vertex_size: float = 8, arrow_keys_modifier: str = "Shift", name: str = None, ): @@ -211,10 +211,10 @@ def __init__( bottom_right_vertex_data = (xmax, ymin, 1) top_left_vertex = pygfx.Points( - pygfx.Geometry(positions=[top_left_vertex_data], sizes=[vertex_thickness]), + pygfx.Geometry(positions=[top_left_vertex_data], sizes=[vertex_size]), pygfx.PointsMarkerMaterial( marker="square", - size=vertex_thickness, + size=vertex_size, color=self.vertex_color, size_mode="vertex", edge_color=self.vertex_color, @@ -222,10 +222,10 @@ def __init__( ) top_right_vertex = pygfx.Points( - pygfx.Geometry(positions=[top_right_vertex_data], sizes=[vertex_thickness]), + pygfx.Geometry(positions=[top_right_vertex_data], sizes=[vertex_size]), pygfx.PointsMarkerMaterial( marker="square", - size=vertex_thickness, + size=vertex_size, color=self.vertex_color, size_mode="vertex", edge_color=self.vertex_color, @@ -233,12 +233,10 @@ def __init__( ) bottom_left_vertex = pygfx.Points( - pygfx.Geometry( - positions=[bottom_left_vertex_data], sizes=[vertex_thickness] - ), + pygfx.Geometry(positions=[bottom_left_vertex_data], sizes=[vertex_size]), pygfx.PointsMarkerMaterial( marker="square", - size=vertex_thickness, + size=vertex_size, color=self.vertex_color, size_mode="vertex", edge_color=self.vertex_color, @@ -246,12 +244,10 @@ def __init__( ) bottom_right_vertex = pygfx.Points( - pygfx.Geometry( - positions=[bottom_right_vertex_data], sizes=[vertex_thickness] - ), + pygfx.Geometry(positions=[bottom_right_vertex_data], sizes=[vertex_size]), pygfx.PointsMarkerMaterial( marker="square", - size=vertex_thickness, + size=vertex_size, color=self.vertex_color, size_mode="vertex", edge_color=self.vertex_color, From 0e1a308d2e14556b783d9ae075353430ce157418 Mon Sep 17 00:00:00 2001 From: Flynn <75346097+FlynnOConnell@users.noreply.github.com> Date: Mon, 19 May 2025 21:41:44 -0400 Subject: [PATCH 180/185] coerce dtype=np.int64 for np.prod calls in subsample (#806) * coerce dtype=np.int64 for np.prod calls in subsample * fix: tuple is immutable * change arr.shape calls to full_shape as uint64 --- fastplotlib/utils/functions.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/fastplotlib/utils/functions.py b/fastplotlib/utils/functions.py index d6d996a45..45678aa5e 100644 --- a/fastplotlib/utils/functions.py +++ b/fastplotlib/utils/functions.py @@ -455,18 +455,19 @@ def subsample_array( np.ndarray subsample of the input array """ - if np.prod(arr.shape) <= max_size: + full_shape = np.array(arr.shape, dtype=np.uint64) + if np.prod(full_shape) <= max_size: return arr[:] # no need to subsample if already below the threshold # get factor by which to divide all dims - f = np.power((np.prod(arr.shape) / max_size), 1.0 / arr.ndim) + f = np.power((np.prod(full_shape) / max_size), 1.0 / arr.ndim) # new shape for subsampled array - ns = np.floor(np.array(arr.shape) / f).clip(min=1) + ns = np.floor(np.array(full_shape) / f).clip(min=1) # get the step size for the slices - slices = tuple( - slice(None, None, int(s)) for s in np.floor(arr.shape / ns).astype(int) + slices = list( + slice(None, None, int(s)) for s in np.floor(full_shape / ns).astype(int) ) # ignore dims e.g. RGB, which we don't want to downsample From 0de38a7f2648aab23657b27bd418b18a524283f3 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Tue, 20 May 2025 07:42:47 -0400 Subject: [PATCH 181/185] docstring and docs cleanup, other misc small things (#828) * rename cleanup, docstring cleanup * docstring cleanup * docstrings * more docstring * docstring * image docstrings * scatter docstrings * rename * text docstrings * yet more docstrings * another gridplot rename * better Figure names handling * properly remove padding kwarg * update quickstart.ipynb * black * Update fastplotlib/graphics/image.py Co-authored-by: Caitlin Lewis <69729525+clewis7@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Caitlin Lewis <69729525+clewis7@users.noreply.github.com> * better names logic * update example * sphinx is annoying * update add_graphic methods mixin * update generate script --------- Co-authored-by: Caitlin Lewis <69729525+clewis7@users.noreply.github.com> --- examples/controllers/specify_integers.py | 6 +- examples/gridplot/README.rst | 4 +- examples/gridplot/gridplot.py | 6 +- examples/gridplot/gridplot_non_square.py | 6 +- examples/gridplot/gridplot_viewports_check.py | 4 +- examples/gridplot/multigraphic_gridplot.py | 6 +- examples/misc/multiplot_animation.py | 6 +- examples/notebooks/quickstart.ipynb | 59 +++++++----- examples/scatter/scatter_size.py | 9 +- fastplotlib/graphics/_positions_base.py | 10 ++- fastplotlib/graphics/image.py | 53 +++++------ fastplotlib/graphics/line.py | 48 +++++----- fastplotlib/graphics/scatter.py | 26 +++--- fastplotlib/graphics/selectors/_rectangle.py | 7 +- fastplotlib/graphics/text.py | 14 +-- fastplotlib/layouts/_figure.py | 67 ++++++++++---- fastplotlib/layouts/_graphic_methods_mixin.py | 54 ++++++----- fastplotlib/utils/functions.py | 3 +- fastplotlib/widgets/image_widget/_widget.py | 2 +- scripts/generate_add_graphic_methods.py | 15 ++-- tests/test_figure.py | 90 +++++++++++++++++++ 21 files changed, 328 insertions(+), 167 deletions(-) diff --git a/examples/controllers/specify_integers.py b/examples/controllers/specify_integers.py index 128f240ad..14b09b015 100644 --- a/examples/controllers/specify_integers.py +++ b/examples/controllers/specify_integers.py @@ -24,15 +24,15 @@ [2, 0], ] -names = [f"contr. id: {i}" for i in np.asarray(ids).ravel()] - figure = fpl.Figure( shape=(2, 2), controller_ids=ids, - names=names, size=(700, 560), ) +for subplot, controller_id in zip(figure, np.asarray(ids).ravel()): + subplot.title = f"contr. id: {controller_id}" + figure[0, 0].add_line(np.column_stack([xs, sine])) figure[0, 1].add_line(np.random.rand(100)) diff --git a/examples/gridplot/README.rst b/examples/gridplot/README.rst index 486e708e7..0a2cc1828 100644 --- a/examples/gridplot/README.rst +++ b/examples/gridplot/README.rst @@ -1,2 +1,2 @@ -GridPlot Examples -================= +Grid layout Examples +==================== diff --git a/examples/gridplot/gridplot.py b/examples/gridplot/gridplot.py index 5c38d6d43..af4d82408 100644 --- a/examples/gridplot/gridplot.py +++ b/examples/gridplot/gridplot.py @@ -1,8 +1,8 @@ """ -GridPlot Simple -=============== +Grid layout Simple +================== -Example showing simple 2x2 GridPlot with Standard images from imageio. +Example showing simple 2x2 grid layout with standard images from imageio. """ # test_example = true diff --git a/examples/gridplot/gridplot_non_square.py b/examples/gridplot/gridplot_non_square.py index 0277bcccd..e8ce15b7b 100644 --- a/examples/gridplot/gridplot_non_square.py +++ b/examples/gridplot/gridplot_non_square.py @@ -1,8 +1,8 @@ """ -GridPlot Non-Square Example -=========================== +Grid Layout 2 +============= -Example showing simple 2x2 GridPlot with Standard images from imageio. +Simple 2x2 grid layout Figure with standard images from imageio, one subplot is left empty """ # test_example = true diff --git a/examples/gridplot/gridplot_viewports_check.py b/examples/gridplot/gridplot_viewports_check.py index 99584b411..496204b98 100644 --- a/examples/gridplot/gridplot_viewports_check.py +++ b/examples/gridplot/gridplot_viewports_check.py @@ -1,6 +1,6 @@ """ -GridPlot test viewport rects -============================ +Grid layout test viewport rects +=============================== Test figure to test that viewport rects are positioned correctly """ diff --git a/examples/gridplot/multigraphic_gridplot.py b/examples/gridplot/multigraphic_gridplot.py index 1bed60b31..8408f4f23 100644 --- a/examples/gridplot/multigraphic_gridplot.py +++ b/examples/gridplot/multigraphic_gridplot.py @@ -1,8 +1,8 @@ """ -Multi-Graphic GridPlot -====================== +Multi-Graphic Grid layout +========================= -Example showing a Figure with multiple subplots and multiple graphic types. +A Figure with multiple subplots and multiple graphic types. """ # test_example = false diff --git a/examples/misc/multiplot_animation.py b/examples/misc/multiplot_animation.py index 18512add1..4eb9399f8 100644 --- a/examples/misc/multiplot_animation.py +++ b/examples/misc/multiplot_animation.py @@ -2,7 +2,7 @@ Multi-Subplot Image Update ========================== -Example showing updating a multiple subplots with new random 512x512 data. +Multiple subplots with an image that updates with new data on every render. """ # test_example = false @@ -27,7 +27,7 @@ figure[1,1]["rand-img"].cmap = "spring" # Define a function to update the image graphics with new data -# add_animations will pass the gridplot to the animation function +# add_animations will pass the figure to the animation function def update_data(f): for subplot in f: new_data = np.random.rand(512, 512) @@ -37,7 +37,7 @@ def update_data(f): # add the animation function figure.add_animations(update_data) -# show the gridplot +# show the figure figure.show() diff --git a/examples/notebooks/quickstart.ipynb b/examples/notebooks/quickstart.ipynb index 737aee3e7..0d8fc3c31 100644 --- a/examples/notebooks/quickstart.ipynb +++ b/examples/notebooks/quickstart.ipynb @@ -463,7 +463,7 @@ "id": "5694dca1-1041-4e09-a1da-85b293c5af47", "metadata": {}, "source": [ - "### RGB images are also supported\n", + "### RGB(A) images are supported\n", "\n", "`cmap` arguments are ignored for rgb images, but vmin vmax still works" ] @@ -538,7 +538,7 @@ "source": [ "### Image updates\n", "\n", - "This examples show how you can define animation functions that run on every render cycle." + "This example shows how you can define animation functions that run on every render cycle." ] }, { @@ -620,7 +620,7 @@ "id": "f226c9c2-8d0e-41ab-9ab9-1ae31fd91de5", "metadata": {}, "source": [ - "#### Keeping a reference to the Graphic instance, as shown above `image_graphic_instance`, is useful if you're creating something where you need flexibility in the naming of the graphics" + "#### Keeping a reference to the Graphic instance, as shown above `image_graphic_instance`, is useful if you're creating something where it is convenient to keep your own reference to a `Graphic`" ] }, { @@ -628,7 +628,7 @@ "id": "d11fabb7-7c76-4e94-893d-80ed9ee3be3d", "metadata": {}, "source": [ - "### You can also use `ipywidgets.VBox` and `HBox` to stack plots. See the `subplot` notebooks for more automated subplotting" + "### You can also use `ipywidgets.VBox` and `HBox` to stack plots." ] }, { @@ -664,7 +664,7 @@ "\n", "## 2D line plots\n", "\n", - "This example plots a sine wave, cosine wave, and ricker wavelet and demonstrates how **Graphic Features** can be modified by slicing!" + "This example plots a sine wave, cosine wave, and ricker wavelet and demonstrates how **Graphic Properties** can be modified by slicing!" ] }, { @@ -755,7 +755,7 @@ "\n", "Set `maintain_aspect = False` on a camera, and then use the right mouse button and move the mouse to stretch and squeeze the view!\n", "\n", - "You can also click the **`1:1`** button to toggle this, or use `subplot.camera.maintain_aspect`" + "You can also click the **`⛶`** button to toggle this, or use `subplot.camera.maintain_aspect`" ] }, { @@ -763,7 +763,7 @@ "id": "1651e965-f750-47ac-bf53-c23dae84cc98", "metadata": {}, "source": [ - "### reset the plot area" + "### reset the plot area camera" ] }, { @@ -783,7 +783,9 @@ "id": "dcd68796-c190-4c3f-8519-d73b98ff6367", "metadata": {}, "source": [ - "## Graphic features support slicing! :D " + "## Graphic properties support slicing! :D\n", + "\n", + "Data, colors, and cmaps can often be sliced just like arrays to set or get values!" ] }, { @@ -811,7 +813,7 @@ "id": "c9689887-cdf3-4a4d-948f-7efdb09bde4e", "metadata": {}, "source": [ - "## You can capture changes to a graphic feature as events" + "## Graphic properties are _evented_, so you can capture when they change" ] }, { @@ -1551,7 +1553,7 @@ " subplot.add_image(data, name=\"rand-img\")\n", "\n", "# Define a function to update the image graphics with new data\n", - "# add_animations will pass the gridplot to the animation function\n", + "# add_animations will pass the figure to the animation function\n", "def update_data(f):\n", " for subplot in f:\n", " new_data = np.random.rand(512, 512)\n", @@ -1561,7 +1563,7 @@ "# add the animation function\n", "figure_grid.add_animations(update_data)\n", "\n", - "# show the gridplot\n", + "# show the figure\n", "figure_grid.show()" ] }, @@ -1575,7 +1577,7 @@ } }, "source": [ - "### Slicing GridPlot" + "### Slicing a grid layout to get subplots" ] }, { @@ -1605,7 +1607,7 @@ } }, "source": [ - "You can get the graphics within a subplot, just like with simple `Plot`" + "You can get the graphics within a subplot" ] }, { @@ -1661,7 +1663,7 @@ } }, "source": [ - "more slicing with `GridPlot`" + "more slicing with a `Figure` that has a grid layout" ] }, { @@ -1707,7 +1709,7 @@ }, "outputs": [], "source": [ - "# these are really the same\n", + "# these are the same\n", "figure_grid[\"top-right-plot\"] is figure_grid[0, 2]" ] }, @@ -1749,7 +1751,7 @@ } }, "source": [ - "## Figure subplot customization" + "## Figure subplot customization in a grid layout" ] }, { @@ -1776,13 +1778,13 @@ "]\n", "\n", "\n", - "# you can give string names for each subplot within the gridplot\n", + "# you can give string names for each subplot within the figure\n", "names = [\n", " [\"subplot0\", \"subplot1\", \"subplot2\"],\n", " [\"subplot3\", \"subplot4\", \"subplot5\"]\n", "]\n", "\n", - "# Create the grid plot\n", + "# Create the figure\n", "figure_grid = fpl.Figure(\n", " shape=shape,\n", " controller_ids=controller_ids,\n", @@ -1819,7 +1821,7 @@ } }, "source": [ - "Indexing the gridplot to access subplots" + "Slicing/indexing the figure to get subplots" ] }, { @@ -1834,7 +1836,7 @@ }, "outputs": [], "source": [ - "# can access subplot by name\n", + "# get subplot by name\n", "figure_grid[\"subplot0\"]" ] }, @@ -1850,7 +1852,7 @@ }, "outputs": [], "source": [ - "# can access subplot by index\n", + "# or get subplot by index\n", "figure_grid[0, 0]" ] }, @@ -1864,7 +1866,7 @@ } }, "source": [ - "**subplots also support indexing!**\n", + "**from before, remember subplots themselves also support slicing to get graphics within them!**\n", "\n", "this can be used to get graphics if they are named" ] @@ -1885,6 +1887,17 @@ "figure_grid[\"subplot0\"][\"rand-image\"]" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "87905450bdc0ec0a", + "metadata": {}, + "outputs": [], + "source": [ + "# or by their numerical index\n", + "figure_grid[\"subplot0\"].graphics[0]" + ] + }, { "cell_type": "code", "execution_count": null, @@ -1911,7 +1924,7 @@ } }, "source": [ - "positional indexing also works event if subplots have string names" + "positional indexing also works even if subplots have string names" ] }, { diff --git a/examples/scatter/scatter_size.py b/examples/scatter/scatter_size.py index 73be31f62..c982e0220 100644 --- a/examples/scatter/scatter_size.py +++ b/examples/scatter/scatter_size.py @@ -2,7 +2,10 @@ Scatter Plot Size ================= -Example showing point size change for scatter plot. +Example that shows how to set scatter sizes in two different ways. + +One subplot uses a single scaler value for every point, and another subplot uses an array that defines the size for +each individual scatter point. """ # test_example = true @@ -14,10 +17,10 @@ # figure with 2 rows and 3 columns shape = (2, 1) -# you can give string names for each subplot within the gridplot +# you can give string names for each subplot within the figure names = [["scalar_size"], ["array_size"]] -# Create the grid plot +# Create the figure figure = fpl.Figure(shape=shape, names=names, size=(700, 560)) # get y_values using sin function diff --git a/fastplotlib/graphics/_positions_base.py b/fastplotlib/graphics/_positions_base.py index 5d98d16d1..8b127aa19 100644 --- a/fastplotlib/graphics/_positions_base.py +++ b/fastplotlib/graphics/_positions_base.py @@ -19,7 +19,7 @@ class PositionsGraphic(Graphic): @property def data(self) -> VertexPositions: - """Get or set the vertex positions data""" + """Get or set the graphic's data""" return self._data @data.setter @@ -28,7 +28,7 @@ def data(self, value): @property def colors(self) -> VertexColors | pygfx.Color: - """Get or set the colors data""" + """Get or set the colors""" if isinstance(self._colors, VertexColors): return self._colors @@ -45,7 +45,11 @@ def colors(self, value: str | np.ndarray | tuple[float] | list[float] | list[str @property def cmap(self) -> VertexCmap: - """Control the cmap, cmap transform, or cmap alpha""" + """ + Control the cmap, cmap transform, or cmap alpha + + For supported colormaps see the ``cmap`` library catalogue: https://cmap-docs.readthedocs.io/en/stable/catalog/ + """ return self._cmap @cmap.setter diff --git a/fastplotlib/graphics/image.py b/fastplotlib/graphics/image.py index bebdbbf6d..b2a8048b3 100644 --- a/fastplotlib/graphics/image.py +++ b/fastplotlib/graphics/image.py @@ -107,7 +107,8 @@ def __init__( maximum value for color scaling, calculated from data if not provided cmap: str, optional, default "plasma" - colormap to use to display the data + colormap to use to display the data. For supported colormaps see the + ``cmap`` library catalogue: https://cmap-docs.readthedocs.io/en/stable/catalog/ interpolation: str, optional, default "nearest" interpolation filter, one of "nearest" or "linear" @@ -118,7 +119,8 @@ def __init__( isolated_buffer: bool, default True If True, initialize a buffer with the same shape as the input data and then set the data, useful if the data arrays are ready-only such as memmaps. - If False, the input array is itself used as the buffer. + If False, the input array is itself used as the buffer - useful if the + array is large. kwargs: additional keyword arguments passed to Graphic @@ -200,7 +202,11 @@ def data(self, data): @property def cmap(self) -> str: - """colormap name""" + """ + Get or set the colormap + + For supported colormaps see the ``cmap`` library catalogue: https://cmap-docs.readthedocs.io/en/stable/catalog/ + """ if self.data.value.ndim > 2: raise AttributeError("RGB(A) images do not have a colormap property") return self._cmap.value @@ -231,7 +237,7 @@ def vmax(self, value: float): @property def interpolation(self) -> str: - """image data interpolation method""" + """Data interpolation method""" return self._interpolation.value @interpolation.setter @@ -249,12 +255,7 @@ def cmap_interpolation(self, value: str): def reset_vmin_vmax(self): """ - Reset the vmin, vmax by estimating it from the data - - Returns - ------- - None - + Reset the vmin, vmax by estimating it from the data by subsampling. """ vmin, vmax = quick_min_max(self._data.value) @@ -262,19 +263,19 @@ def reset_vmin_vmax(self): self.vmax = vmax def add_linear_selector( - self, selection: int = None, axis: str = "x", padding: float = None, **kwargs + self, selection: int = None, axis: str = "x", **kwargs ) -> LinearSelector: """ Adds a :class:`.LinearSelector`. + Selectors are just ``Graphic`` objects, so you can manage, remove, or delete them + from a plot area just like any other ``Graphic``. + Parameters ---------- selection: int, optional initial position of the selector - padding: float, optional - pad the length of the selector - kwargs: passed to :class:`.LinearSelector` @@ -285,22 +286,12 @@ def add_linear_selector( """ if axis == "x": - size = self._data.value.shape[0] - center = size / 2 limits = (0, self._data.value.shape[1]) elif axis == "y": - size = self._data.value.shape[1] - center = size / 2 limits = (0, self._data.value.shape[0]) else: raise ValueError("`axis` must be one of 'x' | 'y'") - # default padding is 25% the height or width of the image - if padding is None: - size *= 1.25 - else: - size += padding - if selection is None: selection = limits[0] @@ -333,8 +324,10 @@ def add_linear_region_selector( **kwargs, ) -> LinearRegionSelector: """ - Add a :class:`.LinearRegionSelector`. Selectors are just ``Graphic`` objects, so you can manage, - remove, or delete them from a plot area just like any other ``Graphic``. + Add a :class:`.LinearRegionSelector`. + + Selectors are just ``Graphic`` objects, so you can manage, remove, or delete them + from a plot area just like any other ``Graphic``. Parameters ---------- @@ -353,7 +346,6 @@ def add_linear_region_selector( Returns ------- LinearRegionSelector - linear selection graphic """ @@ -408,13 +400,16 @@ def add_rectangle_selector( **kwargs, ) -> RectangleSelector: """ - Add a :class:`.RectangleSelector`. Selectors are just ``Graphic`` objects, so you can manage, - remove, or delete them from a plot area just like any other ``Graphic``. + Add a :class:`.RectangleSelector`. + + Selectors are just ``Graphic`` objects, so you can manage, remove, or delete them + from a plot area just like any other ``Graphic``. Parameters ---------- selection: (float, float, float, float), optional initial (xmin, xmax, ymin, ymax) of the selection + """ # default selection is 25% of the diagonal if selection is None: diff --git a/fastplotlib/graphics/line.py b/fastplotlib/graphics/line.py index 09410e9fd..ab5b94146 100644 --- a/fastplotlib/graphics/line.py +++ b/fastplotlib/graphics/line.py @@ -30,11 +30,11 @@ def __init__( self, data: Any, thickness: float = 2.0, - colors: str | np.ndarray | Iterable = "w", + colors: str | np.ndarray | Sequence = "w", uniform_color: bool = False, alpha: float = 1.0, cmap: str = None, - cmap_transform: np.ndarray | Iterable = None, + cmap_transform: np.ndarray | Sequence = None, isolated_buffer: bool = True, size_space: str = "screen", **kwargs, @@ -45,14 +45,17 @@ def __init__( Parameters ---------- data: array-like - Line data to plot, 2D must be of shape [n_points, 2], 3D must be of shape [n_points, 3] + Line data to plot. Can provide 1D, 2D, or a 3D data. + | If passing a 1D array, it is used to set the y-values and the x-values are generated as an integer range + from [0, data.size] + | 2D data must be of shape [n_points, 2]. 3D data must be of shape [n_points, 3] thickness: float, optional, default 2.0 thickness of the line colors: str, array, or iterable, default "w" specify colors as a single human-readable string, a single RGBA array, - or an iterable of strings or RGBA arrays + or a Sequence (array, tuple, or list) of strings or RGBA arrays uniform_color: bool, default ``False`` if True, uses a uniform buffer for the line color, @@ -62,14 +65,15 @@ def __init__( alpha value for the colors cmap: str, optional - apply a colormap to the line instead of assigning colors manually, this - overrides any argument passed to "colors" + Apply a colormap to the line instead of assigning colors manually, this + overrides any argument passed to "colors". For supported colormaps see the + ``cmap`` library catalogue: https://cmap-docs.readthedocs.io/en/stable/catalog/ cmap_transform: 1D array-like of numerical values, optional if provided, these values are used to map the colors from the cmap size_space: str, default "screen" - coordinate space in which the size is expressed ("screen", "world", "model") + coordinate space in which the thickness is expressed ("screen", "world", "model") **kwargs passed to Graphic @@ -121,7 +125,7 @@ def __init__( @property def thickness(self) -> float: - """line thickness""" + """Get or set the line thickness""" return self._thickness.value @thickness.setter @@ -129,24 +133,22 @@ def thickness(self, value: float): self._thickness.set_value(self, value) def add_linear_selector( - self, selection: float = None, padding: float = 0.0, axis: str = "x", **kwargs + self, selection: float = None, axis: str = "x", **kwargs ) -> LinearSelector: """ - Adds a linear selector. + Adds a :class:`.LinearSelector`. + + Selectors are just ``Graphic`` objects, so you can manage, remove, or delete them from a + plot area just like any other ``Graphic``. - Parameters - ---------- Parameters ---------- selection: float, optional - selected point on the linear selector, computed from data if not provided + selected point on the linear selector, by default the first datapoint on the line. axis: str, default "x" axis that the selector resides on - padding: float, default 0.0 - Extra padding to extend the linear selector along the orthogonal axis to make it easier to interact with. - kwargs passed to :class:`.LinearSelector` @@ -157,7 +159,7 @@ def add_linear_selector( """ bounds_init, limits, size, center = self._get_linear_selector_init_args( - axis, padding + axis, padding=0 ) if selection is None: @@ -186,8 +188,10 @@ def add_linear_region_selector( **kwargs, ) -> LinearRegionSelector: """ - Add a :class:`.LinearRegionSelector`. Selectors are just ``Graphic`` objects, so you can manage, - remove, or delete them from a plot area just like any other ``Graphic``. + Add a :class:`.LinearRegionSelector`. + + Selectors are just ``Graphic`` objects, so you can manage, remove, or delete them from a + plot area just like any other ``Graphic``. Parameters ---------- @@ -243,8 +247,10 @@ def add_rectangle_selector( **kwargs, ) -> RectangleSelector: """ - Add a :class:`.RectangleSelector`. Selectors are just ``Graphic`` objects, so you can manage, - remove, or delete them from a plot area just like any other ``Graphic``. + Add a :class:`.RectangleSelector`. + + Selectors are just ``Graphic`` objects, so you can manage, remove, or delete them from a + plot area just like any other ``Graphic``. Parameters ---------- diff --git a/fastplotlib/graphics/scatter.py b/fastplotlib/graphics/scatter.py index a8479bbf6..7fd09ffca 100644 --- a/fastplotlib/graphics/scatter.py +++ b/fastplotlib/graphics/scatter.py @@ -33,7 +33,7 @@ def __init__( cmap: str = None, cmap_transform: np.ndarray = None, isolated_buffer: bool = True, - sizes: float | np.ndarray | Iterable[float] = 1, + sizes: float | np.ndarray | Sequence[float] = 1, uniform_size: bool = False, size_space: str = "screen", **kwargs, @@ -44,36 +44,38 @@ def __init__( Parameters ---------- data: array-like - Scatter data to plot, 2D must be of shape [n_points, 2], 3D must be of shape [n_points, 3] + Scatter data to plot, Can provide 2D, or a 3D data. 2D data must be of shape [n_points, 2]. + 3D data must be of shape [n_points, 3] - colors: str, array, or iterable, default "w" - specify colors as a single human readable string, a single RGBA array, - or an iterable of strings or RGBA arrays + colors: str, array, tuple, list, Sequence, default "w" + specify colors as a single human-readable string, a single RGBA array, + or a Sequence (array, tuple, or list) of strings or RGBA arrays uniform_color: bool, default False - if True, uses a uniform buffer for the scatter point colors, - basically saves GPU VRAM when the entire line has a single color + if True, uses a uniform buffer for the scatter point colors. Useful if you need to + save GPU VRAM when all points have the same color. alpha: float, optional, default 1.0 alpha value for the colors cmap: str, optional apply a colormap to the scatter instead of assigning colors manually, this - overrides any argument passed to "colors" + overrides any argument passed to "colors". For supported colormaps see the + ``cmap`` library catalogue: https://cmap-docs.readthedocs.io/en/stable/catalog/ cmap_transform: 1D array-like or list of numerical values, optional if provided, these values are used to map the colors from the cmap isolated_buffer: bool, default True whether the buffers should be isolated from the user input array. - Generally always ``True``, ``False`` is for rare advanced use. + Generally always ``True``, ``False`` is for rare advanced use if you have large arrays. sizes: float or iterable of float, optional, default 1.0 - size of the scatter points + sizes of the scatter points uniform_size: bool, default False - if True, uses a uniform buffer for the scatter point sizes, - basically saves GPU VRAM when all scatter points are the same size + if True, uses a uniform buffer for the scatter point sizes. Useful if you need to + save GPU VRAM when all points have the same size. size_space: str, default "screen" coordinate space in which the size is expressed ("screen", "world", "model") diff --git a/fastplotlib/graphics/selectors/_rectangle.py b/fastplotlib/graphics/selectors/_rectangle.py index 8fecb6af5..e3dd3887e 100644 --- a/fastplotlib/graphics/selectors/_rectangle.py +++ b/fastplotlib/graphics/selectors/_rectangle.py @@ -83,14 +83,17 @@ def __init__( if ``True``, the edges can be dragged to resize the selection fill_color: str, array, or tuple - fill color for the selector, passed to pygfx.Color + fill color for the selector as a str or RGBA array edge_color: str, array, or tuple - edge color for the selector, passed to pygfx.Color + edge color for the selector as a str or RGBA array edge_thickness: float, default 8 edge thickness + vertex_color: str, array, or tuple + vertex color for the selector as a str or RGBA array + arrow_keys_modifier: str modifier key that must be pressed to initiate movement using arrow keys, must be one of: "Control", "Shift", "Alt" or ``None`` diff --git a/fastplotlib/graphics/text.py b/fastplotlib/graphics/text.py index 70f9b2a43..fba3962ad 100644 --- a/fastplotlib/graphics/text.py +++ b/fastplotlib/graphics/text.py @@ -43,10 +43,10 @@ def __init__( font_size: float | int, default 10 font size - face_color: str or array, default "w" + face_color: str, array, list, tuple, default "w" str or RGBA array to set the color of the text - outline_color: str or array, default "w" + outline_color: str, array, list, tuple, default "w" str or RGBA array to set the outline color of the text outline_thickness: float, default 0 @@ -102,7 +102,7 @@ def world_object(self) -> pygfx.Text: @property def text(self) -> str: - """the text displayed""" + """Get or set the text""" return self._text.value @text.setter @@ -111,7 +111,7 @@ def text(self, text: str): @property def font_size(self) -> float | int: - """ "text font size""" + """Get or set the font size""" return self._font_size.value @font_size.setter @@ -120,7 +120,7 @@ def font_size(self, size: float | int): @property def face_color(self) -> pygfx.Color: - """text face color""" + """Get or set the face color""" return self._face_color.value @face_color.setter @@ -129,7 +129,7 @@ def face_color(self, color: str | np.ndarray | list[float] | tuple[float]): @property def outline_thickness(self) -> float: - """text outline thickness""" + """Get or set the outline thickness""" return self._outline_thickness.value @outline_thickness.setter @@ -138,7 +138,7 @@ def outline_thickness(self, thickness: float): @property def outline_color(self) -> pygfx.Color: - """text outline color""" + """Get or set the outline color""" return self._outline_color.value @outline_color.setter diff --git a/fastplotlib/layouts/_figure.py b/fastplotlib/layouts/_figure.py index a1bae965e..10322d240 100644 --- a/fastplotlib/layouts/_figure.py +++ b/fastplotlib/layouts/_figure.py @@ -102,9 +102,10 @@ def __init__( | this syncs subplot_a, subplot_b and subplot_e together; syncs subplot_c and subplot_d together controllers: pygfx.Controller | list[pygfx.Controller] | np.ndarray[pygfx.Controller], optional - directly provide pygfx.Controller instances(s). Useful if you want to use a controller from an existing - plot/subplot. Other controller kwargs, i.e. ``controller_types`` and ``controller_ids`` are ignored if - ``controllers`` are provided. + Directly provide pygfx.Controller instances(s). Useful if you want to use a ``Controller`` from an existing + subplot or a ``Controller`` you have already instantiated. Also useful if you want to provide a custom + ``Controller`` subclass. Other controller kwargs, i.e. ``controller_types`` and ``controller_ids`` + are ignored if `controllers` are provided. canvas: str, BaseRenderCanvas, pygfx.Texture Canvas to draw the figure onto, usually auto-selected based on running environment. @@ -144,7 +145,9 @@ def __init__( else: if not all(isinstance(v, (int, np.integer)) for v in shape): - raise TypeError("shape argument must be a tuple[n_rows, n_cols]") + raise TypeError( + f"shape argument must be a tuple[n_rows, n_cols], you have passed: {shape}" + ) n_subplots = shape[0] * shape[1] layout_mode = "grid" @@ -154,13 +157,40 @@ def __init__( rects = [None] * n_subplots if names is not None: + # user has specified subplot names subplot_names = np.asarray(names).flatten() - if subplot_names.size != n_subplots: + # make an array without nones for sanity checks + subplot_names_without_nones = subplot_names[subplot_names != np.array(None)] + + # make sure all names are unique + if ( + subplot_names_without_nones.size + != np.unique(subplot_names_without_nones).size + ): raise ValueError( - f"must provide same number of subplot `names` as specified by shape, extents, or rects: {n_subplots}" + f"subplot `names` must be unique, you have provided: {names}" + ) + + # check that there are enough subplots given the number of names + if subplot_names.size > n_subplots: + raise ValueError( + f"must provide same number or fewer subplot `names` than number of supblots specified by shape, " + f"extents, or rects." + f"You have specified {n_subplots} subplots, but {subplot_names.size} subplot names." + ) + + if subplot_names.size < n_subplots: + # pad the subplot names with nones + subplot_names = np.concatenate( + [ + subplot_names, + np.asarray([None] * (n_subplots - subplot_names.size)), + ] ) else: + # no user specified subplot names if layout_mode == "grid": + # make names that show the [row index, col index] subplot_names = np.asarray( list(map(str, product(range(shape[0]), range(shape[1])))) ) @@ -188,7 +218,7 @@ def __init__( if cameras.size != n_subplots: raise ValueError( - f"Number of cameras: {cameras.size} does not match the number of subplots: {n_subplots}" + f"Number of cameras: {cameras.size} does not match the number of specified subplots: {n_subplots}" ) # create the cameras @@ -213,8 +243,8 @@ def __init__( pass else: raise TypeError( - "controllers argument must be a single pygfx.Controller instance, or a Iterable of " - "pygfx.Controller instances" + f"controllers argument must be a single pygfx.Controller instance, or a Iterable of " + f"pygfx.Controller instances. You have passed: {controllers}" ) subplot_controllers: np.ndarray[pygfx.Controller] = np.asarray( @@ -242,7 +272,8 @@ def __init__( else: raise ValueError( f"`controller_ids` must be one of 'sync', an array/list of subplot names, or an array/list of " - f"integer ids. See the docstring for more details." + f"integer ids. You have passed: {controller_ids}.\n" + f"See the docstring for more details." ) # list controller_ids @@ -259,12 +290,14 @@ def __init__( # make sure each controller_id str is a subplot name if not all([n in subplot_names for n in ids_flat]): raise KeyError( - f"all `controller_ids` strings must be one of the subplot names" + f"all `controller_ids` strings must be one of the subplot names. You have passed " + f"the following `controller_ids`:\n{controller_ids}\n\n" + f"and the following subplot names:\n{subplot_names}" ) if len(ids_flat) > len(set(ids_flat)): raise ValueError( - "id strings must not appear twice in `controller_ids`" + f"id strings must not appear twice in `controller_ids`: \n{controller_ids}" ) # initialize controller_ids array @@ -284,7 +317,8 @@ def __init__( controller_ids = np.asarray(controller_ids).flatten() if controller_ids.max() < 0: raise ValueError( - "if passing an integer array of `controller_ids`, all the integers must be positive." + f"if passing an integer array of `controller_ids`, " + f"all the integers must be positive:{controller_ids}" ) else: @@ -295,7 +329,8 @@ def __init__( if controller_ids.size != n_subplots: raise ValueError( - f"Number of controller_ids does not match the number of subplots: {n_subplots}" + f"Number of controller_ids: {controller_ids.size} " + f"does not match the number of subplots: {n_subplots}" ) if controller_types is None: @@ -429,7 +464,7 @@ def __init__( @property def shape(self) -> list[tuple[int, int, int, int]] | tuple[int, int]: - """[n_rows, n_cols]""" + """Only for grid layouts of subplots: [n_rows, n_cols]""" if isinstance(self.layout, GridLayout): return self.layout.shape @@ -711,7 +746,7 @@ def export_numpy(self, rgb: bool = False) -> np.ndarray: def export(self, uri: str | Path | bytes, **kwargs): """ - Use ``imageio`` for writing the current Figure to a file, or return a byte string. + Use ``imageio`` to export the current Figure to a file, or return a byte string. Must have ``imageio`` installed. Parameters diff --git a/fastplotlib/layouts/_graphic_methods_mixin.py b/fastplotlib/layouts/_graphic_methods_mixin.py index a753eec73..f2595923f 100644 --- a/fastplotlib/layouts/_graphic_methods_mixin.py +++ b/fastplotlib/layouts/_graphic_methods_mixin.py @@ -51,7 +51,8 @@ def add_image( maximum value for color scaling, calculated from data if not provided cmap: str, optional, default "plasma" - colormap to use to display the data + colormap to use to display the data. For supported colormaps see the + ``cmap`` library catalogue: https://cmap-docs.readthedocs.io/en/stable/catalog/ interpolation: str, optional, default "nearest" interpolation filter, one of "nearest" or "linear" @@ -62,7 +63,8 @@ def add_image( isolated_buffer: bool, default True If True, initialize a buffer with the same shape as the input data and then set the data, useful if the data arrays are ready-only such as memmaps. - If False, the input array is itself used as the buffer. + If False, the input array is itself used as the buffer - useful if the + array is large. kwargs: additional keyword arguments passed to Graphic @@ -176,11 +178,11 @@ def add_line( self, data: Any, thickness: float = 2.0, - colors: Union[str, numpy.ndarray, Iterable] = "w", + colors: Union[str, numpy.ndarray, Sequence] = "w", uniform_color: bool = False, alpha: float = 1.0, cmap: str = None, - cmap_transform: Union[numpy.ndarray, Iterable] = None, + cmap_transform: Union[numpy.ndarray, Sequence] = None, isolated_buffer: bool = True, size_space: str = "screen", **kwargs, @@ -192,14 +194,17 @@ def add_line( Parameters ---------- data: array-like - Line data to plot, 2D must be of shape [n_points, 2], 3D must be of shape [n_points, 3] + Line data to plot. Can provide 1D, 2D, or a 3D data. + | If passing a 1D array, it is used to set the y-values and the x-values are generated as an integer range + from [0, data.size] + | 2D data must be of shape [n_points, 2]. 3D data must be of shape [n_points, 3] thickness: float, optional, default 2.0 thickness of the line colors: str, array, or iterable, default "w" specify colors as a single human-readable string, a single RGBA array, - or an iterable of strings or RGBA arrays + or a Sequence (array, tuple, or list) of strings or RGBA arrays uniform_color: bool, default ``False`` if True, uses a uniform buffer for the line color, @@ -209,14 +214,15 @@ def add_line( alpha value for the colors cmap: str, optional - apply a colormap to the line instead of assigning colors manually, this - overrides any argument passed to "colors" + Apply a colormap to the line instead of assigning colors manually, this + overrides any argument passed to "colors". For supported colormaps see the + ``cmap`` library catalogue: https://cmap-docs.readthedocs.io/en/stable/catalog/ cmap_transform: 1D array-like of numerical values, optional if provided, these values are used to map the colors from the cmap size_space: str, default "screen" - coordinate space in which the size is expressed ("screen", "world", "model") + coordinate space in which the thickness is expressed ("screen", "world", "model") **kwargs passed to Graphic @@ -346,7 +352,7 @@ def add_scatter( cmap: str = None, cmap_transform: numpy.ndarray = None, isolated_buffer: bool = True, - sizes: Union[float, numpy.ndarray, Iterable[float]] = 1, + sizes: Union[float, numpy.ndarray, Sequence[float]] = 1, uniform_size: bool = False, size_space: str = "screen", **kwargs, @@ -358,36 +364,38 @@ def add_scatter( Parameters ---------- data: array-like - Scatter data to plot, 2D must be of shape [n_points, 2], 3D must be of shape [n_points, 3] + Scatter data to plot, Can provide 2D, or a 3D data. 2D data must be of shape [n_points, 2]. + 3D data must be of shape [n_points, 3] - colors: str, array, or iterable, default "w" - specify colors as a single human readable string, a single RGBA array, - or an iterable of strings or RGBA arrays + colors: str, array, tuple, list, Sequence, default "w" + specify colors as a single human-readable string, a single RGBA array, + or a Sequence (array, tuple, or list) of strings or RGBA arrays uniform_color: bool, default False - if True, uses a uniform buffer for the scatter point colors, - basically saves GPU VRAM when the entire line has a single color + if True, uses a uniform buffer for the scatter point colors. Useful if you need to + save GPU VRAM when all points have the same color. alpha: float, optional, default 1.0 alpha value for the colors cmap: str, optional apply a colormap to the scatter instead of assigning colors manually, this - overrides any argument passed to "colors" + overrides any argument passed to "colors". For supported colormaps see the + ``cmap`` library catalogue: https://cmap-docs.readthedocs.io/en/stable/catalog/ cmap_transform: 1D array-like or list of numerical values, optional if provided, these values are used to map the colors from the cmap isolated_buffer: bool, default True whether the buffers should be isolated from the user input array. - Generally always ``True``, ``False`` is for rare advanced use. + Generally always ``True``, ``False`` is for rare advanced use if you have large arrays. sizes: float or iterable of float, optional, default 1.0 - size of the scatter points + sizes of the scatter points uniform_size: bool, default False - if True, uses a uniform buffer for the scatter point sizes, - basically saves GPU VRAM when all scatter points are the same size + if True, uses a uniform buffer for the scatter point sizes. Useful if you need to + save GPU VRAM when all points have the same size. size_space: str, default "screen" coordinate space in which the size is expressed ("screen", "world", "model") @@ -436,10 +444,10 @@ def add_text( font_size: float | int, default 10 font size - face_color: str or array, default "w" + face_color: str, array, list, tuple, default "w" str or RGBA array to set the color of the text - outline_color: str or array, default "w" + outline_color: str, array, list, tuple, default "w" str or RGBA array to set the outline color of the text outline_thickness: float, default 0 diff --git a/fastplotlib/utils/functions.py b/fastplotlib/utils/functions.py index 45678aa5e..a1d6d476a 100644 --- a/fastplotlib/utils/functions.py +++ b/fastplotlib/utils/functions.py @@ -269,8 +269,7 @@ def make_colors_dict(labels: Sequence, cmap: str, **kwargs) -> OrderedDict: def quick_min_max(data: np.ndarray, max_size=1e6) -> tuple[float, float]: """ - Adapted from pyqtgraph.ImageView. - Estimate the min/max values of *data* by subsampling. + Estimate the min/max values of *data* by subsampling relative to the size of each dimension in the array. Parameters ---------- diff --git a/fastplotlib/widgets/image_widget/_widget.py b/fastplotlib/widgets/image_widget/_widget.py index 0fbc02be3..d6b4d9d08 100644 --- a/fastplotlib/widgets/image_widget/_widget.py +++ b/fastplotlib/widgets/image_widget/_widget.py @@ -329,7 +329,7 @@ def __init__( manually provide the shape for the Figure, otherwise the number of rows and columns is estimated figure_kwargs: dict, optional - passed to `GridPlot` + passed to ``Figure`` names: Optional[str] gives names to the subplots diff --git a/scripts/generate_add_graphic_methods.py b/scripts/generate_add_graphic_methods.py index 533ae77c6..85e0be669 100644 --- a/scripts/generate_add_graphic_methods.py +++ b/scripts/generate_add_graphic_methods.py @@ -49,23 +49,26 @@ def generate_add_graphics_methods(): f.write(" return graphic\n\n") for m in modules: - class_name = m - method_name = class_name.type + cls = m + if cls.__name__ == "Graphic": + # skip base class + continue + method_name = cls.type - class_args = inspect.getfullargspec(class_name)[0][1:] + class_args = inspect.getfullargspec(cls)[0][1:] class_args = [arg + ", " for arg in class_args] s = "" for a in class_args: s += a f.write( - f" def add_{method_name}{inspect.signature(class_name.__init__)} -> {class_name.__name__}:\n" + f" def add_{method_name}{inspect.signature(cls.__init__)} -> {cls.__name__}:\n" ) f.write(' """\n') - f.write(f" {class_name.__init__.__doc__}\n") + f.write(f" {cls.__init__.__doc__}\n") f.write(' """\n') f.write( - f" return self._create_graphic({class_name.__name__}, {s} **kwargs)\n\n" + f" return self._create_graphic({cls.__name__}, {s} **kwargs)\n\n" ) f.close() diff --git a/tests/test_figure.py b/tests/test_figure.py index 757b1eeae..520091009 100644 --- a/tests/test_figure.py +++ b/tests/test_figure.py @@ -170,3 +170,93 @@ def test_set_controllers_from_existing_controllers(): assert fig[0, 0].camera is cameras[0][0] assert fig[0, 1].camera.fov == 50 + + +def test_subplot_names(): + # names must be unique + with pytest.raises(ValueError): + fpl.Figure( + shape=(2, 3), + names=["1", "2", "3", "4", "4", "5"] + ) + + with pytest.raises(ValueError): + fpl.Figure( + shape=(2, 3), + names=["1", "2", None, "4", "4", "5"] + ) + + with pytest.raises(ValueError): + fpl.Figure( + shape=(2, 3), + names=[None, "2", None, "4", "4", "5"] + ) + + # len(names) <= n_subplots + fig = fpl.Figure( + shape=(2, 3), + names=["1", "2", "3", "4", "5", "6"] + ) + + assert fig[0, 0].name == "1" + assert fig[0, 1].name == "2" + assert fig[0, 2].name == "3" + assert fig[1, 0].name == "4" + assert fig[1, 1].name == "5" + assert fig[1, 2].name == "6" + + fig = fpl.Figure( + shape=(2, 3), + names=["1", "2", "3", None, "5", "6"] + ) + + assert fig[0, 0].name == "1" + assert fig[0, 1].name == "2" + assert fig[0, 2].name == "3" + assert fig[1, 0].name is None + assert fig[1, 1].name == "5" + assert fig[1, 2].name == "6" + + fig = fpl.Figure( + shape=(2, 3), + names=["1", "2", "3", None, "5", None] + ) + + assert fig[0, 0].name == "1" + assert fig[0, 1].name == "2" + assert fig[0, 2].name == "3" + assert fig[1, 0].name is None + assert fig[1, 1].name == "5" + assert fig[1, 2].name is None + + # if fewer subplot names are given than n_sublots, pad with Nones + fig = fpl.Figure( + shape=(2, 3), + names=["1", "2", "3", "4"] + ) + + assert fig[0, 0].name == "1" + assert fig[0, 1].name == "2" + assert fig[0, 2].name == "3" + assert fig[1, 0].name == "4" + assert fig[1, 1].name is None + assert fig[1, 2].name is None + + # raise if len(names) > n_subplots + with pytest.raises(ValueError): + fpl.Figure( + shape=(2, 3), + names=["1", "2", "3", "4", "5", "6", "7"] + ) + + with pytest.raises(ValueError): + fpl.Figure( + shape=(2, 3), + names=["1", "2", "3", "4", None, "6", "7"] + ) + + with pytest.raises(ValueError): + fpl.Figure( + shape=(2, 3), + names=["1", None, "3", "4", None, "6", "7"] + ) From fe8deda6522ab62a768eeed66b4efd5b92c2cd5c Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Tue, 20 May 2025 07:44:00 -0400 Subject: [PATCH 182/185] iw.current_index is now non-reentrant (#829) --- fastplotlib/widgets/image_widget/_widget.py | 61 +++++++++++++-------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/fastplotlib/widgets/image_widget/_widget.py b/fastplotlib/widgets/image_widget/_widget.py index d6b4d9d08..2773470c6 100644 --- a/fastplotlib/widgets/image_widget/_widget.py +++ b/fastplotlib/widgets/image_widget/_widget.py @@ -195,33 +195,44 @@ def current_index(self, index: dict[str, int]): if not self._initialized: return - if not set(index.keys()).issubset(set(self._current_index.keys())): - raise KeyError( - f"All dimension keys for setting `current_index` must be present in the widget sliders. " - f"The dimensions currently used for sliders are: {list(self.current_index.keys())}" - ) + if self._reentrant_block: + return - for k, val in index.items(): - if not isinstance(val, int): - raise TypeError("Indices for all dimensions must be int") - if val < 0: - raise IndexError("negative indexing is not supported for ImageWidget") - if val > self._dims_max_bounds[k]: - raise IndexError( - f"index {val} is out of bounds for dimension '{k}' " - f"which has a max bound of: {self._dims_max_bounds[k]}" + try: + self._reentrant_block = True # block re-execution until current_index has *fully* completed execution + if not set(index.keys()).issubset(set(self._current_index.keys())): + raise KeyError( + f"All dimension keys for setting `current_index` must be present in the widget sliders. " + f"The dimensions currently used for sliders are: {list(self.current_index.keys())}" ) - self._current_index.update(index) + for k, val in index.items(): + if not isinstance(val, int): + raise TypeError("Indices for all dimensions must be int") + if val < 0: + raise IndexError("negative indexing is not supported for ImageWidget") + if val > self._dims_max_bounds[k]: + raise IndexError( + f"index {val} is out of bounds for dimension '{k}' " + f"which has a max bound of: {self._dims_max_bounds[k]}" + ) - for i, (ig, data) in enumerate(zip(self.managed_graphics, self.data)): - frame = self._process_indices(data, self._current_index) - frame = self._process_frame_apply(frame, i) - ig.data = frame + self._current_index.update(index) - # call any event handlers - for handler in self._current_index_changed_handlers: - handler(self.current_index) + for i, (ig, data) in enumerate(zip(self.managed_graphics, self.data)): + frame = self._process_indices(data, self._current_index) + frame = self._process_frame_apply(frame, i) + ig.data = frame + + # call any event handlers + for handler in self._current_index_changed_handlers: + handler(self.current_index) + except Exception as exc: + # raise original exception + raise exc # current_index setter has raised. The lines above below are probably more relevant! + finally: + # set_value has finished executing, now allow future executions + self._reentrant_block = False @property def n_img_dims(self) -> list[int]: @@ -574,10 +585,12 @@ def __init__( self.figure.add_gui(self._image_widget_sliders) - self._initialized = True - self._current_index_changed_handlers = set() + self._reentrant_block = False + + self._initialized = True + @property def frame_apply(self) -> dict | None: return self._frame_apply From 220527e0852c3ec337418805d5853c8967624865 Mon Sep 17 00:00:00 2001 From: Caitlin Lewis <69729525+clewis7@users.noreply.github.com> Date: Tue, 20 May 2025 10:32:44 -0400 Subject: [PATCH 183/185] lint (#831) --- fastplotlib/widgets/image_widget/_widget.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fastplotlib/widgets/image_widget/_widget.py b/fastplotlib/widgets/image_widget/_widget.py index 2773470c6..b3fe1d05d 100644 --- a/fastplotlib/widgets/image_widget/_widget.py +++ b/fastplotlib/widgets/image_widget/_widget.py @@ -199,7 +199,7 @@ def current_index(self, index: dict[str, int]): return try: - self._reentrant_block = True # block re-execution until current_index has *fully* completed execution + self._reentrant_block = True # block re-execution until current_index has *fully* completed execution if not set(index.keys()).issubset(set(self._current_index.keys())): raise KeyError( f"All dimension keys for setting `current_index` must be present in the widget sliders. " @@ -210,7 +210,9 @@ def current_index(self, index: dict[str, int]): if not isinstance(val, int): raise TypeError("Indices for all dimensions must be int") if val < 0: - raise IndexError("negative indexing is not supported for ImageWidget") + raise IndexError( + "negative indexing is not supported for ImageWidget" + ) if val > self._dims_max_bounds[k]: raise IndexError( f"index {val} is out of bounds for dimension '{k}' " From 5cda159029382e38b4ad628963ea91b60c3364a2 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Thu, 22 May 2025 08:59:24 -0400 Subject: [PATCH 184/185] Tooltips, add overlay render pass (#830) * add overlay render pass * Graphic accessible in graphics * tooltip prototype * basic tooltips work * custom tooltip example * auto tooltips * black * update iris example, add to __init__ * update docs * type * comments * docstring * add tools dir to api root toctree * forgot to regenerate * Apply suggestions from code review Co-authored-by: Caitlin Lewis <69729525+clewis7@users.noreply.github.com> --------- Co-authored-by: Caitlin Lewis <69729525+clewis7@users.noreply.github.com> --- docs/source/api/graphics/Graphic.rst | 47 ++++ docs/source/api/graphics/index.rst | 1 + docs/source/api/index.rst | 1 + docs/source/api/layouts/figure.rst | 2 + docs/source/api/layouts/imgui_figure.rst | 2 + docs/source/api/tools/HistogramLUTTool.rst | 53 ++++ docs/source/api/tools/Tooltip.rst | 38 +++ docs/source/api/tools/index.rst | 8 + docs/source/generate_api.py | 43 ++- examples/line_collection/line_stack.py | 25 +- examples/misc/tooltips.py | 54 ++++ examples/misc/tooltips_custom.py | 54 ++++ fastplotlib/graphics/__init__.py | 1 + fastplotlib/layouts/_engine.py | 2 +- fastplotlib/layouts/_figure.py | 47 +++- fastplotlib/layouts/_imgui_figure.py | 2 + fastplotlib/layouts/_plot_area.py | 5 +- fastplotlib/tools/__init__.py | 6 + fastplotlib/tools/_tooltip.py | 297 +++++++++++++++++++++ 19 files changed, 678 insertions(+), 10 deletions(-) create mode 100644 docs/source/api/graphics/Graphic.rst create mode 100644 docs/source/api/tools/HistogramLUTTool.rst create mode 100644 docs/source/api/tools/Tooltip.rst create mode 100644 docs/source/api/tools/index.rst create mode 100644 examples/misc/tooltips.py create mode 100644 examples/misc/tooltips_custom.py create mode 100644 fastplotlib/tools/_tooltip.py diff --git a/docs/source/api/graphics/Graphic.rst b/docs/source/api/graphics/Graphic.rst new file mode 100644 index 000000000..08ab0404b --- /dev/null +++ b/docs/source/api/graphics/Graphic.rst @@ -0,0 +1,47 @@ +.. _api.Graphic: + +Graphic +******* + +======= +Graphic +======= +.. currentmodule:: fastplotlib + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: Graphic_api + + Graphic + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: Graphic_api + + Graphic.axes + Graphic.block_events + Graphic.deleted + Graphic.event_handlers + Graphic.name + Graphic.offset + Graphic.right_click_menu + Graphic.rotation + Graphic.supported_events + Graphic.visible + Graphic.world_object + +Methods +~~~~~~~ +.. autosummary:: + :toctree: Graphic_api + + Graphic.add_axes + Graphic.add_event_handler + Graphic.clear_event_handlers + Graphic.remove_event_handler + Graphic.rotate + Graphic.share_property + Graphic.unshare_property + diff --git a/docs/source/api/graphics/index.rst b/docs/source/api/graphics/index.rst index a2addb7bf..491013dff 100644 --- a/docs/source/api/graphics/index.rst +++ b/docs/source/api/graphics/index.rst @@ -4,6 +4,7 @@ Graphics .. toctree:: :maxdepth: 1 + Graphic LineGraphic ScatterGraphic ImageGraphic diff --git a/docs/source/api/index.rst b/docs/source/api/index.rst index 87c134782..3a1184e6c 100644 --- a/docs/source/api/index.rst +++ b/docs/source/api/index.rst @@ -9,6 +9,7 @@ API Reference graphics/index graphic_features/index selectors/index + tools/index ui/index widgets/index fastplotlib diff --git a/docs/source/api/layouts/figure.rst b/docs/source/api/layouts/figure.rst index b5cbbd2bb..d191fe8ce 100644 --- a/docs/source/api/layouts/figure.rst +++ b/docs/source/api/layouts/figure.rst @@ -27,6 +27,8 @@ Properties Figure.names Figure.renderer Figure.shape + Figure.show_tooltips + Figure.tooltip_manager Methods ~~~~~~~ diff --git a/docs/source/api/layouts/imgui_figure.rst b/docs/source/api/layouts/imgui_figure.rst index a338afe96..0abfcc067 100644 --- a/docs/source/api/layouts/imgui_figure.rst +++ b/docs/source/api/layouts/imgui_figure.rst @@ -29,6 +29,8 @@ Properties ImguiFigure.names ImguiFigure.renderer ImguiFigure.shape + ImguiFigure.show_tooltips + ImguiFigure.tooltip_manager Methods ~~~~~~~ diff --git a/docs/source/api/tools/HistogramLUTTool.rst b/docs/source/api/tools/HistogramLUTTool.rst new file mode 100644 index 000000000..d134eb1ce --- /dev/null +++ b/docs/source/api/tools/HistogramLUTTool.rst @@ -0,0 +1,53 @@ +.. _api.HistogramLUTTool: + +HistogramLUTTool +**************** + +================ +HistogramLUTTool +================ +.. currentmodule:: fastplotlib + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: HistogramLUTTool_api + + HistogramLUTTool + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: HistogramLUTTool_api + + HistogramLUTTool.axes + HistogramLUTTool.block_events + HistogramLUTTool.cmap + HistogramLUTTool.deleted + HistogramLUTTool.event_handlers + HistogramLUTTool.image_graphic + HistogramLUTTool.name + HistogramLUTTool.offset + HistogramLUTTool.right_click_menu + HistogramLUTTool.rotation + HistogramLUTTool.supported_events + HistogramLUTTool.visible + HistogramLUTTool.vmax + HistogramLUTTool.vmin + HistogramLUTTool.world_object + +Methods +~~~~~~~ +.. autosummary:: + :toctree: HistogramLUTTool_api + + HistogramLUTTool.add_axes + HistogramLUTTool.add_event_handler + HistogramLUTTool.clear_event_handlers + HistogramLUTTool.disconnect_image_graphic + HistogramLUTTool.remove_event_handler + HistogramLUTTool.rotate + HistogramLUTTool.set_data + HistogramLUTTool.share_property + HistogramLUTTool.unshare_property + diff --git a/docs/source/api/tools/Tooltip.rst b/docs/source/api/tools/Tooltip.rst new file mode 100644 index 000000000..71607bf20 --- /dev/null +++ b/docs/source/api/tools/Tooltip.rst @@ -0,0 +1,38 @@ +.. _api.Tooltip: + +Tooltip +******* + +======= +Tooltip +======= +.. currentmodule:: fastplotlib + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: Tooltip_api + + Tooltip + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: Tooltip_api + + Tooltip.background_color + Tooltip.font_size + Tooltip.outline_color + Tooltip.padding + Tooltip.text_color + Tooltip.world_object + +Methods +~~~~~~~ +.. autosummary:: + :toctree: Tooltip_api + + Tooltip.register + Tooltip.unregister + Tooltip.unregister_all + diff --git a/docs/source/api/tools/index.rst b/docs/source/api/tools/index.rst new file mode 100644 index 000000000..c2666ed28 --- /dev/null +++ b/docs/source/api/tools/index.rst @@ -0,0 +1,8 @@ +Tools +***** + +.. toctree:: + :maxdepth: 1 + + HistogramLUTTool + Tooltip diff --git a/docs/source/generate_api.py b/docs/source/generate_api.py index 512826b5e..0be967a36 100644 --- a/docs/source/generate_api.py +++ b/docs/source/generate_api.py @@ -9,6 +9,7 @@ from fastplotlib.layouts import Subplot from fastplotlib import graphics from fastplotlib.graphics import features, selectors +from fastplotlib import tools from fastplotlib import widgets from fastplotlib import utils from fastplotlib import ui @@ -21,6 +22,7 @@ GRAPHICS_DIR = API_DIR.joinpath("graphics") GRAPHIC_FEATURES_DIR = API_DIR.joinpath("graphic_features") SELECTORS_DIR = API_DIR.joinpath("selectors") +TOOLS_DIR = API_DIR.joinpath("tools") WIDGETS_DIR = API_DIR.joinpath("widgets") UI_DIR = API_DIR.joinpath("ui") GUIDE_DIR = current_dir.joinpath("user_guide") @@ -31,6 +33,7 @@ GRAPHICS_DIR, GRAPHIC_FEATURES_DIR, SELECTORS_DIR, + TOOLS_DIR, WIDGETS_DIR, UI_DIR, ] @@ -264,7 +267,8 @@ def main(): ) # the rest of this is a mess and can be refactored later - + ############################################################################## + # ** Graphic classes ** # graphic_classes = [getattr(graphics, g) for g in graphics.__all__] graphic_class_names = [g.__name__ for g in graphic_classes] @@ -290,7 +294,7 @@ def main(): source_path=GRAPHICS_DIR.joinpath(f"{graphic_cls.__name__}.rst"), ) ############################################################################## - + # ** GraphicFeature classes ** # feature_classes = [getattr(features, f) for f in features.__all__] feature_class_names = [f.__name__ for f in feature_classes] @@ -315,7 +319,7 @@ def main(): source_path=GRAPHIC_FEATURES_DIR.joinpath(f"{feature_cls.__name__}.rst"), ) ############################################################################## - + # ** Selector classes ** # selector_classes = [getattr(selectors, s) for s in selectors.__all__] selector_class_names = [s.__name__ for s in selector_classes] @@ -339,8 +343,35 @@ def main(): modules=["fastplotlib"], source_path=SELECTORS_DIR.joinpath(f"{selector_cls.__name__}.rst"), ) + ############################################################################## + # ** Tools classes ** # + tools_classes = [getattr(tools, t) for t in tools.__all__] + tools_class_names = [t.__name__ for t in tools_classes] + + tools_class_names_str = "\n ".join([""] + tools_class_names) + + with open(TOOLS_DIR.joinpath("index.rst"), "w") as f: + f.write( + f"Tools\n" + f"*****\n" + f"\n" + f".. toctree::\n" + f" :maxdepth: 1\n" + f"{tools_class_names_str}\n" + ) + + for tool_cls in tools_classes: + generate_page( + page_name=tool_cls.__name__, + classes=[tool_cls], + modules=["fastplotlib"], + source_path=TOOLS_DIR.joinpath(f"{tool_cls.__name__}.rst"), + ) + + ############################################################################## + # ** Widget classes ** # widget_classes = [getattr(widgets, w) for w in widgets.__all__] widget_class_names = [w.__name__ for w in widget_classes] @@ -365,7 +396,7 @@ def main(): source_path=WIDGETS_DIR.joinpath(f"{widget_cls.__name__}.rst"), ) ############################################################################## - + # ** UI classes ** # ui_classes = [ui.BaseGUI, ui.Window, ui.EdgeWindow, ui.Popup] ui_class_names = [cls.__name__ for cls in ui_classes] @@ -410,6 +441,7 @@ def main(): " graphics/index\n" " graphic_features/index\n" " selectors/index\n" + " tools/index\n" " ui/index\n" " widgets/index\n" " fastplotlib\n" @@ -438,6 +470,9 @@ def write_table(name, feature_cls): f.write("============\n\n") for graphic_cls in [*graphic_classes, *selector_classes]: + if graphic_cls is graphics.Graphic: + # skip Graphic base class + continue f.write(f"{graphic_cls.__name__}\n") f.write("-" * len(graphic_cls.__name__) + "\n\n") for name, type_ in graphic_cls._features.items(): diff --git a/examples/line_collection/line_stack.py b/examples/line_collection/line_stack.py index 95b681b76..4f0c6037d 100644 --- a/examples/line_collection/line_stack.py +++ b/examples/line_collection/line_stack.py @@ -19,7 +19,10 @@ data = np.column_stack([xs, ys]) multi_data = np.stack([data] * 10) -figure = fpl.Figure(size=(700, 560)) +figure = fpl.Figure( + size=(700, 560), + show_tooltips=True +) line_stack = figure[0, 0].add_line_stack( multi_data, # shape: (10, 100, 2), i.e. [n_lines, n_points, xy] @@ -28,6 +31,26 @@ separation=1, # spacing between lines along the separation axis, default separation along "y" axis ) + +def tooltip_info(ev): + """A custom function to display the index of the graphic within the collection.""" + index = ev.pick_info["vertex_index"] # index of the line datapoint being hovered + + # get index of the hovered line within the line stack + line_index = np.where(line_stack.graphics == ev.graphic)[0].item() + info = f"line index: {line_index}\n" + + # append data value info + info += "\n".join(f"{dim}: {val}" for dim, val in zip("xyz", ev.graphic.data[index])) + + # return str to display in tooltip + return info + +# register the line stack with the custom tooltip function +figure.tooltip_manager.register( + line_stack, custom_info=tooltip_info +) + figure.show(maintain_aspect=False) diff --git a/examples/misc/tooltips.py b/examples/misc/tooltips.py new file mode 100644 index 000000000..4fdae1482 --- /dev/null +++ b/examples/misc/tooltips.py @@ -0,0 +1,54 @@ +""" +Tooltips +======== + +Show tooltips on all graphics +""" + +# test_example = false +# sphinx_gallery_pygfx_docs = 'screenshot' + +import numpy as np +import imageio.v3 as iio +import fastplotlib as fpl + + +# get some data +scatter_data = np.random.rand(1_000, 3) + +xs = np.linspace(0, 2 * np.pi, 100) +ys = np.sin(xs) + +gray = iio.imread("imageio:camera.png") +rgb = iio.imread("imageio:astronaut.png") + +# create a figure +figure = fpl.Figure( + cameras=["3d", "2d", "2d", "2d"], + controller_types=["orbit", "panzoom", "panzoom", "panzoom"], + size=(700, 560), + shape=(2, 2), + show_tooltips=True, # tooltip will display data value info for all graphics +) + +# create graphics +scatter = figure[0, 0].add_scatter(scatter_data, sizes=3, colors="r") +line = figure[0, 1].add_line(np.column_stack([xs, ys])) +image = figure[1, 0].add_image(gray) +image_rgb = figure[1, 1].add_image(rgb) + + +figure.show() + +# to hide tooltips for all graphics in an existing Figure +# figure.show_tooltips = False + +# to show tooltips for all graphics in an existing Figure +# figure.show_tooltips = True + + +# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively +# please see our docs for using fastplotlib interactively in ipython and jupyter +if __name__ == "__main__": + print(__doc__) + fpl.loop.run() diff --git a/examples/misc/tooltips_custom.py b/examples/misc/tooltips_custom.py new file mode 100644 index 000000000..a62190906 --- /dev/null +++ b/examples/misc/tooltips_custom.py @@ -0,0 +1,54 @@ +""" +Tooltips Customization +====================== + +Customize the information displayed in a tooltip. This example uses the Iris dataset and sets the tooltip to display +the species and cluster label of the point that is being hovered by the mouse pointer. +""" + +# test_example = false +# sphinx_gallery_pygfx_docs = 'screenshot' + + +import fastplotlib as fpl +from sklearn.cluster import AgglomerativeClustering +from sklearn import datasets + + +figure = fpl.Figure(size=(700, 560)) + +dataset = datasets.load_iris() +data = dataset["data"] + +agg = AgglomerativeClustering(n_clusters=3) +agg.fit_predict(data) + +scatter_graphic = figure[0, 0].add_scatter( + data=data[:, :-1], # use only xy data + sizes=15, + cmap="Set1", + cmap_transform=agg.labels_ # use the labels as a transform to map colors from the colormap +) + + +def tooltip_info(ev) -> str: + # get index of the scatter point that is being hovered + index = ev.pick_info["vertex_index"] + + # get the species name + target = dataset["target"][index] + cluster = agg.labels_[index] + info = f"species: {dataset['target_names'][target]}\ncluster: {cluster}" + + # return this string to display it in the tooltip + return info + + +figure.tooltip_manager.register(scatter_graphic, custom_info=tooltip_info) + +figure.show() + + +if __name__ == "__main__": + print(__doc__) + fpl.loop.run() diff --git a/fastplotlib/graphics/__init__.py b/fastplotlib/graphics/__init__.py index 03f361502..b458a8c48 100644 --- a/fastplotlib/graphics/__init__.py +++ b/fastplotlib/graphics/__init__.py @@ -7,6 +7,7 @@ __all__ = [ + "Graphic", "LineGraphic", "ScatterGraphic", "ImageGraphic", diff --git a/fastplotlib/layouts/_engine.py b/fastplotlib/layouts/_engine.py index 877a7fbab..bf73d5f0d 100644 --- a/fastplotlib/layouts/_engine.py +++ b/fastplotlib/layouts/_engine.py @@ -7,7 +7,7 @@ from ._rect import RectManager -class UnderlayCamera(pygfx.Camera): +class ScreenSpaceCamera(pygfx.Camera): """ Same as pygfx.ScreenCoordsCamera but y-axis is inverted. diff --git a/fastplotlib/layouts/_figure.py b/fastplotlib/layouts/_figure.py index 10322d240..bfd97000b 100644 --- a/fastplotlib/layouts/_figure.py +++ b/fastplotlib/layouts/_figure.py @@ -19,8 +19,9 @@ ) from ._utils import controller_types as valid_controller_types from ._subplot import Subplot -from ._engine import GridLayout, WindowLayout, UnderlayCamera +from ._engine import GridLayout, WindowLayout, ScreenSpaceCamera from .. import ImageGraphic +from ..tools import Tooltip class Figure: @@ -51,6 +52,7 @@ def __init__( canvas_kwargs: dict = None, size: tuple[int, int] = (500, 300), names: list | np.ndarray = None, + show_tooltips: bool = False, ): """ Create a Figure containing Subplots. @@ -122,6 +124,9 @@ def __init__( names: list or array of str, optional subplot names + show_tooltips: bool, default False + show tooltips on graphics + """ if rects is not None: @@ -444,13 +449,23 @@ def __init__( canvas_rect=self.get_pygfx_render_area(), ) - self._underlay_camera = UnderlayCamera() - + # underlay render pass + self._underlay_camera = ScreenSpaceCamera() self._underlay_scene = pygfx.Scene() for subplot in self._subplots.ravel(): self._underlay_scene.add(subplot.frame._world_object) + # overlay render pass + self._overlay_camera = ScreenSpaceCamera() + self._overlay_scene = pygfx.Scene() + + # tooltip in overlay render pass + self._tooltip_manager = Tooltip() + self._overlay_scene.add(self._tooltip_manager.world_object) + + self._show_tooltips = show_tooltips + self._animate_funcs_pre: list[callable] = list() self._animate_funcs_post: list[callable] = list() @@ -518,6 +533,29 @@ def names(self) -> np.ndarray[str]: names.flags.writeable = False return names + @property + def tooltip_manager(self) -> Tooltip: + """manage tooltips""" + return self._tooltip_manager + + @property + def show_tooltips(self) -> bool: + """show/hide tooltips for all graphics""" + return self._show_tooltips + + @show_tooltips.setter + def show_tooltips(self, val: bool): + self._show_tooltips = val + + if val: + # register all graphics + for subplot in self: + for graphic in subplot.graphics: + self._tooltip_manager.register(graphic) + + elif not val: + self._tooltip_manager.unregister_all() + def _render(self, draw=True): # draw the underlay planes self.renderer.render(self._underlay_scene, self._underlay_camera, flush=False) @@ -527,6 +565,9 @@ def _render(self, draw=True): for subplot in self: subplot._render() + # overlay render pass + self.renderer.render(self._overlay_scene, self._overlay_camera, flush=False) + self.renderer.flush() # call post-render animate functions diff --git a/fastplotlib/layouts/_imgui_figure.py b/fastplotlib/layouts/_imgui_figure.py index b0267dc75..c54890239 100644 --- a/fastplotlib/layouts/_imgui_figure.py +++ b/fastplotlib/layouts/_imgui_figure.py @@ -44,6 +44,7 @@ def __init__( canvas_kwargs: dict = None, size: tuple[int, int] = (500, 300), names: list | np.ndarray = None, + show_tooltips: bool = False, ): self._guis: dict[str, EdgeWindow] = {k: None for k in GUI_EDGES} @@ -60,6 +61,7 @@ def __init__( canvas_kwargs=canvas_kwargs, size=size, names=names, + show_tooltips=show_tooltips, ) self._imgui_renderer = ImguiRenderer(self.renderer.device, self.canvas) diff --git a/fastplotlib/layouts/_plot_area.py b/fastplotlib/layouts/_plot_area.py index 2934e0589..2542fc215 100644 --- a/fastplotlib/layouts/_plot_area.py +++ b/fastplotlib/layouts/_plot_area.py @@ -491,6 +491,10 @@ def _add_or_insert_graphic( obj_list = self._graphics self._fpl_graphics_scene.add(graphic.world_object) + # add to tooltip registry + if self.get_figure().show_tooltips: + self.get_figure().tooltip_manager.register(graphic) + else: raise TypeError("graphic must be of type Graphic | BaseSelector | Legend") @@ -504,7 +508,6 @@ def _add_or_insert_graphic( if center: self.center_graphic(graphic) - # if we don't use the weakref above, then the object lingers if a plot hook is used! graphic._fpl_add_plot_area_hook(self) def _check_graphic_name_exists(self, name): diff --git a/fastplotlib/tools/__init__.py b/fastplotlib/tools/__init__.py index 80396c98d..df129a369 100644 --- a/fastplotlib/tools/__init__.py +++ b/fastplotlib/tools/__init__.py @@ -1 +1,7 @@ from ._histogram_lut import HistogramLUTTool +from ._tooltip import Tooltip + +__all__ = [ + "HistogramLUTTool", + "Tooltip", +] diff --git a/fastplotlib/tools/_tooltip.py b/fastplotlib/tools/_tooltip.py new file mode 100644 index 000000000..2fbdfcec2 --- /dev/null +++ b/fastplotlib/tools/_tooltip.py @@ -0,0 +1,297 @@ +from functools import partial + +import numpy as np +import pygfx + +from ..graphics import LineGraphic, ImageGraphic, ScatterGraphic, Graphic +from ..graphics.features import GraphicFeatureEvent + + +class MeshMasks: + """Used set the x0, x1, y0, y1 positions of the plane mesh""" + + x0 = np.array( + [ + [False, False, False], + [True, False, False], + [False, False, False], + [True, False, False], + ] + ) + + x1 = np.array( + [ + [True, False, False], + [False, False, False], + [True, False, False], + [False, False, False], + ] + ) + + y0 = np.array( + [ + [False, True, False], + [False, True, False], + [False, False, False], + [False, False, False], + ] + ) + + y1 = np.array( + [ + [False, False, False], + [False, False, False], + [False, True, False], + [False, True, False], + ] + ) + + +masks = MeshMasks + + +class Tooltip: + def __init__(self): + # text object + self._text = pygfx.Text( + text="", + font_size=12, + screen_space=False, + anchor="bottom-left", + material=pygfx.TextMaterial( + color="w", + outline_color="w", + outline_thickness=0.0, + pick_write=False, + ), + ) + + # plane for the background of the text object + geometry = pygfx.plane_geometry(1, 1) + material = pygfx.MeshBasicMaterial(color=(0.1, 0.1, 0.3, 0.95)) + self._plane = pygfx.Mesh(geometry, material) + # else text not visible + self._plane.world.z = 0.5 + + # line to outline the plane mesh + self._line = pygfx.Line( + geometry=pygfx.Geometry( + positions=np.array( + [ + [0, 0, 0], + [0, 0, 0], + [0, 0, 0], + [0, 0, 0], + [0, 0, 0], + ], + dtype=np.float32, + ) + ), + material=pygfx.LineThinMaterial(thickness=1.0, color=(0.8, 0.8, 1.0, 1.0)), + ) + + self._world_object = pygfx.Group() + self._world_object.add(self._plane, self._text, self._line) + + # padded to bbox so the background box behind the text extends a bit further + # making the text easier to read + self._padding = np.array([[5, 5, 0], [-5, -5, 0]], dtype=np.float32) + + self._registered_graphics = dict() + + @property + def world_object(self) -> pygfx.Group: + return self._world_object + + @property + def font_size(self): + """Get or set font size""" + return self._text.font_size + + @font_size.setter + def font_size(self, size: float): + self._text.font_size = size + + @property + def text_color(self): + """Get or set text color using a str or RGB(A) array""" + return self._text.material.color + + @text_color.setter + def text_color(self, color: str | tuple | list | np.ndarray): + self._text.material.color = color + + @property + def background_color(self): + """Get or set background color using a str or RGB(A) array""" + return self._plane.material.color + + @background_color.setter + def background_color(self, color: str | tuple | list | np.ndarray): + self._plane.material.color = color + + @property + def outline_color(self): + """Get or set outline color using a str or RGB(A) array""" + return self._line.material.color + + @outline_color.setter + def outline_color(self, color: str | tuple | list | np.ndarray): + self._line.material.color = color + + @property + def padding(self) -> np.ndarray: + """ + Get or set the background padding in number of pixels. + The padding defines the number of pixels around the tooltip text that the background is extended by. + """ + + return self.padding[0, :2].copy() + + @padding.setter + def padding(self, padding_xy: tuple[float, float]): + self._padding[0, :2] = padding_xy + self._padding[1, :2] = -np.asarray(padding_xy) + + def _set_position(self, pos: tuple[float, float]): + """ + Set the position of the tooltip + + Parameters + ---------- + pos: [float, float] + position in screen space + + """ + # need to flip due to inverted y + x, y = pos[0], pos[1] + + # put the tooltip slightly to the top right of the cursor positoin + x += 8 + y -= 8 + + self._text.world.position = (x, -y, 0) + + bbox = self._text.get_world_bounding_box() - self._padding + [[x0, y0, _], [x1, y1, _]] = bbox + + self._plane.geometry.positions.data[masks.x0] = x0 + self._plane.geometry.positions.data[masks.x1] = x1 + self._plane.geometry.positions.data[masks.y0] = y0 + self._plane.geometry.positions.data[masks.y1] = y1 + + self._plane.geometry.positions.update_range() + + # line points + pts = [[x0, y0], [x0, y1], [x1, y1], [x1, y0], [x0, y0]] + + self._line.geometry.positions.data[:, :2] = pts + self._line.geometry.positions.update_range() + + def _event_handler(self, custom_tooltip: callable, ev: pygfx.PointerEvent): + """Handles the tooltip appear event, determines the text to be set in the tooltip""" + if custom_tooltip is not None: + info = custom_tooltip(ev) + + elif isinstance(ev.graphic, ImageGraphic): + col, row = ev.pick_info["index"] + if ev.graphic.data.value.ndim == 2: + info = str(ev.graphic.data[row, col]) + else: + info = "\n".join( + f"{channel}: {val}" + for channel, val in zip("rgba", ev.graphic.data[row, col]) + ) + + elif isinstance(ev.graphic, (LineGraphic, ScatterGraphic)): + index = ev.pick_info["vertex_index"] + info = "\n".join( + f"{dim}: {val}" for dim, val in zip("xyz", ev.graphic.data[index]) + ) + else: + raise TypeError("Unsupported graphic") + + # make the tooltip object visible + self.world_object.visible = True + + # set the text and top left position of the tooltip + self._text.set_text(info) + self._set_position((ev.x, ev.y)) + + def _clear(self, ev): + self._text.set_text("") + self.world_object.visible = False + + def register( + self, + graphic: Graphic, + appear_event: str = "pointer_move", + disappear_event: str = "pointer_leave", + custom_info: callable = None, + ): + """ + Register a Graphic to display tooltips. + + **Note:** if the passed graphic is already registered then it first unregistered + and then re-registered using the given arguments. + + Parameters + ---------- + graphic: Graphic + Graphic to register + + appear_event: str, default "pointer_move" + the pointer that triggers the tooltip to appear. Usually one of "pointer_move" | "click" | "double_click" + + disappear_event: str, default "pointer_leave" + the event that triggers the tooltip to disappear, does not have to be a pointer event. + + custom_info: callable, default None + a custom function that takes the pointer event defined as the `appear_event` and returns the text + to display in the tooltip + + """ + if graphic in list(self._registered_graphics.keys()): + # unregister first and then re-register + self.unregister(graphic) + + pfunc = partial(self._event_handler, custom_info) + graphic.add_event_handler(pfunc, appear_event) + graphic.add_event_handler(self._clear, disappear_event) + + self._registered_graphics[graphic] = (pfunc, appear_event, disappear_event) + + # automatically unregister when graphic is deleted + graphic.add_event_handler(self.unregister, "deleted") + + def unregister(self, graphic: Graphic): + """ + Unregister a Graphic to no longer display tooltips for this graphic. + + **Note:** if the passed graphic is not registered then it is just ignored without raising any exception. + + Parameters + ---------- + graphic: Graphic + Graphic to unregister + + """ + + if isinstance(graphic, GraphicFeatureEvent): + # this happens when the deleted event is triggered + graphic = graphic.graphic + + if graphic not in self._registered_graphics: + return + + # get pfunc and event names + pfunc, appear_event, disappear_event = self._registered_graphics.pop(graphic) + + # remove handlers from graphic + graphic.remove_event_handler(pfunc, appear_event) + graphic.remove_event_handler(self._clear, disappear_event) + + def unregister_all(self): + """unregister all graphics""" + for graphic in self._registered_graphics.keys(): + self.unregister(graphic) From 124d79e056a372cd1e390971d3462d094fe908d0 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Thu, 22 May 2025 21:02:38 -0400 Subject: [PATCH 185/185] pin to pygfx v0.10.0 for release (#836) --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 846c9070b..216b4ab46 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,7 @@ keywords = [ requires-python = ">= 3.10" dependencies = [ "numpy>=1.23.0", - "pygfx>=0.9.0", + "pygfx==0.10.0", "wgpu>=0.20.0", "cmap>=0.1.3", # (this comment keeps this list multiline in VSCode)

;VZziw1&X*&>L3wjJq? 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

$hz9IucE2;ufo55x4IG! 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⪙yzlT&3+@|U)v2=7~I^oN1#2;X!hCO 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°qp$lI;7(90(JY1=glvgJb}VU4eX)q88d> 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