Scatter hover#

Add an event handler to hover on scatter points and highlight them, i.e. change the color and size of the clicked point. Fly around the 3D scatter using WASD keys and click on points to highlight them.

There is no “hover” event, you can create a hover effect by using “pointer_move” events.

scatter hover
/opt/hostedtoolcache/Python/3.12.9/x64/lib/python3.12/site-packages/pygfx/objects/_ruler.py:266: RuntimeWarning: divide by zero encountered in divide
  screen_full = (ndc_full[:, :2] / ndc_full[:, 3:4]) * half_canvas_size
/opt/hostedtoolcache/Python/3.12.9/x64/lib/python3.12/site-packages/pygfx/objects/_ruler.py:266: RuntimeWarning: invalid value encountered in divide
  screen_full = (ndc_full[:, :2] / ndc_full[:, 3:4]) * half_canvas_size
/opt/hostedtoolcache/Python/3.12.9/x64/lib/python3.12/site-packages/pygfx/objects/_ruler.py:278: RuntimeWarning: invalid value encountered in divide
  screen_sel = (ndc_sel[:, :2] / ndc_sel[:, 3:4]) * half_canvas_size
/home/runner/work/fastplotlib/fastplotlib/fastplotlib/graphics/features/_base.py:18: UserWarning: casting float64 array to float32
  warn(f"casting {array.dtype} array to float32")

# test_example = false

import numpy as np
import fastplotlib as fpl
import pygfx

# make a gaussian cloud
data = np.random.normal(loc=0, scale=3, size=1500).reshape(500, 3)

figure = fpl.Figure(cameras="3d", size=(700, 560))

scatter = figure[0, 0].add_scatter(
    data,  # the gaussian cloud
    sizes=10,  # some big points that are easy to click
    cmap="viridis",
    cmap_transform=np.linalg.norm(data, axis=1)  # color points using distance from origin
)

# simple dict to restore the original scatter color and size
# of the previously clicked point upon clicking a new point
old_props = {"index": None, "size": None, "color": None}


@scatter.add_event_handler("pointer_move")
def highlight_point(ev: pygfx.PointerEvent):
    global old_props

    # the index of the point that was just entered
    new_index = ev.pick_info["vertex_index"]

    # if a new point has been entered, but we have not yet had
    # a leave event for the previous point, then reset this old point
    if old_props["index"] is not None:
        old_index = old_props["index"]
        if new_index == old_index:
            # same point, ignore
            return
        scatter.colors[old_index] = old_props["color"]
        scatter.sizes[old_index] = old_props["size"]

    # store the current property values of this new point
    old_props["index"] = new_index
    old_props["color"] = scatter.colors[new_index].copy()  # if you do not copy you will just get a view of the array!
    old_props["size"] = scatter.sizes[new_index]

    # highlight this new point
    scatter.colors[new_index] = "magenta"
    scatter.sizes[new_index] = 20


figure.show()


# NOTE: `if __name__ == "__main__"` is NOT how to use fastplotlib interactively
# please see our docs for using fastplotlib interactively in ipython and jupyter
if __name__ == "__main__":
    print(__doc__)
    fpl.loop.run()

Total running time of the script: (0 minutes 0.531 seconds)

Gallery generated by Sphinx-Gallery