Picking Markers#

Demonstrates how to identify (pick) markers. Hover markers to change their symbol and color.

Controls: * p - Toggle picking view - shows the colors encoding marker ID * r - Reset marker symbols and colors

marker picking
import random
import time
import numpy as np
from scipy.constants import golden as GOLDEN

from vispy import app, scene
from vispy.scene.visuals import Markers
from vispy.visuals.filters import MarkerPickingFilter

canvas = scene.SceneCanvas(keys='interactive', bgcolor='black')
view = canvas.central_widget.add_view(camera="panzoom")
view.camera.rect = (-1, -1, 2, 2)

# floret pattern
n = 10_000
radius = np.linspace(0, 0.9, n)**0.6  # prevent extreme density at center
theta = np.arange(n) * GOLDEN
pos = np.column_stack([radius * np.cos(theta), radius * np.sin(theta)])

COLORS = [
    (1, 0, 0, 1),  # red
    (1, 0.5, 0, 1),  # orange
    (1, 1, 0, 1),  # yellow
    (0, 1, 0, 1),  # green
    (0, 0, 1, 1),  # blue
    (0.29, 0, 0.51, 1),  # indigo
    (0.93, 0.51, 0.93, 1),  # violet
]

colors = np.zeros((n, 4), dtype=np.float32)
colors[:, 0] = 1  # red
colors[:, -1] = 1  # alpha
_colors = colors.copy()

symbols = list(Markers._symbol_shader_values.keys())
symbols_ring = dict(zip(symbols, symbols[1:]))
symbols_ring[symbols[-1]] = symbols[0]

EDGE_COLOR = "white"
MARKER_SIZE = 0.0125
EDGE_WDITH = MARKER_SIZE / 10

markers = Markers(
    pos=pos,
    edge_color=EDGE_COLOR,
    face_color=colors,
    size=MARKER_SIZE,
    edge_width=EDGE_WDITH,
    scaling="scene",
)
markers.update_gl_state(depth_test=True)
view.add(markers)

# Use filters to affect the rendering of the mesh.
picking_filter = MarkerPickingFilter()
markers.attach(picking_filter)


@view.events.connect
def on_viewbox_change(event):
    # workaround for vispy/#2501
    markers.update_gl_state(blend=not picking_filter.enabled)


throttle = time.monotonic()


@canvas.events.mouse_move.connect
def on_mouse_move(event):
    global throttle
    # throttle mouse events to 50ms
    if time.monotonic() - throttle < 0.05:
        return
    throttle = time.monotonic()

    # adjust the event position for hidpi screens
    render_size = tuple(d * canvas.pixel_scale for d in canvas.size)
    x_pos = event.pos[0] * canvas.pixel_scale
    y_pos = render_size[1] - (event.pos[1] * canvas.pixel_scale)

    # render a small patch around the mouse cursor
    restore_state = not picking_filter.enabled
    picking_filter.enabled = True
    markers.update_gl_state(blend=False)
    picking_render = canvas.render(
        crop=(x_pos - 2, y_pos - 2, 5, 5),
        bgcolor=(0, 0, 0, 0),
        alpha=True,
    )
    if restore_state:
        picking_filter.enabled = False
    markers.update_gl_state(blend=not picking_filter.enabled)

    # unpack the face index from the color in the center pixel
    marker_idx = (picking_render.view(np.uint32) - 1)[2, 2, 0]

    if marker_idx >= 0 and marker_idx < len(pos):
        new_symbols = list(markers.symbol)
        new_symbol = symbols_ring[new_symbols[marker_idx]]
        new_symbols[marker_idx] = new_symbol

        colors[marker_idx] = random.choice(COLORS)
        markers.set_data(
            pos=pos,
            edge_color=EDGE_COLOR,
            face_color=colors,
            size=MARKER_SIZE,
            edge_width=EDGE_WDITH,
            symbol=new_symbols,
        )


@canvas.events.key_press.connect
def on_key_press(event):
    global colors
    if event.key == 'p':
        # toggle face picking view
        picking_filter.enabled = not picking_filter.enabled
        markers.update_gl_state(blend=not picking_filter.enabled)
        markers.update()
    if event.key == 'r':
        # reset marker symbols
        colors = _colors.copy()
        markers.set_data(
            pos=pos,
            edge_color=EDGE_COLOR,
            face_color=colors,
            size=MARKER_SIZE,
            edge_width=EDGE_WDITH,
        )


canvas.show()


if __name__ == "__main__":
    print(__doc__)
    app.run()

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

Gallery generated by Sphinx-Gallery