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