Drag points#

Example where you can drag scatter points on a line. This example also demonstrates how you can use a shared buffer between two graphics to represent the same data using different graphics. When you update the data of one graphic the data of the other graphic is also changed simultaneously since they use the same underlying buffer on the GPU.

drag points
/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

xs = np.linspace(0, 2 * np.pi, 10)
ys = np.sin(xs)

data = np.column_stack([xs, ys])

figure = fpl.Figure(size=(700, 560))

# add a line
line_graphic = figure[0, 0].add_line(data)

# add a scatter, share the line graphic buffer!
scatter_graphic = figure[0, 0].add_scatter(data=line_graphic.data, sizes=25, colors="r")

is_moving = False
vertex_index = None


@scatter_graphic.add_event_handler("pointer_down")
def start_drag(ev: pygfx.PointerEvent):
    global is_moving
    global vertex_index

    if ev.button != 1:
        return

    is_moving = True
    vertex_index = ev.pick_info["vertex_index"]
    scatter_graphic.colors[vertex_index] = "cyan"


@figure.renderer.add_event_handler("pointer_move")
def move_point(ev):
    global is_moving
    global vertex_index

    # if not moving, return
    if not is_moving:
        return

    # disable controller
    figure[0, 0].controller.enabled = False

    # map x, y from screen space to world space
    pos = figure[0, 0].map_screen_to_world(ev)

    if pos is None:
        # end movement
        is_moving = False
        scatter_graphic.colors[vertex_index] = "r"  # reset color
        vertex_index = None
        return

    # change scatter data
    # since we are sharing the buffer, the line data will also change
    scatter_graphic.data[vertex_index, :-1] = pos[:-1]

    # re-enable controller
    figure[0, 0].controller.enabled = True


@figure.renderer.add_event_handler("pointer_up")
def end_drag(ev: pygfx.PointerEvent):
    global is_moving
    global vertex_index

    # end movement
    if is_moving:
        # reset color
        scatter_graphic.colors[vertex_index] = "r"

    is_moving = False
    vertex_index = None


figure.show()


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

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

Gallery generated by Sphinx-Gallery