From 1ef8eabcabe6fce68a2a7dc0811c2d4cf7a60209 Mon Sep 17 00:00:00 2001 From: Kushal Kolar Date: Wed, 1 Nov 2023 21:51:38 -0400 Subject: [PATCH 1/5] 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 2/5] 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 3/5] 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 4/5] 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 5/5] 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 pFad - Phonifier reborn

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

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


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy