-
Notifications
You must be signed in to change notification settings - Fork 618
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2500 from aganders3/face-picking
- Loading branch information
Showing
8 changed files
with
560 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
# -*- coding: utf-8 -*- | ||
# vispy: gallery 30 | ||
# ----------------------------------------------------------------------------- | ||
# Copyright (c) Vispy Development Team. All Rights Reserved. | ||
# Distributed under the (new) BSD License. See LICENSE.txt for more info. | ||
# ----------------------------------------------------------------------------- | ||
""" | ||
Picking Faces from a Mesh | ||
========================= | ||
Demonstrates how to identify (pick) individual faces on a mesh. | ||
Arguments: | ||
* --mesh - Path to a mesh file (OBJ/OBJ.GZ) [optional] | ||
Controls: | ||
* p - Toggle face picking view - shows the colors encoding face ID | ||
* r - Clear painted faces | ||
* s - Cycle shading modes (None, 'flat', 'smooth') | ||
* w - Toggle wireframe | ||
""" | ||
import argparse | ||
import itertools | ||
import time | ||
|
||
import numpy as np | ||
|
||
from vispy import app, scene | ||
from vispy.io import read_mesh, load_data_file | ||
from vispy.scene.visuals import Mesh | ||
from vispy.scene import transforms | ||
from vispy.visuals.filters import ShadingFilter, WireframeFilter, FacePickingFilter | ||
|
||
|
||
parser = argparse.ArgumentParser() | ||
default_mesh = load_data_file('orig/triceratops.obj.gz') | ||
parser.add_argument('--mesh', default=default_mesh) | ||
args, _ = parser.parse_known_args() | ||
|
||
vertices, faces, _normals, _texcoords = read_mesh(args.mesh) | ||
|
||
canvas = scene.SceneCanvas(keys='interactive', bgcolor='white') | ||
view = canvas.central_widget.add_view() | ||
|
||
view.camera = 'arcball' | ||
view.camera.depth_value = 1e3 | ||
|
||
# Create a colored `MeshVisual`. | ||
face_colors = np.tile((0.5, 0.0, 0.5, 1.0), (len(faces), 1)) | ||
mesh = Mesh( | ||
vertices, | ||
faces, | ||
face_colors=face_colors.copy() | ||
) | ||
mesh.transform = transforms.MatrixTransform() | ||
mesh.transform.rotate(90, (1, 0, 0)) | ||
mesh.transform.rotate(-45, (0, 0, 1)) | ||
view.add(mesh) | ||
|
||
# Use filters to affect the rendering of the mesh. | ||
wireframe_filter = WireframeFilter() | ||
shading_filter = ShadingFilter() | ||
face_picking_filter = FacePickingFilter() | ||
mesh.attach(wireframe_filter) | ||
mesh.attach(shading_filter) | ||
mesh.attach(face_picking_filter) | ||
|
||
|
||
def attach_headlight(view): | ||
light_dir = (0, 1, 0, 0) | ||
shading_filter.light_dir = light_dir[:3] | ||
initial_light_dir = view.camera.transform.imap(light_dir) | ||
|
||
@view.scene.transform.changed.connect | ||
def on_transform_change(event): | ||
transform = view.camera.transform | ||
shading_filter.light_dir = transform.map(initial_light_dir)[:3] | ||
|
||
|
||
attach_headlight(view) | ||
|
||
shading = itertools.cycle(("flat", "smooth", None)) | ||
shading_filter.shading = next(shading) | ||
|
||
|
||
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 face_picking_filter.enabled | ||
face_picking_filter.enabled = True | ||
mesh.update_gl_state(blend=False) | ||
picking_render = canvas.render( | ||
region=(x_pos - 1, y_pos - 1, 3, 3), | ||
size=(3, 3), | ||
bgcolor=(0, 0, 0, 0), | ||
alpha=True, | ||
) | ||
if restore_state: | ||
face_picking_filter.enabled = False | ||
mesh.update_gl_state(blend=not face_picking_filter.enabled) | ||
|
||
# unpack the face index from the color in the center pixel | ||
face_idx = (picking_render.view(np.uint32) - 1)[1, 1, 0] | ||
|
||
if face_idx > 0 and face_idx < len(face_colors): | ||
# this may be less safe, but it's faster than set_data | ||
mesh.mesh_data._face_colors_indexed_by_faces[face_idx] = (0, 1, 0, 1) | ||
mesh.mesh_data_changed() | ||
|
||
|
||
@canvas.events.key_press.connect | ||
def on_key_press(event): | ||
if event.key == 'p': | ||
face_picking_filter.enabled = not face_picking_filter.enabled | ||
mesh.update_gl_state(blend=not face_picking_filter.enabled) | ||
mesh.update() | ||
|
||
if event.key == 'r': | ||
mesh.set_data(vertices, faces, face_colors=face_colors) | ||
|
||
if event.key == 's': | ||
shading_filter.shading = next(shading) | ||
mesh.update() | ||
|
||
if event.key == 'w': | ||
wireframe_filter.enabled = not wireframe_filter.enabled | ||
mesh.update() | ||
|
||
|
||
canvas.show() | ||
|
||
|
||
if __name__ == "__main__": | ||
print(__doc__) | ||
app.run() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
# -*- coding: utf-8 -*- | ||
# vispy: gallery 30 | ||
# ----------------------------------------------------------------------------- | ||
# Copyright (c) Vispy Development Team. All Rights Reserved. | ||
# Distributed under the (new) BSD License. See LICENSE.txt for more info. | ||
# ----------------------------------------------------------------------------- | ||
""" | ||
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 | ||
""" | ||
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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.